Skip to content

Latest commit

 

History

History
853 lines (657 loc) · 22 KB

File metadata and controls

853 lines (657 loc) · 22 KB

JobTrackerr-FullStack-React-Django - Project Report

Developer: Kaysarul Anas
Technology Stack: Django REST Framework + React (Vite)
Report Date: February 10, 2026
Project Status: Active Development


Executive Summary

The JobTrackerr-FullStack-React-Django is a comprehensive Full-Stack Cross-Platform Solution designed to help job seekers organize and manage their job applications efficiently. The system consists of a robust Django backend, a modern React web frontend, and a native mobile application built with React Native. It provides secure user authentication, real-time synchronization across devices, file management, and insightful analytics.

Key Achievements

  • Cross-Platform: Web (React) and Mobile (React Native) applications
  • Secure Auth: JWT-based authentication with Google OAuth integration
  • Cloud Native: Backend on AWS EC2, Frontend on Vercel
  • User Experience: Seamless auto-login and optional email verification flows
  • Data Integrity: PostgreSQL production database with robust API
  • Modern UI: Tailwind CSS v4 (Web) and Native Styling (Mobile)
  • File System: Secure multi-file upload management

Table of Contents

  1. Problem Statement
  2. My Solution
  3. System Architecture
  4. Backend Development
  5. Frontend Development
  6. UI/UX Design
  7. Authentication & Security
  8. Database Design
  9. API Documentation
  10. Deployment
  11. Testing

1. Problem Statement

The Challenge I Identified

As a job seeker myself, I noticed several pain points:

  • Disorganization: Difficulty tracking multiple applications across different companies
  • Lost Information: Forgetting which resume version was sent where
  • Missed Follow-ups: Losing track of application statuses and interview dates
  • Scattered Data: Job descriptions and notes stored in various locations
  • No Analytics: Unable to see patterns in my application success rates

Target Users

I designed this for:

  • Recent graduates entering the job market
  • Career changers managing multiple applications
  • Active job seekers applying to 10+ positions simultaneously
  • Anyone who wants organized job search records

2. My Solution

Core Features I Implemented

🔐 Secure Authentication

  • Email/password registration and login
  • Google OAuth integration for quick access
  • JWT token-based session management
  • Automatic token refresh mechanism

📊 Application Tracking

  • Track company name, position, and application status
  • Record application dates and deadlines
  • Store job post URLs for easy reference
  • Save job requirements for interview preparation
  • Add personal notes and observations

📁 File Management

  • Upload multiple files per application (resumes, cover letters, portfolios)
  • Track which version of documents was sent to each company
  • Secure file storage with user-specific access control

📈 Dashboard & Analytics

  • Visual statistics showing application status breakdown
  • Board view (Kanban-style) for organization
  • List view for detailed information display
  • Search and filter capabilities
  • Demo mode for exploring features without login

User Feedback System

  • Submit reviews and ratings
  • Public testimonials for homepage
  • Moderation system for review approval

3. System Architecture

High-Level Architecture

graph TB
    subgraph "Client Layer"
        A["Web Application<br/>React + Tailwind v4<br/>(Hosted on Vercel)"]
        B["Mobile Application<br/>React Native<br/>(iOS & Android)"]
    end

    subgraph "API Gateway & Security"
        C[Nginx<br/>Reverse Proxy]
        D[Gunicorn<br/>WSGI Server]
        E[SSL/TLS<br/>Encryption]
    end

    subgraph "Backend Layer (AWS EC2)"
        F[Django REST Framework<br/>API Logic]
        G[Authentication System<br/>SimpleJWT + Allauth]
        H[Business Logic<br/>Apps & Views]
    end

    subgraph "Data & Storage"
        I[PostgreSQL<br/>Production DB]
        J[File System<br/>Resume/Cover Letter Storage]
    end

    A <-->|HTTPS/JSON| C
    B <-->|HTTPS/JSON| C
    C <--> D
    D <--> F
    F --> G
    F --> H
    H --> I
    H --> J

    style A fill:#61dafb,stroke:#333,stroke-width:2px
    style B fill:#61dafb,stroke:#333,stroke-width:2px
    style F fill:#092e20,stroke:#333,stroke-width:2px,color:#fff
    style I fill:#336791,stroke:#333,stroke-width:2px,color:#fff
Loading

Technology Stack I Chose

Layer Technology Why I Chose It
Frontend React 19.2 Modern, component-based UI
Vite 7.2 Fast dev server and builds
Tailwind CSS v4 Rapid styling with utilities
React Router v7 Robust client-side routing
Axios Clean API integration
Backend Django 5.2 Batteries-included framework
Django REST Framework 3.16 Powerful API toolkit
SimpleJWT 5.5 Industry-standard auth
Django Allauth 65.14 Easy social auth
Database SQLite Simple for development
PostgreSQL Scalable for production
DevOps Gunicorn Production WSGI server
WhiteNoise Efficient static files
Docker Containerization

4. Backend Development

4.1 My Backend Setup

I built the backend with Django 5.2 and Django REST Framework 3.16 for a robust, scalable API.

Dependencies I Used

# requirements.txt
Django==5.2.11
djangorestframework==3.16.1
djangorestframework_simplejwt==5.5.1
django-allauth==65.14.0
django-cors-headers==4.9.0
dj-rest-auth==7.0.2
gunicorn==23.0.0
psycopg2-binary==2.9.10
dj-database-url==2.3.0
whitenoise==6.9.0
python-dotenv==1.2.1

4.2 Database Models I Created

Application Model

The core model for tracking job applications:

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

User = get_user_model()

class Application(models.Model):
    # Status choices
    APPLIED = 'Applied'
    INTERVIEW = 'Interview'
    OFFER = 'Offer'
    REJECTED = 'Rejected'

    STATUS_CHOICES = [
        (APPLIED, 'Applied'),
        (INTERVIEW, 'Interview'),
        (OFFER, 'Offer'),
        (REJECTED, 'Rejected'),
    ]

    # Relationships
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="applications"
    )

    # Core fields
    company_name = models.CharField(max_length=255)
    position_title = models.CharField(max_length=255)
    notes = models.TextField(blank=True, null=True)
    
    # Job details
    job_post_url = models.URLField(blank=True, null=True)
    job_requirements = models.TextField(blank=True, null=True)
    
    # Status tracking
    status = models.CharField(
        max_length=50,
        choices=STATUS_CHOICES,
        default=APPLIED
    )
    applied_at = models.DateField(blank=True, null=True)
    
    # Timestamps
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"{self.company_name} - {self.position_title}"

My Design Decisions:

  • ✅ User foreign key ensures data isolation
  • ✅ Status choices provide consistent data
  • ✅ Timestamps track creation and updates
  • ✅ Optional fields allow flexibility

ApplicationFile Model

I created this to handle multiple file uploads per application:

class ApplicationFile(models.Model):
    application = models.ForeignKey(
        Application,
        on_delete=models.CASCADE,
        related_name="files"
    )
    
    file = models.FileField(upload_to="application_files/")
    file_type = models.CharField(max_length=50)
    original_filename = models.CharField(max_length=255)
    
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.original_filename

Review Model

For user feedback and testimonials:

class Review(models.Model):
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="reviews"
    )
    rating = models.IntegerField(default=5)  # 1-5 stars
    comment = models.TextField()
    is_public = models.BooleanField(default=False)  # For moderation
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"Review by {self.user.username} - {self.rating} stars"

4.3 Database Relationships

erDiagram
    USER ||--o{ APPLICATION : creates
    USER ||--o{ REVIEW : writes
    APPLICATION ||--o{ APPLICATION_FILE : contains
    
    USER {
        int id PK
        string username
        string email
        string password
        datetime date_joined
    }
    
    APPLICATION {
        int id PK
        int user_id FK
        string company_name
        string position_title
        text notes
        string job_post_url
        text job_requirements
        string status
        date applied_at
        datetime created_at
        datetime updated_at
    }
    
    APPLICATION_FILE {
        int id PK
        int application_id FK
        file file
        string file_type
        string original_filename
        datetime created_at
    }
    
    REVIEW {
        int id PK
        int user_id FK
        int rating
        text comment
        boolean is_public
        datetime created_at
        datetime updated_at
    }
Loading

4.4 API Views I Implemented

I used ViewSets for clean CRUD operations:

ApplicationViewSet

from rest_framework import viewsets, permissions

class ApplicationViewSet(viewsets.ModelViewSet):
    serializer_class = ApplicationSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        # Only return applications belonging to current user
        return self.request.user.applications.all()

    def perform_create(self, serializer):
        # Automatically set user to current logged-in user
        serializer.save(user=self.request.user)

Security Features I Added:

  • ✅ Authentication required
  • ✅ User-specific data filtering
  • ✅ Automatic user assignment
  • ✅ No cross-user data access

5. Frontend Development

5.1 My Frontend Stack

I chose modern React ecosystem tools for optimal developer experience:

{
  "dependencies": {
    "react": "^19.2.0",
    "react-dom": "^19.2.0",
    "react-router-dom": "^7.13.0",
    "axios": "^1.13.4",
    "@react-oauth/google": "^0.13.4",
    "lucide-react": "^0.563.0"
  },
  "devDependencies": {
    "vite": "^7.2.4",
    "tailwindcss": "^4.1.18",
    "@tailwindcss/vite": "^4.1.18",
    "@vitejs/plugin-react": "^5.1.1",
    "eslint": "^9.39.1"
  }
}

5.2 Component Architecture

I organized my components into:

src/
├── components/
│   ├── AuthLayout.jsx        # Auth page wrapper
│   ├── Navbar.jsx            # Navigation
│   ├── Footer.jsx            # Footer
│   ├── LandingHero.jsx       # Homepage hero
│   ├── Features.jsx          # Feature showcase
│   ├── Testimonials.jsx      # User reviews
│   └── dashcomp/             # Dashboard components
│       ├── DashboardHeader.jsx
│       ├── StatsSection.jsx
│       ├── BoardView.jsx
│       ├── ListView.jsx
│       ├── ApplicationCard.jsx
│       ├── SearchControls.jsx
│       └── ReviewForm.jsx
├── context/
│   └── AuthContext.jsx       # Authentication state
├── pages/
│   ├── HomePage.jsx
│   ├── LoginPage.jsx
│   ├── SignupPage.jsx
│   └── Dashboard/
│       └── DashBoard.jsx
└── api/
    └── axios.js              # API client

5.3 API Integration

I configured Axios with interceptors for automatic token management:

// src/api/axios.js
import axios from 'axios';

const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || 'http://127.0.0.1:8000/',
  headers: {
    'Content-Type': 'application/json',
  },
});

// Request interceptor: Attach JWT token
api.interceptors.request.use(
  (config) => {
    const authEndpoints = ['/api/auth/token/', '/api/auth/registration/'];
    const isAuthEndpoint = authEndpoints.some(endpoint => 
      config.url.includes(endpoint)
    );
    
    if (!isAuthEndpoint) {
      const token = localStorage.getItem('access_token');
      if (token) {
        config.headers.Authorization = `Bearer ${token}`;
      }
    }
    
    return config;
  },
  (error) => Promise.reject(error)
);

export default api;

6. Mobile Application Development

6.1 Technology Stack

I extended the platform to mobile devices using React Native and Expo, sharing the same backend API as the web version.

Core Mobile Tech

{
  "dependencies": {
    "react-native": "0.76.6",
    "expo": "~52.0.25",
    "expo-router": "~4.0.16",
    "axios": "^1.7.9",
    "lucide-react-native": "^0.474.0",
    "react-native-safe-area-context": "4.12.0",
    "react-native-gesture-handler": "~2.20.2",
    "expo-linear-gradient": "~14.0.2"
  }
}

6.2 Key Features

📱 Native Experience (Coming Soon)

  • Status: Currently in final development phase.
  • Teaser Banner: Interactive landing page banner with Android slideshow.
  • Navigation: Stack and Tab navigation using expo-router
  • Gestures: Smooth transitions and touch interactions
  • Safe Area: Optimized for iPhone Dynamic Island and notches
  • Status Bar: Adaptive styling for dark/light modes

🔐 Mobile Authentication

  • Secure Storage: JWT tokens stored in AsyncStorage
  • Auto-Login: Token validation on app launch
  • Profile Modal: Integrated user management within the app

📊 Dashboard on the Go

  • Real-time Stats: View application counts and statuses
  • Infinite Scroll: Efficiently browse long lists of applications
  • Pull-to-Refresh: Update data with a simple gesture

6.3 Mobile Folder Structure

mobile/
├── src/
│   ├── api/             # API configuration (Axios)
│   ├── assets/          # Images and fonts
│   ├── components/      # Reusable UI components
│   │   ├── ProfileContent.js
│   │   └── ...
│   ├── context/         # Auth & Theme Contexts
│   ├── navigation/      # Stack & Tab Navigators
│   └── screens/         # Application screens
│       ├── DashboardScreen.js
│       ├── LoginScreen.js
│       └── SignupScreen.js
├── App.js              # Entry point
└── app.json            # Expo configuration

7. UI/UX Design

6.1 My Design Philosophy

I focused on:

  1. Minimalism: Clean, uncluttered interfaces
  2. Responsiveness: Mobile-first design approach
  3. Accessibility: Semantic HTML and ARIA labels
  4. Performance: Optimized assets and lazy loading
  5. Consistency: Unified color scheme and typography

6.2 Design System

I use Tailwind CSS v4 with custom configuration:

/* src/index.css */
@import "tailwindcss";

@theme {
  --font-sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

body {
  @apply font-sans;
}

Color Usage:

  • Primary: Blue tones for CTAs and interactive elements
  • Success: Green for positive actions (Offer status)
  • Warning: Yellow/Orange for pending states (Interview)
  • Danger: Red for rejections and destructive actions
  • Neutral: Gray scale for text and backgrounds

7. Authentication & Security

7.1 My Authentication Implementation

I implemented a dual-authentication system:

  1. Email/Password: Traditional credentials-based auth
  2. Google OAuth: Social authentication for convenience

7.2 Security Measures I Added

Token Management

Access Token:

  • Lifetime: 60 minutes
  • Storage: localStorage
  • Usage: Attached to all API requests

Refresh Token:

  • Lifetime: 1 day
  • Storage: localStorage
  • Usage: Obtain new access token when expired

Data Isolation

Every API endpoint I created implements user-based filtering:

def get_queryset(self):
    return self.request.user.applications.all()

This ensures users can only access their own data.

7.3 Challenges I Overcame

During development, I encountered and resolved several issues:

Challenge 1: Registration Failures

Problem: Backend rejected valid registration data
Solution: Added password2 field for confirmation

Challenge 2: Google OAuth 500 Errors

Problem: Django Sites framework misconfiguration
Solution: Corrected SITE_ID and linked Social Application

Challenge 3: 401 Unauthorized Loop

Problem: Axios interceptor attached tokens to login requests
Solution: Excluded auth endpoints from token attachment

Challenge 4: COOP Headers (Google Login)

Problem: Google Login blocked in production by browser security
Solution: Removed conflicting same-origin-allow-popups headers from vercel.json and set SECURE_CROSS_ORIGIN_OPENER_POLICY = None in Django.

Challenge 5: Admin CSRF Failures

Problem: Cloud Admin panel rejected all login attempts
Solution: Added the API domain to CSRF_TRUSTED_ORIGINS.


8. Database Design

8.1 Schema Overview

My database consists of 4 main tables:

  1. User (Django built-in)
  2. Application (Custom)
  3. ApplicationFile (Custom)
  4. Review (Custom)

8.2 Migrations

I use Django's migration system:

# Create migrations
python manage.py makemigrations

# Apply migrations
python manage.py migrate

9. API Documentation

9.1 Base URL

  • Development: http://127.0.0.1:8000/
  • Production: https://api.jobtracker.kaysarulanas.me/

9.2 Authentication Endpoints

POST /api/auth/token/

Obtain JWT access and refresh tokens.

Request:

{
  "email": "user@example.com",
  "password": "securepassword"
}

Response:

{
  "access": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "refresh": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}

9.3 Application Endpoints

GET /api/applications/

List all applications for authenticated user.

POST /api/applications/

Create new application.

Request:

{
  "company_name": "Microsoft",
  "position_title": "Frontend Developer",
  "status": "Applied",
  "applied_at": "2026-02-08",
  "notes": "Applied through LinkedIn"
}

PUT /api/applications/{id}/

Update application.

DELETE /api/applications/{id}/

Delete application.


10. Deployment

10.1 Production Architecture

  • Backend: Hosted on AWS EC2 (Ubuntu) using Gunicorn as the application server and Nginx as a reverse proxy.
  • Frontend: Deployed on Vercel with global CDN distribution.
  • Database: PostgreSQL (Production) hosted on AWS/RDS or similar provider.
  • Domain: Custom domain jobtracker.kaysarulanas.me with SSL via Let's Encrypt.

10.2 Backend Deployment (AWS)

Infrastructure Setup

  1. EC2 Instance: t2.micro (Free Tier), Ubuntu 22.04 LTS
  2. Security Groups: Allow HTTP (80), HTTPS (443), SSH (22)
  3. Database: PostgreSQL installed and secured

Service Configuration

Gunicorn Systemd Service:

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/JobTrackerr-FullStack-React-Django/backend
ExecStart=/home/ubuntu/JobTrackerr-FullStack-React-Django/backend/venv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          backend.wsgi:application

Nginx Configuration:

  • Reverse proxy to Gunicorn socket
  • Static file serving (/static/)
  • SSL termination (Certbot/Let's Encrypt)
  • CORS headers for Vercel frontend

10.3 Frontend Deployment (Vercel)

10.4 Mobile Distribution

  • Expo Application Services (EAS):
    • eas build -p android: Generates APK/AAB for Android
    • eas build -p ios: Generates IPA for iOS (requires Apple Developer Account)
  • Over-the-Air Updates: Push JavaScript updates directly to users via eas update

11. Testing & Future Roadmap

11.1 Testing Approach

I plan to implement:

  • Unit tests for all models
  • API endpoint tests
  • Authentication flow tests
  • Component unit tests
  • Integration tests
  • E2E tests with Playwright

11.2 Example Test

# applications/tests.py
from django.test import TestCase
from .models import Application

class ApplicationModelTest(TestCase):
    def test_create_application(self):
        app = Application.objects.create(
            user=self.user,
            company_name='Test Corp',
            position_title='Developer',
            status='Applied'
        )
        self.assertEqual(str(app), 'Test Corp - Developer')

Conclusion

What I've Accomplished

I successfully built a full-stack job application tracker that demonstrates:

Full-stack Development

  • Modern React frontend with Tailwind CSS v4
  • Robust Django REST Framework backend
  • Secure JWT authentication with OAuth integration

Software Engineering Best Practices

  • RESTful API design
  • Component-based architecture
  • Database normalization
  • Security-first approach

User-Centric Design

  • Intuitive dashboard interface
  • Responsive mobile design
  • Demo mode for exploration

Technical Skills I Demonstrated

Backend:

  • Django ORM and migrations
  • DRF serializers and viewsets
  • JWT authentication
  • Social OAuth integration
  • File upload handling

Frontend:

  • React hooks and context
  • React Router navigation
  • Axios interceptors
  • Tailwind CSS styling
  • Component composition

DevOps:

  • Docker containerization
  • Environment configuration
  • Static file serving
  • CORS configuration

This project is part of my portfolio demonstrating full-stack development capabilities.