Solving User Management Challenges in JobTrackr

Solving User Management Challenges in JobTrackr

I am currently developing JobTrackr, a platform designed to streamline job applications for job seekers and companies, which was a rewarding yet challenging experience. One of the most significant problems I encountered during the development process was managing different user types—job seekers and companies—and creating distinct user experiences for each. In this blog post, I will share the steps I took to solve this problem and how my implemented solutions improved the platform. I will also discuss my motivations for joining the HNG Internship and what I hope to achieve through this opportunity.

Understanding the Problem

JobTrackr is a web application that allows companies to post job listings and job seekers to apply for these jobs. This dual functionality meant that I needed to create two distinct user experiences while maintaining a unified authentication system. The main challenges were:

  1. Storing User-Specific Information: Companies need to provide additional information such as company name and website, which is not required for job seekers.

  2. Separate Registration and Login Flows: Ensuring that the registration and login processes catered to the specific needs of each user type.

  3. Access Control: Ensuring that job seekers and companies can only access information relevant to their roles.

Step-by-Step Solution

1. Extending the User Model

To address the issue of storing user-specific information, I extended the default Django User model. This approach allowed me to create additional fields for storing company-specific information without duplicating the authentication logic.

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    is_company = models.BooleanField(default=False)
    is_job_seeker = models.BooleanField(default=False)

class Company(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    company_name = models.CharField(max_length=255)
    website = models.URLField()

class JobSeeker(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    resume = models.FileField(upload_to='resumes/')

In this implementation, the User model includes fields to indicate whether the user is a company or a job seeker. The Company and JobSeeker models store additional information relevant to each user type.

2. Implementing Separate Registration and Login Flows

Next, I needed to create separate registration and login flows for job seekers and companies. By customizing Django’s built-in authentication views, I ensured that each user type had a tailored experience.

Registration Views:

from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from .forms import CompanyRegistrationForm, JobSeekerRegistrationForm

def register(request):
    if request.method == 'POST':
        user_form = UserCreationForm(request.POST)
        if user_form.is_valid():
            user = user_form.save()
            if 'is_company' in request.POST:
                company_form = CompanyRegistrationForm(request.POST, instance=user.company)
                if company_form.is_valid():
                    company_form.save()
            else:
                job_seeker_form = JobSeekerRegistrationForm(request.POST, instance=user.jobseeker)
                if job_seeker_form.is_valid():
                    job_seeker_form.save()
            return redirect('login')
    else:
        user_form = UserCreationForm()
        company_form = CompanyRegistrationForm()
        job_seeker_form = JobSeekerRegistrationForm()
    return render(request, 'registration/register.html', {
        'user_form': user_form,
        'company_form': company_form,
        'job_seeker_form': job_seeker_form,
    })

In the registration view, the form checks whether the user is a company or a job seeker and saves the relevant information accordingly.

3. Access Control and User Permissions

To ensure that users could only access information pertinent to their roles, I implemented access control mechanisms in the views. This approach helped maintain security and provided a better user experience.

Dashboard View:

from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect

@login_required
def dashboard(request):
    if request.user.is_company:
        jobs = Job.objects.filter(company=request.user.company)
        return render(request, 'company_dashboard.html', {'jobs': jobs})
    elif request.user.is_job_seeker:
        applications = Application.objects.filter(job_seeker=request.user.jobseeker)
        return render(request, 'job_seeker_dashboard.html', {'applications': applications})
    else:
        return redirect('home')

In the dashboard view, the user type is checked, and the relevant information is displayed accordingly. This ensures that companies can manage their job listings while job seekers can track their applications.

Styling with Tailwind CSS

Styling Django forms presented another challenge. Django does not provide an out-of-the-box solution for styling forms, so I used the django-widget-tweaks library to integrate Tailwind CSS into my forms.

Before using django-widget-tweaks, I wrapped each form field in a div and applied styles to the div, hoping the form field would inherit the styles. This method was inefficient and cumbersome. With django-widget-tweaks, I could apply CSS classes directly to the form fields, simplifying the styling process.

Form Styling Example:

{% load widget_tweaks %}
<form method="post">
    {% csrf_token %}
    {{ form.non_field_errors }}
    <div class="mb-4">
        {% render_field form.first_name placeholder="Doe" class+="p-2 mt-1 w-full rounded-md border-gray-200 bg-white text-sm text-gray-700 shadow-sm placeholder-gray-300" %}
        {{ form.first_name.errors }}
    </div>
    <button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Submit</button>
</form>

Using django-widget-tweaks, I could load the widget first and then apply Tailwind CSS styles directly to the form fields, significantly improving the aesthetics and usability of the forms.

Moving Forward with the HNG Internship

As I look forward to joining the HNG Internship, I am excited about the opportunity to further improve my backend development skills and enhance my resume. The HNG Internship is known for its challenging nature, and I am eager to tackle difficult problems and push my limits.

One of the reasons I want to join the HNG Internship is to build on the knowledge and experience I gained while developing JobTrackr. I believe that this internship will provide me with the necessary tools and mentorship to advance my career in backend development. Additionally, the internship will offer a platform to connect with other talented developers and learn from their experiences.

What excites me the most about the upcoming internship is the challenge it presents. I thrive on solving complex problems and enjoy pushing myself to learn and grow. The HNG Internship promises to be a demanding yet rewarding experience, and I am looking forward to embracing it fully.

If you're interested in learning more about the HNG Internship, I encourage you to check out their website and explore the various opportunities they offer. Additionally, if you're looking to hire talented developers or premium services, visit their hire page.

In conclusion, developing JobTrackr was a significant milestone in my web development journey. It provided valuable learning experiences and prepared me for the challenges ahead. I look forward to joining the HNG Internship and continuing to grow as a backend developer.