+
{/* Sticky top navbar */}
-
{children}
+
+ {children}
+
);
}
diff --git a/apps/admin/app/(main)/page.tsx b/apps/admin/app/(main)/page.tsx
index e9ff9d3..6642297 100644
--- a/apps/admin/app/(main)/page.tsx
+++ b/apps/admin/app/(main)/page.tsx
@@ -1,17 +1,15 @@
-import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@workspace/ui/components/card';
+import {
+ Card, CardContent, CardFooter, CardHeader, CardTitle,
+} from '@workspace/ui/components/card';
import { Input } from '@workspace/ui/components/input';
import { Textarea } from '@workspace/ui/components/textarea';
import { Button } from '@workspace/ui/components/button';
-import Link from 'next/link';
-import { revalidatePath } from 'next/cache';
-import { db, companies, jobs } from '@workspace/db';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogDescription } from '@workspace/ui/components/dialog';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@workspace/ui/components/accordion';
import { Badge } from '@workspace/ui/components/badge';
-
-// -----------------------
-// Server Actions
-// -----------------------
+import Link from 'next/link';
+import { revalidatePath } from 'next/cache';
+import { db, companies, jobs } from '@workspace/db';
async function createCompany(formData: FormData) {
'use server';
@@ -19,7 +17,9 @@ async function createCompany(formData: FormData) {
const email = String(formData.get('email') ?? '').trim();
const link = String(formData.get('link') ?? '').trim();
const description = String(formData.get('description') ?? '').trim() || 'N/A';
- const imageURL = String(formData.get('imageURL') ?? '').trim() || 'https://via.placeholder.com/200x200?text=Company';
+ const imageURL =
+ String(formData.get('imageURL') ?? '').trim() ||
+ 'https://via.placeholder.com/200x200?text=Company';
if (!name) return;
@@ -34,7 +34,9 @@ async function createJob(formData: FormData) {
const link = String(formData.get('jobLink') ?? '').trim();
const description = String(formData.get('jobDescription') ?? '').trim() || 'N/A';
const location = String(formData.get('location') ?? '').trim() || 'N/A';
- const imageURL = String(formData.get('jobImageURL') ?? '').trim() || 'https://via.placeholder.com/100x100?text=Job';
+ const imageURL =
+ String(formData.get('jobImageURL') ?? '').trim() ||
+ 'https://via.placeholder.com/100x100?text=Job';
const salary = String(formData.get('salary') ?? '').trim() || 'N/A';
const deadlineRaw = formData.get('deadline');
const applicationDeadline = deadlineRaw ? new Date(String(deadlineRaw)) : new Date();
@@ -55,103 +57,62 @@ async function createJob(formData: FormData) {
revalidatePath('/');
}
-// -----------------------
-// Component Helpers
-// -----------------------
-
async function getDashboardData() {
- const comps = await db.select().from(companies);
- const allJobs = await db.select().from(jobs);
-
- return comps.map((comp) => ({
- ...comp,
- jobs: allJobs.filter((j) => j.companyId === comp.id),
- }));
+ return await db.query.companies.findMany({ with: { jobs: true } });
}
export default async function DashboardPage() {
const data = await getDashboardData();
return (
-
-
-
-
Companies Dashboard
-
Manage companies and their job openings.
-
+
+
+ Companies Dashboard
-
- {data.length === 0 && No companies yet. Add your first company to get started!}
-
-
- {data.map((company) => (
-
-
-
-
-
{company.name}
-
{company.link}
+
+ {data.map((company) => (
+
+
{company.name}
+
+ )}
+
+
+ ))}
);
diff --git a/apps/admin/package.json b/apps/admin/package.json
index a8693da..0e6d5e3 100644
--- a/apps/admin/package.json
+++ b/apps/admin/package.json
@@ -12,10 +12,12 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
+ "@hookform/resolvers": "^5.1.1",
"@tailwindcss/postcss": "^4.0.8",
"@tanstack/react-table": "^8.21.3",
"@workspace/db": "workspace:*",
"@workspace/ui": "workspace:*",
+ "date-fns": "^4.1.0",
"framer-motion": "^12.22.0",
"lucide-react": "^0.475.0",
"next": "^15.3.4",
@@ -23,6 +25,7 @@
"next-themes": "^0.4.6",
"react": "^19.1.0",
"react-dom": "^19.1.0",
+ "react-hook-form": "^7.59.0",
"tailwindcss": "^4.0.8",
"zod": "^3.25.67"
},
diff --git a/packages/db/index.ts b/packages/db/index.ts
index 6b47fc0..b827539 100644
--- a/packages/db/index.ts
+++ b/packages/db/index.ts
@@ -1,5 +1,6 @@
import { drizzle } from 'drizzle-orm/neon-http';
+import * as schema from './schema.ts';
-export const db = drizzle(process.env.DATABASE_URL!);
+export const db = drizzle(process.env.DATABASE_URL!, { schema });
-export * from './schema.ts';
+export * from './schema.ts';
\ No newline at end of file
diff --git a/packages/db/migrations/0006_mysterious_lionheart.sql b/packages/db/migrations/0006_mysterious_lionheart.sql
new file mode 100644
index 0000000..aecb8d3
--- /dev/null
+++ b/packages/db/migrations/0006_mysterious_lionheart.sql
@@ -0,0 +1,30 @@
+ALTER TABLE "applications" RENAME COLUMN "job_id" TO "jobId";--> statement-breakpoint
+ALTER TABLE "applications" RENAME COLUMN "student_id" TO "studentId";--> statement-breakpoint
+ALTER TABLE "applications" RENAME COLUMN "resume_id" TO "resumeId";--> statement-breakpoint
+ALTER TABLE "grades" RENAME COLUMN "student_id" TO "studentId";--> statement-breakpoint
+ALTER TABLE "internships" RENAME COLUMN "student_id" TO "studentId";--> statement-breakpoint
+ALTER TABLE "jobs" RENAME COLUMN "company_id" TO "companyId";--> statement-breakpoint
+ALTER TABLE "resumes" RENAME COLUMN "student_id" TO "studentId";--> statement-breakpoint
+ALTER TABLE "applications" DROP CONSTRAINT "applications_job_id_jobs_id_fk";
+--> statement-breakpoint
+ALTER TABLE "applications" DROP CONSTRAINT "applications_student_id_students_id_fk";
+--> statement-breakpoint
+ALTER TABLE "applications" DROP CONSTRAINT "applications_resume_id_resumes_id_fk";
+--> statement-breakpoint
+ALTER TABLE "grades" DROP CONSTRAINT "grades_student_id_students_id_fk";
+--> statement-breakpoint
+ALTER TABLE "internships" DROP CONSTRAINT "internships_student_id_students_id_fk";
+--> statement-breakpoint
+ALTER TABLE "jobs" DROP CONSTRAINT "jobs_company_id_companies_id_fk";
+--> statement-breakpoint
+ALTER TABLE "resumes" DROP CONSTRAINT "resumes_student_id_students_id_fk";
+--> statement-breakpoint
+ALTER TABLE "grades" DROP CONSTRAINT "grades_student_id_sem_pk";--> statement-breakpoint
+ALTER TABLE "grades" ADD CONSTRAINT "grades_studentId_sem_pk" PRIMARY KEY("studentId","sem");--> statement-breakpoint
+ALTER TABLE "applications" ADD CONSTRAINT "applications_jobId_jobs_id_fk" FOREIGN KEY ("jobId") REFERENCES "public"."jobs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "applications" ADD CONSTRAINT "applications_studentId_students_id_fk" FOREIGN KEY ("studentId") REFERENCES "public"."students"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "applications" ADD CONSTRAINT "applications_resumeId_resumes_id_fk" FOREIGN KEY ("resumeId") REFERENCES "public"."resumes"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "grades" ADD CONSTRAINT "grades_studentId_students_id_fk" FOREIGN KEY ("studentId") REFERENCES "public"."students"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "internships" ADD CONSTRAINT "internships_studentId_students_id_fk" FOREIGN KEY ("studentId") REFERENCES "public"."students"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "jobs" ADD CONSTRAINT "jobs_companyId_companies_id_fk" FOREIGN KEY ("companyId") REFERENCES "public"."companies"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "resumes" ADD CONSTRAINT "resumes_studentId_students_id_fk" FOREIGN KEY ("studentId") REFERENCES "public"."students"("id") ON DELETE no action ON UPDATE no action;
\ No newline at end of file
diff --git a/packages/db/migrations/meta/0006_snapshot.json b/packages/db/migrations/meta/0006_snapshot.json
new file mode 100644
index 0000000..fc4a0c8
--- /dev/null
+++ b/packages/db/migrations/meta/0006_snapshot.json
@@ -0,0 +1,769 @@
+{
+ "id": "9394fab4-d946-45ac-ac5a-2fc22cf46a07",
+ "prevId": "1506d00a-2620-44ac-af01-19e04c21f679",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.admins": {
+ "name": "admins",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "admins_email_unique": {
+ "name": "admins_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.applications": {
+ "name": "applications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "jobId": {
+ "name": "jobId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "studentId": {
+ "name": "studentId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resumeId": {
+ "name": "resumeId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "applications_jobId_jobs_id_fk": {
+ "name": "applications_jobId_jobs_id_fk",
+ "tableFrom": "applications",
+ "tableTo": "jobs",
+ "columnsFrom": [
+ "jobId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "applications_studentId_students_id_fk": {
+ "name": "applications_studentId_students_id_fk",
+ "tableFrom": "applications",
+ "tableTo": "students",
+ "columnsFrom": [
+ "studentId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "applications_resumeId_resumes_id_fk": {
+ "name": "applications_resumeId_resumes_id_fk",
+ "tableFrom": "applications",
+ "tableTo": "resumes",
+ "columnsFrom": [
+ "resumeId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.companies": {
+ "name": "companies",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link": {
+ "name": "link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "imageURL": {
+ "name": "imageURL",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.grades": {
+ "name": "grades",
+ "schema": "",
+ "columns": {
+ "studentId": {
+ "name": "studentId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sem": {
+ "name": "sem",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sgpi": {
+ "name": "sgpi",
+ "type": "numeric(4, 2)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "isKT": {
+ "name": "isKT",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deadKT": {
+ "name": "deadKT",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "grades_studentId_students_id_fk": {
+ "name": "grades_studentId_students_id_fk",
+ "tableFrom": "grades",
+ "tableTo": "students",
+ "columnsFrom": [
+ "studentId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "grades_studentId_sem_pk": {
+ "name": "grades_studentId_sem_pk",
+ "columns": [
+ "studentId",
+ "sem"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "sem_check1": {
+ "name": "sem_check1",
+ "value": "\"grades\".\"sem\" < 9"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.internships": {
+ "name": "internships",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "studentId": {
+ "name": "studentId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "company": {
+ "name": "company",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "startDate": {
+ "name": "startDate",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "endDate": {
+ "name": "endDate",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "internships_studentId_students_id_fk": {
+ "name": "internships_studentId_students_id_fk",
+ "tableFrom": "internships",
+ "tableTo": "students",
+ "columnsFrom": [
+ "studentId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.jobs": {
+ "name": "jobs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "companyId": {
+ "name": "companyId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link": {
+ "name": "link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "imageURL": {
+ "name": "imageURL",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "salary": {
+ "name": "salary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "applicationDeadline": {
+ "name": "applicationDeadline",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "minCGPA": {
+ "name": "minCGPA",
+ "type": "numeric(4, 2)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "minSSC": {
+ "name": "minSSC",
+ "type": "numeric(4, 2)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "minHSC": {
+ "name": "minHSC",
+ "type": "numeric(4, 2)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "allowDeadKT": {
+ "name": "allowDeadKT",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "allowLiveKT": {
+ "name": "allowLiveKT",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "jobs_companyId_companies_id_fk": {
+ "name": "jobs_companyId_companies_id_fk",
+ "tableFrom": "jobs",
+ "tableTo": "companies",
+ "columnsFrom": [
+ "companyId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.resumes": {
+ "name": "resumes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "studentId": {
+ "name": "studentId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link": {
+ "name": "link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "resumes_studentId_students_id_fk": {
+ "name": "resumes_studentId_students_id_fk",
+ "tableFrom": "resumes",
+ "tableTo": "students",
+ "columnsFrom": [
+ "studentId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.students": {
+ "name": "students",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rollNumber": {
+ "name": "rollNumber",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "verified": {
+ "name": "verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "firstName": {
+ "name": "firstName",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "middleName": {
+ "name": "middleName",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lastName": {
+ "name": "lastName",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mothersName": {
+ "name": "mothersName",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gender": {
+ "name": "gender",
+ "type": "varchar(10)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dob": {
+ "name": "dob",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "personalGmail": {
+ "name": "personalGmail",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "phoneNumber": {
+ "name": "phoneNumber",
+ "type": "varchar(10)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "address": {
+ "name": "address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "profilePicture": {
+ "name": "profilePicture",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "degree": {
+ "name": "degree",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "year": {
+ "name": "year",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "skills": {
+ "name": "skills",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "ARRAY[]::text[]"
+ },
+ "linkedin": {
+ "name": "linkedin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "github": {
+ "name": "github",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ssc": {
+ "name": "ssc",
+ "type": "numeric(4, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "hsc": {
+ "name": "hsc",
+ "type": "numeric(4, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "isDiploma": {
+ "name": "isDiploma",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json
index 261d76c..c42f81e 100644
--- a/packages/db/migrations/meta/_journal.json
+++ b/packages/db/migrations/meta/_journal.json
@@ -43,6 +43,13 @@
"when": 1751182452704,
"tag": "0005_solid_photon",
"breakpoints": true
+ },
+ {
+ "idx": 6,
+ "version": "7",
+ "when": 1751619273079,
+ "tag": "0006_mysterious_lionheart",
+ "breakpoints": true
}
]
-}
+}
\ No newline at end of file
diff --git a/packages/db/schema.ts b/packages/db/schema.ts
index 79dcdda..3ef9a90 100644
--- a/packages/db/schema.ts
+++ b/packages/db/schema.ts
@@ -12,7 +12,7 @@ import {
check,
} from 'drizzle-orm/pg-core';
-export { createSelectSchema } from 'drizzle-zod';
+export { createSelectSchema, createInsertSchema } from 'drizzle-zod';
export const students = pgTable('students', {
id: serial().primaryKey(),
@@ -49,7 +49,7 @@ export const students = pgTable('students', {
export const internships = pgTable('internships', {
id: serial().primaryKey(),
- studentId: integer('student_id')
+ studentId: integer()
.notNull()
.references(() => students.id),
title: text().notNull(),
@@ -101,7 +101,7 @@ export const internships = pgTable('internships', {
export const resumes = pgTable('resumes', {
id: serial().primaryKey(),
- studentId: integer('student_id')
+ studentId: integer()
.notNull()
.references(() => students.id),
title: text().notNull(),
@@ -116,7 +116,7 @@ export const resumes = pgTable('resumes', {
export const grades = pgTable(
'grades',
{
- studentId: integer('student_id')
+ studentId: integer()
.notNull()
.references(() => students.id),
sem: integer().notNull(),
@@ -196,7 +196,7 @@ export const companies = pgTable('companies', {
export const jobs = pgTable('jobs', {
id: serial().primaryKey(),
- companyId: integer('company_id')
+ companyId: integer()
.notNull()
.references(() => companies.id),
title: text().notNull(),
@@ -221,13 +221,13 @@ export const jobs = pgTable('jobs', {
export const applications = pgTable('applications', {
id: serial().primaryKey(),
- jobId: integer('job_id')
+ jobId: integer()
.notNull()
.references(() => jobs.id),
- studentId: integer('student_id')
+ studentId: integer()
.notNull()
.references(() => students.id),
- resumeId: integer('resume_id')
+ resumeId: integer()
.notNull()
.references(() => resumes.id),
status: text().notNull().default('pending'),
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 9f8e4c6..e42a5a1 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -12,15 +12,18 @@
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-navigation-menu": "^1.2.0",
+ "@radix-ui/react-popover": "^1.1.14",
"@radix-ui/react-progress": "^1.1.7",
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "date-fns": "^4.1.0",
"lucide-react": "^0.475.0",
"next-themes": "^0.4.4",
"react": "^19.0.0",
+ "react-day-picker": "^9.7.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.58.1",
"tailwind-merge": "^3.0.1",
diff --git a/packages/ui/src/components/calendar.tsx b/packages/ui/src/components/calendar.tsx
new file mode 100644
index 0000000..562bbac
--- /dev/null
+++ b/packages/ui/src/components/calendar.tsx
@@ -0,0 +1,210 @@
+"use client"
+
+import * as React from "react"
+import {
+ ChevronDownIcon,
+ ChevronLeftIcon,
+ ChevronRightIcon,
+} from "lucide-react"
+import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker"
+
+import { cn } from "@workspace/ui/lib/utils"
+import { Button, buttonVariants } from "@workspace/ui/components/button"
+
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = true,
+ captionLayout = "label",
+ buttonVariant = "ghost",
+ formatters,
+ components,
+ ...props
+}: React.ComponentProps & {
+ buttonVariant?: React.ComponentProps["variant"]
+}) {
+ const defaultClassNames = getDefaultClassNames()
+
+ return (
+ svg]:rotate-180`,
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
+ className
+ )}
+ captionLayout={captionLayout}
+ formatters={{
+ formatMonthDropdown: (date) =>
+ date.toLocaleString("default", { month: "short" }),
+ ...formatters,
+ }}
+ classNames={{
+ root: cn("w-fit", defaultClassNames.root),
+ months: cn(
+ "flex gap-4 flex-col md:flex-row relative",
+ defaultClassNames.months
+ ),
+ month: cn("flex flex-col w-full gap-4", defaultClassNames.month),
+ nav: cn(
+ "flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
+ defaultClassNames.nav
+ ),
+ button_previous: cn(
+ buttonVariants({ variant: buttonVariant }),
+ "size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
+ defaultClassNames.button_previous
+ ),
+ button_next: cn(
+ buttonVariants({ variant: buttonVariant }),
+ "size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
+ defaultClassNames.button_next
+ ),
+ month_caption: cn(
+ "flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)",
+ defaultClassNames.month_caption
+ ),
+ dropdowns: cn(
+ "w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5",
+ defaultClassNames.dropdowns
+ ),
+ dropdown_root: cn(
+ "relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md",
+ defaultClassNames.dropdown_root
+ ),
+ dropdown: cn("absolute inset-0 opacity-0", defaultClassNames.dropdown),
+ caption_label: cn(
+ "select-none font-medium",
+ captionLayout === "label"
+ ? "text-sm"
+ : "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5",
+ defaultClassNames.caption_label
+ ),
+ table: "w-full border-collapse",
+ weekdays: cn("flex", defaultClassNames.weekdays),
+ weekday: cn(
+ "text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none",
+ defaultClassNames.weekday
+ ),
+ week: cn("flex w-full mt-2", defaultClassNames.week),
+ week_number_header: cn(
+ "select-none w-(--cell-size)",
+ defaultClassNames.week_number_header
+ ),
+ week_number: cn(
+ "text-[0.8rem] select-none text-muted-foreground",
+ defaultClassNames.week_number
+ ),
+ day: cn(
+ "relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
+ defaultClassNames.day
+ ),
+ range_start: cn(
+ "rounded-l-md bg-accent",
+ defaultClassNames.range_start
+ ),
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
+ range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
+ today: cn(
+ "bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
+ defaultClassNames.today
+ ),
+ outside: cn(
+ "text-muted-foreground aria-selected:text-muted-foreground",
+ defaultClassNames.outside
+ ),
+ disabled: cn(
+ "text-muted-foreground opacity-50",
+ defaultClassNames.disabled
+ ),
+ hidden: cn("invisible", defaultClassNames.hidden),
+ ...classNames,
+ }}
+ components={{
+ Root: ({ className, rootRef, ...props }) => {
+ return (
+
+ )
+ },
+ Chevron: ({ className, orientation, ...props }) => {
+ if (orientation === "left") {
+ return (
+
+ )
+ }
+
+ if (orientation === "right") {
+ return (
+
+ )
+ }
+
+ return (
+
+ )
+ },
+ DayButton: CalendarDayButton,
+ WeekNumber: ({ children, ...props }) => {
+ return (
+
+
+ {children}
+
+ |
+ )
+ },
+ ...components,
+ }}
+ {...props}
+ />
+ )
+}
+
+function CalendarDayButton({
+ className,
+ day,
+ modifiers,
+ ...props
+}: React.ComponentProps) {
+ const defaultClassNames = getDefaultClassNames()
+
+ const ref = React.useRef(null)
+ React.useEffect(() => {
+ if (modifiers.focused) ref.current?.focus()
+ }, [modifiers.focused])
+
+ return (
+