diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..12b7a58
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,18 @@
+Dockerfile
+.dockerignore
+node_modules
+npm-debug.log
+README.md
+.next
+.git
+.pnp
+.pnp.js
+.turbo
+.vercel
+.next/
+out/
+build
+dist
+npm-debug.log*
+.DS_Store
+*.pem
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..9767c4b
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,29 @@
+FROM node:22-alpine AS base
+ENV PNPM_HOME="/pnpm"
+ENV PATH="$PNPM_HOME:$PATH"
+RUN corepack enable && corepack prepare pnpm@latest --activate
+
+FROM base AS builder
+RUN apk update && apk add --no-cache libc6-compat
+WORKDIR /app
+RUN pnpm add -g turbo@^2
+COPY . .
+RUN turbo prune admin --docker
+
+FROM base AS installer
+RUN apk update && apk add --no-cache libc6-compat
+WORKDIR /app
+COPY --from=builder /app/out/json/ .
+RUN pnpm fetch --frozen-lockfile
+RUN pnpm install --offline --frozen-lockfile --prod=false
+COPY --from=builder /app/out/full/ .
+RUN pnpm turbo run build
+
+FROM base AS runner
+WORKDIR /app
+RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs
+USER nextjs
+COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
+COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
+COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
+CMD node apps/web/server.js
diff --git a/apps/admin/Dockerfile b/apps/admin/Dockerfile
new file mode 100644
index 0000000..9767c4b
--- /dev/null
+++ b/apps/admin/Dockerfile
@@ -0,0 +1,29 @@
+FROM node:22-alpine AS base
+ENV PNPM_HOME="/pnpm"
+ENV PATH="$PNPM_HOME:$PATH"
+RUN corepack enable && corepack prepare pnpm@latest --activate
+
+FROM base AS builder
+RUN apk update && apk add --no-cache libc6-compat
+WORKDIR /app
+RUN pnpm add -g turbo@^2
+COPY . .
+RUN turbo prune admin --docker
+
+FROM base AS installer
+RUN apk update && apk add --no-cache libc6-compat
+WORKDIR /app
+COPY --from=builder /app/out/json/ .
+RUN pnpm fetch --frozen-lockfile
+RUN pnpm install --offline --frozen-lockfile --prod=false
+COPY --from=builder /app/out/full/ .
+RUN pnpm turbo run build
+
+FROM base AS runner
+WORKDIR /app
+RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs
+USER nextjs
+COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
+COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
+COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
+CMD node apps/web/server.js
diff --git a/apps/admin/app/(main)/page.tsx b/apps/admin/app/(main)/page.tsx
index adda14f..4103738 100644
--- a/apps/admin/app/(main)/page.tsx
+++ b/apps/admin/app/(main)/page.tsx
@@ -1,19 +1,13 @@
-import Login from '@/components/login';
import Studs from '@/components/studs';
-import { db, admins } from '@workspace/db';
-import { auth, signIn, signOut } from '@/auth';
+import { db, students } from '@workspace/db';
+import { auth, signOut } from '@/auth';
async function getStudents() {
'use server';
- const s = await db.select().from(admins);
+ const s = await db.select().from(students);
console.log(s);
}
-async function logIn() {
- 'use server';
- await signIn('google');
-}
-
async function logOut() {
'use server';
await signOut();
@@ -25,7 +19,6 @@ export default async function Page() {
Hello admin {session?.user?.name}
- {!session?.user && }
diff --git a/apps/admin/app/(main)/students/columns.tsx b/apps/admin/app/(main)/students/columns.tsx
index 239ac0e..4d6fda6 100644
--- a/apps/admin/app/(main)/students/columns.tsx
+++ b/apps/admin/app/(main)/students/columns.tsx
@@ -6,20 +6,39 @@ const studentSelectSchema = createSelectSchema(students);
export type Student = z.infer;
export const columns: ColumnDef[] = [
- {
- accessorKey: 'id',
- header: 'ID',
- },
{
accessorKey: 'firstName',
header: 'First Name',
+ filterFn: 'includesString',
},
{
accessorKey: 'lastName',
header: 'Last Name',
+ filterFn: 'includesString',
+ },
+ {
+ accessorKey: 'rollNumber',
+ header: 'Roll Number',
+ filterFn: 'includesString',
},
{
accessorKey: 'email',
header: 'Email',
+ filterFn: 'includesString',
+ },
+ {
+ accessorKey: 'yearOfGraduation',
+ header: 'Year of Graduation',
+ filterFn: 'includesString',
+ },
+ {
+ accessorKey: 'degree',
+ header: 'Degree',
+ filterFn: 'includesString',
+ },
+ {
+ accessorKey: 'branch',
+ header: 'Branch',
+ filterFn: 'includesString',
},
];
diff --git a/apps/admin/app/(main)/students/data-table.tsx b/apps/admin/app/(main)/students/data-table.tsx
index 3a8dd74..fc097df 100644
--- a/apps/admin/app/(main)/students/data-table.tsx
+++ b/apps/admin/app/(main)/students/data-table.tsx
@@ -1,6 +1,12 @@
'use client';
-import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
+import {
+ ColumnDef,
+ flexRender,
+ getCoreRowModel,
+ getPaginationRowModel,
+ useReactTable,
+} from '@tanstack/react-table';
import {
Table,
@@ -21,6 +27,7 @@ export function DataTable({ columns, data }: DataTableProps void;
+}) {
+ useEffect(() => {
+ console.error('Students page error:', error);
+ }, [error]);
+
+ return (
+
+
+
Something went wrong!
+
+ Failed to load students data. Please try again.
+
+
+
+
+ );
+}
diff --git a/apps/admin/app/(main)/students/loading.tsx b/apps/admin/app/(main)/students/loading.tsx
new file mode 100644
index 0000000..4575c60
--- /dev/null
+++ b/apps/admin/app/(main)/students/loading.tsx
@@ -0,0 +1,63 @@
+import { Skeleton } from '@workspace/ui/components/skeleton';
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from '@workspace/ui/components/table';
+
+export default function Loading() {
+ return (
+
+
+ {/* Header skeleton */}
+
+
Students
+
+
+
+ {/* Table skeleton */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {Array.from({ length: 8 }).map((_, index) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/admin/app/(main)/students/page.tsx b/apps/admin/app/(main)/students/page.tsx
index ec18ffb..543735b 100644
--- a/apps/admin/app/(main)/students/page.tsx
+++ b/apps/admin/app/(main)/students/page.tsx
@@ -3,16 +3,32 @@ import { DataTable } from './data-table';
import { db, students } from '@workspace/db';
async function getData(): Promise {
- const data = db.select().from(students);
+ const data = await db.select().from(students);
return data;
}
-export default async function DemoPage() {
+async function StudentsTable() {
const data = await getData();
return (
-
+
+
+
Students
+
+ {data.length} {data.length === 1 ? 'student' : 'students'} total
+
+
+
+
);
}
+
+export default function StudentsPage() {
+ return (
+
+ );
+}
+
+export const dynamic = 'force-dynamic';
\ No newline at end of file
diff --git a/apps/admin/next.config.mjs b/apps/admin/next.config.mjs
index 3538f42..0f9b693 100644
--- a/apps/admin/next.config.mjs
+++ b/apps/admin/next.config.mjs
@@ -1,6 +1,12 @@
/** @type {import('next').NextConfig} */
+import path from 'path';
+
+const __dirname = path.resolve();
+
const nextConfig = {
transpilePackages: ['@workspace/ui', '@workspace/db'],
+ output: 'standalone',
+ outputFileTracingRoot: path.join(__dirname, '../../'),
};
export default nextConfig;
diff --git a/apps/admin/package.json b/apps/admin/package.json
index 039df49..a8693da 100644
--- a/apps/admin/package.json
+++ b/apps/admin/package.json
@@ -12,6 +12,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
+ "@tailwindcss/postcss": "^4.0.8",
"@tanstack/react-table": "^8.21.3",
"@workspace/db": "workspace:*",
"@workspace/ui": "workspace:*",
@@ -22,6 +23,7 @@
"next-themes": "^0.4.6",
"react": "^19.1.0",
"react-dom": "^19.1.0",
+ "tailwindcss": "^4.0.8",
"zod": "^3.25.67"
},
"devDependencies": {
diff --git a/apps/student/Dockerfile b/apps/student/Dockerfile
new file mode 100644
index 0000000..c5d53bd
--- /dev/null
+++ b/apps/student/Dockerfile
@@ -0,0 +1,38 @@
+FROM node:22-alpine AS base
+
+FROM base AS builder
+RUN apk update
+RUN apk add --no-cache libc6-compat
+WORKDIR /app
+
+RUN corepack enable pnpm && pnpm add -g turbo@^2
+COPY . .
+
+RUN turbo prune student --docker
+
+FROM base AS installer
+RUN apk update
+RUN apk add --no-cache libc6-compat
+WORKDIR /app
+
+COPY --from=builder /app/out/json/ .
+RUN yarn install --frozen-lockfile
+
+COPY --from=builder /app/out/full/ .
+RUN pnpm turbo run build
+
+FROM base AS runner
+WORKDIR /app
+
+# Don't run production as root
+RUN addgroup --system --gid 1001 nodejs
+RUN adduser --system --uid 1001 nextjs
+USER nextjs
+
+# Automatically leverage output traces to reduce image size
+# https://nextjs.org/docs/advanced-features/output-file-tracing
+COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
+COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
+COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
+
+CMD node apps/web/server.js
\ No newline at end of file
diff --git a/apps/student/next.config.mjs b/apps/student/next.config.mjs
index 3538f42..0f9b693 100644
--- a/apps/student/next.config.mjs
+++ b/apps/student/next.config.mjs
@@ -1,6 +1,12 @@
/** @type {import('next').NextConfig} */
+import path from 'path';
+
+const __dirname = path.resolve();
+
const nextConfig = {
transpilePackages: ['@workspace/ui', '@workspace/db'],
+ output: 'standalone',
+ outputFileTracingRoot: path.join(__dirname, '../../'),
};
export default nextConfig;
diff --git a/package.json b/package.json
index 36d68b1..9bfafed 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
"build": "turbo build",
"dev": "turbo dev",
"lint": "turbo lint",
+ "start": "turbo start",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"db:check": "pnpm --filter @workspace/db check",
"db:generate": "pnpm --filter @workspace/db generate",
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 51476e4..a9d9bb1 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -43,4 +43,4 @@
"./components/*": "./src/components/*.tsx",
"./hooks/*": "./src/hooks/*.ts"
}
-}
+}
\ No newline at end of file
diff --git a/packages/ui/src/components/skeleton.tsx b/packages/ui/src/components/skeleton.tsx
new file mode 100644
index 0000000..3b1cabd
--- /dev/null
+++ b/packages/ui/src/components/skeleton.tsx
@@ -0,0 +1,12 @@
+import { cn } from '@workspace/ui/lib/utils';
+
+function Skeleton({ className, ...props }: React.HTMLAttributes) {
+ return (
+
+ );
+}
+
+export { Skeleton };
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 51bf53a..72141d4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -26,6 +26,9 @@ importers:
apps/admin:
dependencies:
+ '@tailwindcss/postcss':
+ specifier: ^4.0.8
+ version: 4.1.11
'@tanstack/react-table':
specifier: ^8.21.3
version: 8.21.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -56,6 +59,9 @@ importers:
react-dom:
specifier: ^19.1.0
version: 19.1.0(react@19.1.0)
+ tailwindcss:
+ specifier: ^4.0.8
+ version: 4.1.11
zod:
specifier: ^3.25.67
version: 3.25.67
diff --git a/turbo.json b/turbo.json
index d6a7fe0..41e8594 100644
--- a/turbo.json
+++ b/turbo.json
@@ -16,6 +16,9 @@
"dev": {
"cache": false,
"persistent": true
+ },
+ "start": {
+ "persistent": true
}
}
}