Project Duration: 14-16 days
Tech Stack: Next.js 14, TypeScript, Supabase, Tailwind CSS, shadcn/ui
- Primary: #2563eb (Professional Blue)
- Secondary: #10b981 (Success Green)
- Accent: #f59e0b (Warm Orange)
- Error: #ef4444 (Professional Red)
- Background: #ffffff (Pure White)
- Surface: #f9fafb (Light Gray)
- Text Primary: #111827
- Text Secondary: #6b7280
- Primary: #3b82f6 (Brighter Blue)
- Secondary: #34d399 (Brighter Green)
- Accent: #fbbf24 (Brighter Orange)
- Error: #f87171 (Softer Red)
- Background: #0f172a (Deep Navy)
- Surface: #1e293b (Slate)
- Text Primary: #f1f5f9
- Text Secondary: #cbd5e1
- Primary Font: Inter (UI, body text, data)
- Headings Font: Poppins (page titles, headings)
- Display: 36px / 2.25rem
- H1: 30px / 1.875rem
- H2: 24px / 1.5rem
- Body: 14px / 0.875rem
- Clean & Minimal - Focus on key metrics
- Professional but Approachable
- Data-Driven visualizations
- Mobile-First responsive design
- Consistent 4px/8px spacing grid
Duration: 2-3 hours | Day 1 Morning
- Install Node.js (v18+) and verify with
node --version - Install VS Code with extensions: ES7 React snippets, Tailwind IntelliSense, Prettier
- Install Git and configure:
git config --global user.name "Your Name" - Learning: Development environment setup, tools understanding
- Run
npx create-next-app@latestwith TypeScript, Tailwind, App Router - Understand app directory structure vs pages directory
- Navigate folders: app/, public/, components/
- Run
npm run devand view at localhost:3000 - Learning: Next.js 14 initialization, App Router
- Open app/page.tsx and remove default content
- Create simple "Hello World" component
- Open app/layout.tsx and understand root layout
- Remove default styles, keep Tailwind imports
- Learning: Root layout concept, component structure
- Run
git initin project folder - Verify .gitignore includes node_modules, .env.local
- Run
git add .andgit commit -m "Initial commit" - Create GitHub repo and push:
git remote add origin <url> - Learning: Git workflow, version control
- Run:
npm install @supabase/supabase-js - Run:
npm install date-fns(date manipulation) - Run:
npm install recharts(charts) - Run:
npm install lucide-react(icons) - Verify in package.json
- Learning: npm package management
- Go to supabase.com and create free account
- Click "New Project", choose name and password
- Wait for project creation (2-3 minutes)
- Navigate to Project Settings → API
- Copy Project URL and anon public key
- Explore: Tables, SQL Editor, Storage, Authentication tabs
- Learning: Supabase dashboard, BaaS concepts
- Create
.env.localfile in project root - Add:
NEXT_PUBLIC_SUPABASE_URL=your_url_here - Add:
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_key_here - Verify .gitignore includes .env.local
- Learning: Environment variables, security
- Create folder:
lib/supabase/ - Create file:
lib/supabase/client.tsfor browser client - Create file:
lib/supabase/server.tsfor server client - Import createClient from @supabase/supabase-js
- Export configured client instances
- Add 'use client' directive to client.ts
- Learning: Client vs Server Components, utility organization
- Create file:
app/api/test-db/route.ts - Write GET handler that calls supabase.from('test').select()
- Test from browser: localhost:3000/api/test-db
- Handle and log errors
- Learning: API routes, async/await, testing connections
Duration: 1-1.5 hours | Day 1 Afternoon
- Run:
npx shadcn-ui@latest init - Choose: Default style, Slate color, CSS variables
- Verify components.json created
- Learning: CLI tools, shadcn/ui setup
- Run:
npx shadcn-ui@latest add button - Run:
npx shadcn-ui@latest add card - Run:
npx shadcn-ui@latest add input - Run:
npx shadcn-ui@latest add dialog - Run:
npx shadcn-ui@latest add table - Run:
npx shadcn-ui@latest add label - Explore installed files in components/ui/
- Learning: shadcn/ui components, Radix UI
- Run:
npm install next-themes - Understand how next-themes manages theme state
- Learning: Theme management libraries
- Open
tailwind.config.ts - Add
darkMode: 'class'in config - Update theme colors to use CSS variables
- Learning: Tailwind configuration, dark mode strategies
- Create
components/providers/theme-provider.tsx - Import ThemeProvider from next-themes
- Add 'use client' directive
- Set attribute="class", defaultTheme="system", enableSystem
- Learning: Provider pattern, Client Components
- Open
app/layout.tsx - Import your ThemeProvider component
- Wrap {children} with
- Learning: Layout nesting, provider composition
- Create
components/theme-toggle.tsx - Import useTheme from next-themes
- Add 'use client' directive
- Create button that calls setTheme("light"/"dark")
- Use Lucide icons: Sun and Moon
- Learning: useTheme hook, conditional rendering
- Add dark: prefix to Tailwind classes
- Example:
bg-white dark:bg-slate-900 - Test switching themes
- Learning: Tailwind dark mode utilities
- Switch to dark mode
- Refresh page - should stay dark
- Close browser and reopen - should stay dark
- Learning: localStorage, state persistence
Duration: 4-5 hours | Day 1 Evening - Day 2 Morning
- Read Supabase Auth docs overview
- Understand: signup → email verification → login → session
- Learn about JWT tokens stored in localStorage
- Learning: Authentication concepts, JWT, session management
- Go to Supabase Dashboard → Authentication → Providers
- Verify Email provider is enabled
- Go to Authentication → Email Templates
- Review Welcome email, Confirmation email templates
- Set Site URL:
http://localhost:3000 - Add redirect URL:
http://localhost:3000/auth/callback - Learning: Auth provider configuration
- Authentication → Settings
- Decide: Enable/disable email confirmation
- Set minimum password length (6 characters)
- Learning: Auth security settings
- Create folder:
lib/auth/ - Create file:
lib/auth/AuthContext.tsx - Add 'use client' directive
- Create AuthContext with createContext
- Create AuthProvider with user state
- Export useAuth custom hook
- Learning: React Context API, custom hooks
- Create file:
lib/auth/auth-helpers.ts - Write signUp function: accepts email, password
- Write signIn function: accepts email, password
- Write signOut function
- Write getCurrentUser function
- Add try-catch error handling
- Learning: Auth API methods, error handling
- In AuthContext, create user and loading states
- In useEffect, call getCurrentUser on mount
- Subscribe to onAuthStateChange
- Update user state when auth state changes
- Learning: useEffect, real-time subscriptions
- Open
app/layout.tsx - Import AuthProvider
- Wrap children with AuthProvider (inside ThemeProvider)
- Learning: Multiple providers, composition
- Create folder:
app/(public)/ - Create file:
app/(public)/layout.tsx - Create simple navbar with logo and "Sign In" link
- Style with Tailwind, make responsive
- Learning: Route groups, public layouts
- Create file:
app/(public)/page.tsx - Build hero section with app name and tagline
- Add features section: list 5 key features with icons
- Add CTA button: "Get Started Free" → links to /signup
- Make mobile responsive
- Learning: Landing page design, CTAs
- Create file:
app/(public)/signup/page.tsx - Add 'use client' directive
- Create form with email and password inputs
- Add confirm password field
- Add terms checkbox
- Use shadcn Form components
- Learning: Form creation, controlled components
- Create handleSubmit async function
- Validate: email format, password match, length
- Call signUp helper function
- Show success: "Check your email"
- Show errors
- Add loading state
- Learning: Form validation, async submission
- Create file:
app/(public)/signin/page.tsx - Add 'use client' directive
- Form with email and password fields
- Add "Forgot Password?" link
- Add "Don't have account? Sign up" link
- Learning: Consistent form patterns
- Create handleSubmit async function
- Call signIn helper function
- On success: redirect to /dashboard using useRouter
- Show errors ("Invalid credentials")
- Add loading state
- Learning: useRouter, programmatic navigation
- Create folder:
app/(protected)/ - Create file:
app/(protected)/layout.tsx - Import useAuth hook
- Check if user is authenticated
- If not: redirect to /signin
- If yes: show layout with sidebar and navbar
- Learning: Protected routes, auth guards
- Create sidebar with logo
- Add navigation links: Dashboard, Applications, Referrals, etc.
- Add user email at bottom
- Add "Sign Out" button
- Make sidebar collapsible on mobile (hamburger)
- Use shadcn Sheet for mobile sidebar
- Learning: Sidebar navigation, mobile menu
- Create file:
app/(protected)/dashboard/page.tsx - Get user from useAuth hook
- Display: "Welcome back, {user.email}!"
- Add placeholder stat cards
- Learning: Protected pages, user data access
- Find "Sign Out" button in protected layout
- Add onClick handler
- Call signOut helper function
- Use router.push('/') to redirect
- Show success toast
- Learning: Logout flow, redirects
- Create file:
middleware.tsin root - Check if user accessing protected routes
- If no session: redirect to /signin
- If accessing /signin with session: redirect to /dashboard
- Use supabase.auth.getSession()
- Learning: Next.js middleware, route protection
- Create file:
app/(public)/forgot-password/page.tsx - Form with email input
- Call supabase.auth.resetPasswordForEmail(email)
- Show success: "Check your email"
- Learning: Password reset flow
- Create file:
app/(public)/reset-password/page.tsx - Form with new password and confirm
- Get token from URL params
- Call supabase.auth.updateUser({ password })
- Redirect to /signin on success
- Learning: Token handling, password update
- Create file:
app/auth/callback/route.ts - Handle email confirmation redirects
- Exchange code for session
- Redirect to /dashboard
- Learning: OAuth callbacks
- Create file:
app/(protected)/loading.tsx - Show spinner while checking auth
- Prevent flash of unauthenticated content
- Learning: Loading states, Suspense
- Test: signup → email → confirm → dashboard
- Test: signin with correct credentials
- Test: signin with wrong credentials → error
- Test: access /dashboard without login → redirects
- Test: signout → redirects to homepage
- Test: forgot password → reset password
- Learning: End-to-end testing, QA
- Create error message mapping
- "Invalid login credentials" → friendly message
- Display using toast notifications
- Learning: Error handling, UX
- Login to application
- Close browser completely
- Reopen and go to /dashboard
- Should still be logged in
- Learning: Session persistence, token storage
Duration: 4-5 hours | Day 2 Afternoon
- Sketch four tables: applications, referrals, resumes, interviews
- Add user_id column to EACH table (UUID, FK to auth.users)
- Define all columns with data types
- Draw relationships
- Note foreign keys and constraints
- Learning: Multi-user schema design, normalization
- Read Supabase RLS documentation
- Understand: RLS ensures users only see their own data
- Learn about auth.uid() function
- Understand policies: SELECT, INSERT, UPDATE, DELETE
- Learning: Database security, RLS concepts
- Open Supabase Dashboard → SQL Editor
- Write CREATE TABLE statement with user_id
- Include: id, user_id, company_name, role, job_link, status, applied_date, resume_id, referral_id, notes, priority, salary_range, tech_stack, created_at, updated_at
- Add foreign key: user_id REFERENCES auth.users(id) ON DELETE CASCADE
- Execute query
- Learning: SQL DDL, foreign keys, CASCADE
- Run:
ALTER TABLE applications ENABLE ROW LEVEL SECURITY; - Understand: now NO ONE can access data (default deny)
- Learning: Enabling RLS, default security
- Create SELECT policy:
USING (auth.uid() = user_id) - Create INSERT policy:
WITH CHECK (auth.uid() = user_id) - Create UPDATE policy:
USING (auth.uid() = user_id) - Create DELETE policy:
USING (auth.uid() = user_id) - Execute all policies
- Learning: RLS policy syntax, auth.uid()
- Test query:
SELECT * FROM applications; - Should return only YOUR applications
- Try inserting with your user_id → success
- Try inserting with different user_id → fail
- Learning: Testing security policies
- Write CREATE TABLE with user_id column
- Include: id, user_id, person_name, company, linkedin_url, relationship, date_asked, status, follow_up_date, notes, created_at
- Enable RLS
- Create all 4 policies (SELECT, INSERT, UPDATE, DELETE)
- Test policies
- Learning: Repeating RLS pattern
- Write CREATE TABLE with user_id
- Include: id, user_id, version_name, file_url, upload_date, times_used, success_rate, created_at
- Enable RLS and create policies
- Test policies
- Learning: File metadata storage with RLS
- Write CREATE TABLE with user_id AND application_id
- Include: id, user_id, application_id, round_name, scheduled_date, status, prep_notes, feedback, created_at
- Add FK to applications(id) ON DELETE CASCADE
- Enable RLS and create policies
- Learning: Multiple foreign keys, related tables
- Create indexes on user_id for all tables
- Create index on applications.status
- Create index on applications.company_name
- Create index on applications.applied_date
- Create index on foreign keys
- Learning: Database performance, indexing
- Write CREATE TABLE with user_id as PRIMARY KEY
- Include: user_id, email_notifications, telegram_notifications, telegram_chat_id, user_email, created_at, updated_at
- Enable RLS and create policies
- Learning: Settings table, one-to-one relationship
- Create function: handle_new_user()
- Function inserts row in user_preferences on signup
- Create trigger: on_auth_user_created
- Test by creating new user
- Learning: Database triggers, automated setup
- Create second test account
- Login as User A, add applications
- Login as User B, verify can't see User A's data
- Learning: Multi-tenancy, data isolation
- Create file:
lib/supabase/queries.ts - Write helper to get current user ID
- Write wrapper functions for common queries
- Add TypeScript return types
- Learning: Query abstraction, code organization
- Create file:
types/database.types.ts - Define interfaces for each table
- Export all types
- Use in query functions
- Learning: TypeScript interfaces, type safety
Duration: 8-10 hours | Day 3 - Day 4
- Create file:
app/(protected)/applications/page.tsx - Add page heading: "My Applications"
- Add "New Application" button
- Learning: Protected routes, page structure
- In
lib/supabase/queries.ts - Write
async function getApplications(userId: string) - Use:
.select('*').eq('user_id', userId) - Add error handling
- Return typed data
- Learning: Supabase queries, user-scoped data
- Get user from server-side auth
- Call getApplications(user.id)
- Pass data to client component
- Handle loading and error states
- Learning: Server Components, async data fetching
- Create
components/applications/ApplicationCard.tsx - Accept props:
application: Application - Display: company, role, status badge, date
- Add Edit and Delete buttons
- Style with shadcn Card
- Learning: Component props, TypeScript typing
- Map over applications array
- Render ApplicationCard for each
- Use Tailwind grid:
grid-cols-1 md:grid-cols-2 lg:grid-cols-3 - Learning: Responsive grid, Tailwind breakpoints
- Create
components/applications/EmptyState.tsx - Show when applications array is empty
- Display icon, message, "Add Application" button
- Learning: Conditional rendering, empty states
- Create
components/applications/AddApplicationDialog.tsx - Add 'use client' directive
- Use shadcn Dialog component
- Trigger: "New Application" button
- Learning: Dialog patterns, modal forms
- Fields: company_name, role, job_link, status, applied_date, notes
- Use shadcn Input, Select, Textarea, Label
- Add required validation
- Status options: Applied, OA, Interview, Offer, Rejected
- Learning: Form fields, validation
- Create
handleSubmitfunction - Prevent default
- Get form data
- Get user ID from useAuth
- Add user_id to data
- Learning: Form submission, user context
- Write
createApplication(data)in queries.ts - Use:
supabase.from('applications').insert(data) - Return { data, error }
- Learning: Database inserts, RLS in action
- After success, close dialog
- Show success toast
- Call router.refresh() to update list
- Learning: Cache revalidation, user feedback
- Add Edit button to ApplicationCard
- Open dialog in "edit mode"
- Pre-fill form with existing data
- Change button text to "Update"
- Learning: Form modes, pre-filling
- Write
updateApplication(id, data) - Use:
.update(data).eq('id', id) - RLS ensures user can only update their own
- Learning: Database updates, RLS on updates
- Add Delete button with trash icon
- Show confirmation with AlertDialog
- On confirm, call delete mutation
- Learning: Destructive actions, confirmation
- Write
deleteApplication(id) - Use:
.delete().eq('id', id) - Show success toast
- Refresh data
- Learning: Database deletes
- Create search input at top
- Filter by company_name or role
- Use client-side filtering
- Learning: Search implementation
- Add Select dropdown for status
- Options: All, Applied, OA, Interview, Offer, Rejected
- Filter applications by status
- Learning: Filtering, Select component
- Add date range picker
- Filter where applied_date between dates
- Use date-fns for comparisons
- Learning: Date filtering
- Create
components/applications/KanbanBoard.tsx - Group applications by status
- Create columns for each status
- Horizontal scroll on mobile
- Learning: Alternative views, Kanban UI
- Add sort dropdown
- Options: Date (newest), Date (oldest), Company (A-Z)
- Apply sorting to array
- Learning: Sorting arrays
- Create
components/applications/ApplicationSkeleton.tsx - Use shadcn Skeleton component
- Show while loading
- Learning: Loading states, Skeleton UI
- Test on mobile viewport
- Ensure buttons are touch-friendly (min 44px)
- Use Sheet instead of Dialog on mobile
- Test all CRUD operations
- Learning: Mobile responsiveness, touch targets
- Add pagination for many applications
- Show 12 per page
- Use .range(start, end)
- Learning: Pagination, performance
Duration: 3-4 hours | Day 5
- Enhance existing dashboard page
- Grid of stat cards at top
- Charts below
- Action items section
- Learning: Dashboard layout
- Query: COUNT(*) WHERE user_id = ?
- Display in StatCard
- Learning: Count queries
- Group by status
- Calculate count for each
- Display in stat cards
- Learning: Grouping data, SQL GROUP BY
- Create
components/dashboard/StatCard.tsx - Props: title, value, icon, trend
- Style with gradient backgrounds
- Learning: Reusable components
- Formula: (interviews + offers) / total * 100
- Display as percentage
- Learning: Calculated metrics
- Query: WHERE scheduled_date > NOW()
- Count results
- Display in stat card
- Learning: Date comparisons
- Import: LineChart, BarChart, PieChart
- Create responsive container
- Learning: Recharts basics
- Group by week/month
- Create LineChart
- X-axis: dates, Y-axis: count
- Learning: Time series data
- Calculate count per status
- Create PieChart or BarChart
- Add colors matching status badges
- Learning: Categorical data, PieChart
- Query: overdue follow-ups
- Query: upcoming interviews (next 7 days)
- Display as list
- Learning: Business rules, actionable insights
- Query: applications this week vs last week
- Calculate: change (+/- percentage)
- Display with arrow icon
- Learning: Trend comparisons
- "Add Application" button
- "Add Referral" button
- Style as prominent CTAs
- Learning: User flow optimization
- Stack cards vertically on mobile
- Make charts full-width
- Horizontal scroll for charts if needed
- Learning: Responsive dashboard
Duration: 3-4 hours | Day 6
- Create file:
app/(protected)/referrals/page.tsx - Heading: "My Referrals"
- "New Referral" button
- Learning: Applying patterns
- Write
getReferrals(userId)in queries.ts - SELECT * FROM referrals WHERE user_id = userId
- Learning: Consistent query patterns
- Create
components/referrals/ReferralCard.tsx - Show: person_name, company, relationship, status, follow_up_date
- Add Edit and Delete buttons
- Learning: Component patterns
- Fields: person_name, company, linkedin_url, relationship, date_asked, status, follow_up_date, notes
- Use Select for relationship: Friend, Colleague, Alumni, Family, Other
- Use Select for status: Pending, Asked, Referred, Declined
- Learning: Form variety, Select options
- createReferral(data)
- updateReferral(id, data)
- deleteReferral(id)
- Learning: CRUD mastery
- Add status dropdown on ReferralCard
- Update without opening full form
- Learning: Quick actions, inline editing
- If follow_up_date < TODAY and status != 'Referred'
- Add red border or badge
- Sort overdue to top
- Learning: Conditional styling
- In Add/Edit Application form, add "From Referral?" field
- Dropdown populated with user's referrals
- Store referral_id
- Learning: Related data, foreign keys
- On ReferralCard, show badge: "3 applications"
- Click to expand and see list
- Query: WHERE referral_id = ?
- Learning: Reverse relationships
- For each referral, count linked applications
- Count offers
- Success rate = offers / total
- Learning: Related data calculations
Duration: 3-4 hours | Day 7
- Go to Supabase → Storage
- Create bucket: "resumes"
- Make it private
- Learning: Cloud storage, bucket config
- Go to Storage → resumes → Policies
- Create INSERT policy: users upload to their folder
- Create SELECT policy: users read their files
- Create DELETE policy: users delete their files
- Learning: Storage RLS
- Create file:
app/(protected)/resumes/page.tsx - Heading: "Resume Versions"
- "Upload New Resume" button
- Learning: File management UI
- File input: accept only .pdf
- Text input: version_name
- Validate file size: max 5MB
- Validate file type
- Learning: File inputs, validation
- Get file from input
- Generate unique filename:
${userId}/${Date.now()}_${filename} - Call:
supabase.storage.from('resumes').upload(path, file) - Get public URL
- Learning: File upload, Storage API
- After upload, insert to resumes table
- Data: user_id, version_name, file_url, upload_date
- Learning: Metadata storage
- Create
components/resumes/ResumeCard.tsx - Show: version_name, upload_date, times_used, success_rate
- Add download and delete buttons
- Learning: File listing
- On download click
- Call:
supabase.storage.from('resumes').download(path) - Create blob and trigger download
- Learning: File download
- In Add/Edit Application form
- Add "Resume Used" dropdown
- Populate with user's resumes
- Store resume_id
- Learning: Related data selection
- When application created with resume_id
- Increment:
SET times_used = times_used + 1 - Learning: Usage tracking
- For each resume, query linked applications
- Count total and count offers
- Success rate = (offers / total) * 100
- Update resumes table
- Learning: Complex calculations
- Delete from storage first
- Then delete from database
- Handle FK constraints
- Learning: Cascading deletes
Duration: 3-4 hours | Day 8
- Create file:
app/(protected)/interviews/page.tsx - Heading: "Upcoming Interviews"
- "Schedule Interview" button
- Learning: Page organization
- Fields: application_id, round_name, scheduled_date, scheduled_time, prep_notes
- Application dropdown: user's applications
- Round examples: Phone Screen, Technical, Managerial, HR
- Learning: DateTime inputs
- createInterview(data)
- updateInterview(id, data)
- deleteInterview(id)
- Learning: CRUD patterns
- Query: ORDER BY scheduled_date ASC
- Create
components/interviews/InterviewCard.tsx - Show: company, role, round, date/time
- Highlight today's interviews
- Learning: Sorting by date
- Status: Scheduled, Completed, Cancelled
- Status dropdown for quick update
- Learning: Status management
- Rich textarea for notes
- Save to prep_notes column
- Show in expandable section
- Learning: Text content management
- After completion, add feedback
- Show only when status = Completed
- Learning: Conditional fields
- In application detail, show related interviews
- Query: WHERE application_id = ?
- Display as timeline/list
- Learning: Related data display
- When interview completed
- Prompt: "Update application status?"
- Learning: Business logic, state transitions
- Show interviews on monthly calendar
- Basic calendar grid (7 columns)
- Click date to see interviews
- Learning: Calendar UI
Duration: 2-3 hours | Day 9 Morning
- Go to resend.com and sign up
- Verify email
- Navigate to API Keys
- Create and copy API key
- Learning: Third-party service setup
- Run:
npm install resend - Add to .env.local:
RESEND_API_KEY=re_xxx - Learning: API keys management
- Create file:
lib/email/resend.ts - Initialize Resend client
- Export send function
- Learning: Service initialization
- Create folder:
lib/email/templates/ - Create interviewReminderTemplate.ts
- Create followUpReminderTemplate.ts
- Create dailyDigestTemplate.ts
- Use inline CSS
- Learning: HTML emails
- Create:
app/api/send-email/route.ts - Accept POST with email type and data
- Call resend.emails.send()
- Learning: API routes, POST handling
- Create:
app/(protected)/settings/page.tsx - Form to save user email
- Toggles for different notification types
- Save to user_preferences
- Learning: Settings management
- Function: checkUpcomingInterviews(userId)
- Query: scheduled_date within 24 hours
- Send email if not already sent
- Learning: Reminder logic
- Function: checkOverdueFollowUps(userId)
- Query: follow_up_date <= TODAY
- Send email for overdue items
- Learning: Overdue detection
- Function: generateDailyDigest(userId)
- Aggregate: applications today, interviews today, pending
- Send summary email
- Learning: Data aggregation
- Create:
app/api/send-reminders/route.ts - Loop through all users
- Call reminder functions
- Learning: Batch processing
- Call manual trigger
- Check Gmail inbox
- Verify formatting
- Test on mobile email
- Learning: Email testing
Duration: 3-4 hours | Day 9 Afternoon
- Open Telegram app
- Search @BotFather
- Send: /newbot
- Choose name and username
- Copy bot token
- Learning: Telegram Bot API
- Add:
TELEGRAM_BOT_TOKEN=123456:ABC... - Learning: Token management
- Start chat with bot
- Send message
- Call: getUpdates API
- Extract chat_id
- Learning: Telegram API exploration
- Create file:
lib/telegram/client.ts - Write sendMessage function
- Use fetch to call Telegram API
- Learning: REST API calls
- Create:
app/api/test-telegram/route.ts - Send: "Hello from Job Tracker!"
- Check Telegram app
- Learning: API testing
- In settings, add Telegram Chat ID input
- Add instructions
- Save to user_preferences
- Learning: User configuration
- Use emojis: 🎯 📅 🏢 💼
- Format with newlines
- Create templates for notifications
- Learning: Message formatting
- Check if telegram_notifications enabled
- Send message via Telegram
- Send 24h and 2h before
- Learning: Multi-channel notifications
- Same logic as email
- Send via Telegram
- Learning: Consistent patterns
- Send morning message with stats
- Format as bullet points
- Learning: Summary messaging
- Wrap in try-catch
- Handle: bot blocked, invalid chat_id, network errors
- Don't break app
- Learning: Error handling
- Function: sendNotification(userId, type, data)
- Check preferences
- Send email and/or Telegram
- Learning: Multi-channel orchestration
Duration: 2-3 hours | Day 10 Morning
- Read Vercel Cron docs
- Learn cron syntax:
0 8 * * * - Understand time zones
- Learning: Cron jobs, scheduled tasks
- Create:
app/api/cron/send-reminders/route.ts - Handle GET request
- Learning: Cron endpoints
- Query all users
- Loop through each
- Check interviews and follow-ups
- Send notifications per preferences
- Learning: Batch processing
- Create/update
vercel.json - Add crons section with path and schedule
- Schedule: "30 2 * * *" for 8 AM IST
- Learning: Vercel configuration
- Protect route from unauthorized access
- Check Vercel cron secret header
- Learning: API security
- Log when cron runs
- Log users processed
- Log notifications sent
- Log errors
- Learning: Production logging
- Create test button to call endpoint
- Verify logic works
- Learning: Testing scheduled tasks
- Deploy to Vercel
- Check Vercel → Cron Jobs
- Verify scheduled
- Check logs
- Learning: Production cron
Duration: 3-4 hours | Day 10 Afternoon - Day 11
- Create:
app/(protected)/analytics/page.tsx - Grid layout for analytics sections
- Learning: Analytics structure
- Group applications by resume_id
- Calculate response rate per resume
- Display as bar chart
- Learning: GROUP BY queries
- Add company_tier field or mapping
- Tiers: FAANG, Unicorn, Mid-Size, Startup
- Calculate success rates
- Learning: Categorization
- Group: referral_id NOT NULL vs NULL
- Calculate response rates
- Display comparison
- Learning: NULL handling
- Add response_date field
- Calculate: AVG(response_date - applied_date)
- Display in days
- Learning: Date arithmetic
- For status = Offer
- Calculate: AVG(offer_date - applied_date)
- Display by tier
- Learning: Timeline analytics
- Stages: Applied → OA → Interview → Offer
- Count at each stage
- Calculate drop-off rates
- Learning: Funnel analysis
- Extract day of week
- Calculate response rate per day
- Display as bar chart
- Learning: Day-of-week analysis
- Calculate avg response time per company
- Sort by fastest
- Display top 10
- Learning: Company analytics
- Calculate offer rate per company
- Identify best companies
- Learning: Performance analytics
- Group by month
- Count per month
- Display as line chart
- Learning: Monthly aggregation
- Date range picker
- Filter all analytics
- Update charts dynamically
- Learning: Dynamic filtering
- Stack charts vertically
- Horizontal scroll
- Smaller dimensions
- Learning: Responsive data viz
Duration: 3 hours | Day 11 Afternoon
- Create:
app/(protected)/calendar/page.tsx - Or use list view for MVP
- Learning: Calendar vs list
- Query interviews with dates
- Query referrals with follow-up dates
- Combine into events array
- Learning: Multi-source aggregation
- Sort by date
- Group: Today, Tomorrow, This Week, Later
- Learning: Date grouping
- 7-column grid for days
- Place events on dates
- Click to see details
- Learning: Calendar grid
- Highlight current date
- Show "Today" label
- Auto-scroll
- Learning: Current date handling
- Previous/Next buttons
- Update month display
- Learning: State management
- Show today's tasks
- Interviews today
- Follow-ups due
- Learning: Task lists
- Use list view on mobile
- Weekly view instead of monthly
- Learning: Mobile calendar UX
Duration: 4-5 hours | Day 12-13
- Use shadcn Sheet for menu
- Hamburger icon
- Slide-out menu
- Learning: Mobile navigation
- Use Chrome DevTools
- Test 375px, 768px, 1024px
- Note issues
- Learning: Responsive testing
- 44px min height for inputs/buttons
- Use type="email", type="tel"
- Ensure keyboard doesn't hide inputs
- Learning: Mobile forms
- Replace Dialog with Sheet on mobile
- Slides from bottom
- Learning: Adaptive components
- Convert to cards on mobile
- Hide less important columns
- Learning: Responsive tables
- Reduce heading sizes
- 16px min for body (prevents zoom)
- Increase line height
- Learning: Responsive typography
- Reduce padding on mobile
- Comfortable thumb reach
- More tap area
- Learning: Mobile spacing
- Every loading component
- Match content shape
- Learning: Loading states
- User-friendly errors
- Add error boundaries
- Add retry buttons
- Learning: Error UX
- Toast for all actions
- Use shadcn Toast/Sonner
- Learning: User feedback
- Tap all buttons
- Test dropdowns
- Learning: Touch usability
- Open on phone
- Test all features
- Note lag
- Learning: Real device testing
- Detect offline
- Show banner
- Learning: Network detection
- Compress images
- Lazy load
- Use WebP
- Learning: Performance
Duration: 4-5 hours | Day 14
- List all needed env vars
- Prepare for production
- Learning: Deployment prep
- Update Supabase Site URL to production
- Add production to redirect URLs
- Update email templates
- Learning: Production auth
- Push to GitHub
- Go to vercel.com
- Import repository
- Learning: CI/CD setup
- Add all env vars in Vercel
- Set for all environments
- Learning: Production config
- Click "Deploy"
- Monitor build logs
- Fix errors
- Learning: Deployment process
- Test signup with real email
- Test confirmation
- Test login/logout
- Test forgot password
- Learning: Production testing
- Create two accounts
- Verify data isolation
- Learning: Security verification
- Try to manipulate URLs
- Verify can't access others' data
- Learning: Security testing
- Complete user flow
- Desktop and mobile
- Email and Telegram
- Verify cron
- Learning: Comprehensive testing
- Run Lighthouse audit
- Check scores (aim >90)
- Fix critical issues
- Learning: Performance metrics
- Add domain to Vercel
- Configure DNS
- Wait for SSL
- Learning: Domain config
- Project description
- Features list with emojis
- Screenshots
- Tech stack
- Setup instructions
- Live demo link
- Learning: Documentation
- Record screen
- Show mobile view
- Create GIF
- Learning: Product demo
- 3 technical challenges solved
- Practice 2-min architecture explanation
- Prepare to demo live
- Think of improvements
- Learning: Interview prep
- Share with friends/peers
- Note bugs and issues
- Create backlog
- Fix critical issues
- Learning: User feedback
By completing all phases, you will have:
- ✅ Next.js 14 App Router mastery
- ✅ TypeScript with React
- ✅ Supabase (PostgreSQL, Auth, Storage, RLS)
- ✅ Tailwind CSS responsive design
- ✅ shadcn/ui component library
- ✅ API integration (Resend, Telegram)
- ✅ Deployment with Vercel
- ✅ Cron jobs and scheduled tasks
- ✅ Production-ready web application
- ✅ Mobile-responsive UI
- ✅ Multi-user authentication system
- ✅ Automated email/Telegram notifications
- ✅ Data analytics and visualization
- ✅ Comprehensive documentation
- ✅ Portfolio-worthy demo
- ✅ Full-stack project to discuss
- ✅ Understanding of modern web architecture
- ✅ Experience with production deployment
- ✅ Security best practices (RLS, auth)
- ✅ Real-world problem solving
Week 1:
- Day 1: Phases 0, 0.5, 0.6 (Setup + Auth)
- Day 2: Phase 1 (Database)
- Day 3: Phase 2 Part 1 (Applications CRUD)
- Day 4: Phase 2 Part 2 (Filters + Views)
Week 2:
- Day 5: Phase 3 (Dashboard)
- Day 6: Phase 4 (Referrals)
- Day 7: Phase 5 (Resumes)
- Day 8: Phase 6 (Interviews)
Week 3:
- Day 9: Phases 6.5, 6.6 (Notifications)
- Day 10: Phases 6.7, 7 (Cron + Analytics)
- Day 11: Phase 8 (Calendar)
- Day 12-13: Phase 9 (Mobile Polish)
Week 4:
- Day 14: Phase 10 (Deployment)
- Day 15: Buffer for fixes
- Save this file in your project root as
TASKS.md - Check off tasks as you complete them
- Commit frequently to Git after each completed task
- Test thoroughly before moving to next phase
- Document learnings in your daily journal
Ready to build? Let's start with Phase 0! 🎯