Generate a fully functional, multi-page Next.js MVP application for 'JobMatch Hub', a platform designed to streamline the job search process for tech professionals by organizing and enhancing the information found in Hacker News' 'Ask HN: Who is hiring?' threads. The application should be built with the App Router, Tailwind CSS, and Drizzle ORM.
**PROJECT OVERVIEW:**
JobMatch Hub aims to solve the problem of fragmented and unstructured job listings within Hacker News' popular 'Ask HN: Who is hiring?' threads. Currently, users must manually sift through comments, apply filters in their heads, and track applications externally. JobMatch Hub will aggregate these listings, provide robust filtering and search capabilities, allow users to save interesting opportunities, and offer a way to track their application status. The core value proposition is to provide a centralized, efficient, and intelligent platform for tech professionals to discover and apply for jobs, saving them time and improving their chances of finding a suitable role.
**TECH STACK:**
- Framework: Next.js (App Router)
- Styling: Tailwind CSS
- ORM: Drizzle ORM (PostgreSQL compatible, e.g., with Vercel Postgres or Neon)
- UI Components: shadcn/ui
- Authentication: NextAuth.js (for user accounts)
- Database: PostgreSQL
- State Management: React Context API or Zustand (for global state if needed)
- Form Handling: React Hook Form
- Validation: Zod
- Data Fetching: React Server Components (RSC), Server Actions, or API Routes with `fetch`
- Other: `clsx` for conditional class names, `lucide-react` for icons
**DATABASE SCHEMA (PostgreSQL with Drizzle ORM):**
1. **`users` table:**
- `id` (UUID, primary key)
- `name` (TEXT)
- `email` (TEXT, unique)
- `emailVerified` (TIMESTAMP with time zone)
- `image` (TEXT)
- `createdAt` (TIMESTAMP with time zone, default now())
- `updatedAt` (TIMESTAMP with time zone, default now())
2. **`accounts` table (for NextAuth.js):**
- `id` (TEXT, primary key)
- `userId` (UUID, foreign key to `users.id`)
- `type` (TEXT)
- `provider` (TEXT)
- `providerAccountId` (TEXT)
- `refresh_token` (TEXT)
- `access_token` (TEXT)
- `expires_at` (BIGINT)
- `token_type` (TEXT)
- `scope` (TEXT)
- `id_token` (TEXT)
- `session_state` (TEXT)
3. **`sessions` table (for NextAuth.js):**
- `sessionToken` (TEXT, primary key)
- `userId` (UUID, foreign key to `users.id`)
- `expires` (TIMESTAMP with time zone)
4. **`verificationTokens` table (for NextAuth.js):**
- `identifier` (TEXT)
- `token` (TEXT, unique)
- `expires` (TIMESTAMP with time zone)
5. **`companies` table:**
- `id` (SERIAL, primary key)
- `name` (TEXT, unique, not null)
- `description` (TEXT)
- `website` (TEXT)
- `hn_thread_url` (TEXT) // Link to the specific HN thread
- `hn_post_id` (TEXT) // Unique ID from the HN post comment
6. **`job_listings` table:**
- `id` (SERIAL, primary key)
- `company_id` (INTEGER, foreign key to `companies.id`, not null)
- `title` (TEXT, not null)
- `location` (TEXT, not null) // e.g., "REMOTE", "REMOTE (US)", "ONSITE (New York, NY)"
- `employment_type` (TEXT) // e.g., "Full-time", "Contract"
- `description_snippet` (TEXT) // Short excerpt from the HN comment
- `posted_at_hn` (TIMESTAMP with time zone) // Timestamp of the HN comment
- `hn_comment_url` (TEXT) // Direct URL to the HN comment
- `is_remote_friendly` (BOOLEAN)
- `remote_country_restrictions` (TEXT) // e.g., "US", "EU", "Worldwide"
- `onsite_city` (TEXT)
- `onsite_country` (TEXT)
- `technologies` (TEXT[]) // Array of technologies mentioned
- `seniority_level` (TEXT) // e.g., "Junior", "Mid-level", "Senior", "Lead"
- `hiring_manager_name` (TEXT) // If available in the post
- `apply_email` (TEXT)
- `apply_url` (TEXT)
- `created_at` (TIMESTAMP with time zone, default now())
7. **`user_saved_jobs` table:**
- `user_id` (UUID, foreign key to `users.id`, not null)
- `job_listing_id` (INTEGER, foreign key to `job_listings.id`, not null)
- `saved_at` (TIMESTAMP with time zone, default now())
- PRIMARY KEY (`user_id`, `job_listing_id`)
8. **`user_applications` table:**
- `id` (SERIAL, primary key)
- `user_id` (UUID, foreign key to `users.id`, not null)
- `job_listing_id` (INTEGER, foreign key to `job_listings.id`, not null)
- `application_status` (TEXT, default 'Applied') // e.g., 'Applied', 'Interviewing', 'Offered', 'Rejected'
- `application_date` (TIMESTAMP with time zone, default now())
- `notes` (TEXT)
- `last_updated` (TIMESTAMP with time zone, default now())
**CORE FEATURES & USER FLOW:**
1. **Hacker News Data Ingestion:**
- A scheduled background job (or a manual trigger script) will fetch the latest 'Ask HN: Who is hiring?' thread URL from Hacker News (e.g., by scraping the front page or using the HN API if available for thread IDs).
- It will then scrape the comments of that thread. Basic parsing will identify potential job posts based on keywords and structure (e.g., "Hiring:", company name, location, description).
- A robust NLP or regex-based parser will extract `title`, `location`, `description_snippet`, `apply_email`/`apply_url`, `company_name`, `website` etc.
- Parsed data will be inserted into `companies` and `job_listings` tables. Existing companies/listings might be updated if new information is found.
- **Flow:** HN Thread -> Scraper -> Parser -> DB Insertion/Update.
2. **Job Listing Display & Filtering:**
- The main page (`/jobs`) will display a paginated list of job postings fetched from the `job_listings` table.
- **UI:** Each job listing card will show: Company Name, Job Title, Location, a snippet of the description, and a 'Save' button.
- **Filtering Sidebar:** A persistent sidebar will contain filter controls:
- **Location:** Checkboxes/Radio buttons for "Remote", "Remote (US Only)", "Remote (Country Specific)", "Onsite".
- **Country:** (Appears if "Remote (Country Specific)" or "Onsite" is selected) Dropdown/Multi-select for countries.
- **City:** (Appears if "Onsite" is selected) Text input.
- **Seniority Level:** Multi-select checkboxes (Junior, Mid-level, Senior, Lead, Staff, Principal).
- **Technology Stack:** Multi-select checkboxes/tags (React, Node.js, Python, Go, AWS, GCP, etc. - requires tech extraction logic).
- **Keywords:** Text search input for job title or description.
- **Filtering Logic:** When filters are applied, the client-side or server-side (preferable for performance with large datasets) query to the backend will update the displayed job list. Filters should persist in the URL (e.g., `/jobs?location=remote&tech=react`).
- **Flow:** User visits `/jobs` -> Default list displayed -> User interacts with filters -> API request (or RSC data fetch) -> Filtered list displayed.
3. **User Authentication:**
- Users can sign up/log in using email/password or OAuth (Google, GitHub).
- Protected routes (`/saved-jobs`, `/applications`, `/settings`) require authentication.
- **Flow:** User clicks 'Sign In' -> Modal/Page displays options -> User selects method -> Authentication provider handles flow -> User redirected to dashboard/previous page.
4. **Save Job Functionality:**
- Authenticated users can click a 'Save' icon/button on a job listing card.
- Clicking toggles the saved state. The icon changes (e.g., filled heart). The `user_saved_jobs` table is updated via a Server Action.
- **Flow:** User views job card -> Clicks 'Save' icon -> Server Action runs -> `user_saved_jobs` row created/deleted -> UI updates to reflect saved state.
5. **Saved Jobs Page (`/saved-jobs`):**
- Displays a list of jobs saved by the currently logged-in user, fetched from `user_saved_jobs` joined with `job_listings` and `companies`.
- Users can unsave jobs from this page.
- **Flow:** Authenticated User visits `/saved-jobs` -> Data fetched -> List displayed -> User clicks 'Unsave' -> Server Action runs -> DB updated -> UI updates.
6. **Application Tracking (`/applications`):**
- Authenticated users can add jobs they've applied for to their tracking list.
- **Add Application Flow:** User clicks 'Track Application' on a job listing -> A modal appears with fields: `application_date`, `application_status` (default 'Applied'), `notes` (optional). -> User submits -> Server Action creates a new entry in `user_applications`.
- **View/Edit Application Flow:** The `/applications` page lists all tracked applications. Each entry shows: Job Title, Company, Application Date, Status. Users can click to edit status, date, or notes. Status can be updated via dropdowns.
- **Flow:** User adds application -> User visits `/applications` -> List displayed -> User clicks 'Edit' -> Modal/Inline edit appears -> User updates details -> Server Action updates `user_applications`.
**API & DATA FETCHING:**
- Primarily use Server Actions for mutations (saving jobs, updating applications, user actions).
- Use React Server Components (RSC) for fetching and rendering data on pages like `/jobs`, `/saved-jobs`, `/applications` to leverage server-side rendering and caching.
- For dynamic client-side interactions (like real-time filter updates before submission), use `fetch` in client components or `useSWR`/`react-query` if needed, but prioritize RSC and Server Actions for MVP.
- API Routes (`/api/...`) can be used for specific tasks if Server Actions are not suitable, e.g., webhook integrations in the future.
- **Example Server Action (Saving a Job):**
```javascript
// app/actions.ts
'use server';
import { db } from '@/db'; // Your Drizzle instance
import { userSavedJobs } from '@/db/schema';
import { auth } from '@/auth'; // Your NextAuth setup
import { eq } from 'drizzle-orm';
export async function toggleSaveJob(jobListingId: number) {
const session = await auth();
if (!session?.user?.id) {
throw new Error('Authentication required');
}
const existingSave = await db.query.userSavedJobs.findFirst({
where: (t, { eq }) => eq(t.userId, session.user.id!)
});
if (existingSave) {
await db.delete(userSavedJobs)
.where(eq(userSavedJobs.jobListingId, jobListingId));
} else {
await db.insert(userSavedJobs)
.values({
userId: session.user.id,
jobListingId: jobListingId
});
}
revalidatePath('/jobs'); // Revalidate cache for job list page
}
```
**COMPONENT BREAKDOWN (Next.js App Router Structure):**
- **`app/layout.tsx`:** Root layout (includes `<html>`, `<body>`, global providers, theme setup).
- **`app/page.tsx`:** Homepage (brief intro, call to action to view jobs).
- **`app/jobs/page.tsx`:** Main job listings page. Uses RSC to fetch jobs and apply filters.
- **`app/jobs/components/JobList.tsx`:** (Client Component) Renders the list of job cards, handles pagination.
- **`app/jobs/components/JobCard.tsx`:** (Client Component) Displays a single job listing with save functionality.
- **`app/jobs/components/FilterSidebar.tsx`:** (Client Component) Contains all filter controls. Manages filter state and triggers data refetching/URL updates.
- **`app/auth/signin/page.tsx`:** Sign-in page.
- **`app/saved-jobs/page.tsx`:** Saved jobs page. Requires authentication. Uses RSC.
- **`app/saved-jobs/components/SavedJobList.tsx`:** Renders the user's saved jobs.
- **`app/applications/page.tsx`:** Application tracking page. Requires authentication. Uses RSC.
- **`app/applications/components/ApplicationTracker.tsx`:** Displays tracked applications, allows status updates.
- **`app/applications/components/AddApplicationForm.tsx`:** Modal/Form to add a new tracked application.
- **`app/components/ui/`:** Re-export common shadcn/ui components.
- **`app/components/Header.tsx`:** Navigation bar with logo, links, auth status.
- **`app/components/Footer.tsx`:** Footer section.
- **`app/components/Button.tsx`:** Custom button component.
- **`app/components/Input.tsx`:** Custom input component.
- **`lib/db.ts`:** Drizzle ORM instance setup.
- **`lib/auth.ts`:** NextAuth.js configuration.
- **`actions/`:** Directory for Server Actions.
- **`db/schema.ts`:** Drizzle schema definition.
**UI/UX DESIGN & VISUAL IDENTITY:**
- **Style:** Modern, Clean, Professional with a hint of Tech/Developer focus.
- **Color Palette:**
- Primary: `#6366F1` (Indigo-500/600) - for primary actions, links, highlights.
- Secondary: `#22D3EE` (Cyan-400/500) - for secondary highlights, accents.
- Background: `#0f172a` (Slate-900) - Dark mode primary background.
- Surface: `#1e293b` (Slate-800) - Card backgrounds, modal backgrounds.
- Text Primary: `#f8fafc` (Slate-100)
- Text Secondary: `#94a3b8` (Slate-400)
- Accent (Success): `#10B981` (Green-500)
- Accent (Warning): `#F59E0B` (Amber-500)
- Accent (Danger): `#EF4444` (Red-500)
- **Typography:** Inter (or a similar sans-serif like Roboto) for body text and headings. Ensure good readability and hierarchy.
- **Layout:**
- Use a max-width container for main content, centered.
- Header: Sticky, clean, with minimal navigation.
- Job Listings Page: Two-column layout - Filters on the left (sidebar), Job Cards on the right.
- Responsiveness: Mobile-first approach. Sidebar collapses into a toggleable drawer/modal on smaller screens. Cards stack vertically.
- **Interactions:** Subtle hover effects on cards and buttons. Smooth transitions for filter changes and loading states.
**ANIMATIONS:**
- **Loading States:** Use `react-skeletons` or Tailwind CSS spinners within buttons and content areas while data is fetching.
- **Transitions:** Fade-in/out for modals and filter changes. Subtle slide-in for the mobile filter drawer.
- **Hover Effects:** Slight scale-up or shadow increase on job cards and buttons on hover.
**EDGE CASES:**
- **No Job Listings:** Display a friendly message and perhaps a prompt to refresh or check back later.
- **No Saved Jobs / No Applications:** Display appropriate empty states with calls to action (e.g., "Start saving jobs!" or "Track your applications.").
- **Authentication:** Handle unauthorized access gracefully, redirecting to login. Ensure session management is secure.
- **Data Parsing Errors:** Log errors and potentially flag affected job listings as 'unprocessed' or display a warning to the user.
- **Form Validation:** Implement client-side and server-side validation for all user inputs (application tracking form, sign-up/login).
- **API Errors:** Display user-friendly error messages for failed data fetches or mutations.
- **HN Thread Unavailable:** Handle cases where the HN thread might be down or inaccessible.
**SAMPLE/MOCK DATA:**
1. **Company:** `name: