prettier setup

This commit is contained in:
Om Lanke
2025-06-30 11:55:48 +05:30
parent 99220fb9a9
commit ba6ee585dc
63 changed files with 3950 additions and 2115 deletions

View File

@@ -1,2 +1,2 @@
import { handlers } from "@workspace/auth";
import { handlers } from '@workspace/auth';
export const { GET, POST } = handlers;

View File

@@ -1,121 +1,119 @@
import localFont from "next/font/local"
import localFont from 'next/font/local';
import "@workspace/ui/globals.css"
import { Providers } from "@/components/providers"
import '@workspace/ui/globals.css';
import { Providers } from '@/components/providers';
const firaSans = localFont({
src: [
{
path: "../fonts/Fira-sans/FiraSans-Thin.ttf",
weight: "100",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-Thin.ttf',
weight: '100',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-ThinItalic.ttf",
weight: "100",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-ThinItalic.ttf',
weight: '100',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-ExtraLight.ttf",
weight: "200",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-ExtraLight.ttf',
weight: '200',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-ExtraLightItalic.ttf",
weight: "200",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-ExtraLightItalic.ttf',
weight: '200',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-Light.ttf",
weight: "300",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-Light.ttf',
weight: '300',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-LightItalic.ttf",
weight: "300",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-LightItalic.ttf',
weight: '300',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-Regular.ttf",
weight: "400",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-Regular.ttf',
weight: '400',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-Italic.ttf",
weight: "400",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-Italic.ttf',
weight: '400',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-Medium.ttf",
weight: "500",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-Medium.ttf',
weight: '500',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-MediumItalic.ttf",
weight: "500",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-MediumItalic.ttf',
weight: '500',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-SemiBold.ttf",
weight: "600",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-SemiBold.ttf',
weight: '600',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-SemiBoldItalic.ttf",
weight: "600",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-SemiBoldItalic.ttf',
weight: '600',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-Bold.ttf",
weight: "700",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-Bold.ttf',
weight: '700',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-BoldItalic.ttf",
weight: "700",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-BoldItalic.ttf',
weight: '700',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-ExtraBold.ttf",
weight: "800",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-ExtraBold.ttf',
weight: '800',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-ExtraBoldItalic.ttf",
weight: "800",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-ExtraBoldItalic.ttf',
weight: '800',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-Black.ttf",
weight: "900",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-Black.ttf',
weight: '900',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-BlackItalic.ttf",
weight: "900",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-BlackItalic.ttf',
weight: '900',
style: 'italic',
},
],
variable: "--font-sans",
})
variable: '--font-sans',
});
const marcellus = localFont({
src: "../fonts/Marcellus/Marcellus-Regular.ttf",
variable: "--font-marcellus",
})
src: '../fonts/Marcellus/Marcellus-Regular.ttf',
variable: '--font-marcellus',
});
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
children: React.ReactNode;
}>) {
return (
<html lang="en" suppressHydrationWarning>
<body
className={`${firaSans.variable} ${marcellus.variable} font-sans antialiased `}
>
<body className={`${firaSans.variable} ${marcellus.variable} font-sans antialiased `}>
<Providers>{children}</Providers>
</body>
</html>
)
);
}

View File

@@ -1,21 +1,21 @@
import Login from "@/components/login";
import Studs from "@/components/studs";
import { db, admins } from "@workspace/db";
import { auth, signIn, signOut } from "@workspace/auth";
import Login from '@/components/login';
import Studs from '@/components/studs';
import { db, admins } from '@workspace/db';
import { auth, signIn, signOut } from '@workspace/auth';
async function getStudents() {
"use server";
'use server';
const s = await db.select().from(admins);
console.log(s);
}
async function logIn() {
"use server";
await signIn("google");
'use server';
await signIn('google');
}
async function logOut() {
"use server";
'use server';
await signOut();
}
@@ -24,9 +24,7 @@ export default async function Page() {
return (
<div className="flex items-center justify-center min-h-svh">
<div className="flex flex-col items-center justify-center gap-4">
<h1 className="text-2xl font-bold">
Hello admin {session?.user?.name}
</h1>
<h1 className="text-2xl font-bold">Hello admin {session?.user?.name}</h1>
{!session?.user && <Login action={logIn} />}
<Studs action={getStudents} logOut={logOut} />
</div>

View File

@@ -1,6 +1,6 @@
"use client";
import { signIn } from "@workspace/auth";
import { Button } from "@workspace/ui/components/button";
'use client';
import { signIn } from '@workspace/auth';
import { Button } from '@workspace/ui/components/button';
export default function Login({ action }: { action: () => Promise<void> }) {
return (

View File

@@ -1,16 +1,11 @@
"use client";
'use client';
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import * as React from 'react';
import { ThemeProvider as NextThemesProvider } from 'next-themes';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<NextThemesProvider
attribute="class"
defaultTheme="system"
enableSystem
enableColorScheme
>
<NextThemesProvider attribute="class" defaultTheme="system" enableSystem enableColorScheme>
{children}
</NextThemesProvider>
);

View File

@@ -1,5 +1,5 @@
import { Button } from "@workspace/ui/components/button";
import { auth } from "@workspace/auth";
import { Button } from '@workspace/ui/components/button';
import { auth } from '@workspace/auth';
export default async function Studs({
action,
@@ -12,7 +12,9 @@ export default async function Studs({
if (!session?.user) return null;
return (
<div className="flex gap-2">
<Button onClick={action} variant="outline">Click me</Button>
<Button onClick={action} variant="outline">
Click me
</Button>
<Button onClick={logOut}>Sign Out</Button>
</div>
);

View File

@@ -1,4 +1,4 @@
import { nextJsConfig } from "@workspace/eslint-config/next-js"
import { nextJsConfig } from '@workspace/eslint-config/next-js';
/** @type {import("eslint").Linter.Config} */
export default nextJsConfig
export default nextJsConfig;

View File

@@ -1,18 +1,18 @@
import { auth } from "@workspace/auth";
import { auth } from '@workspace/auth';
import { NextResponse } from 'next/server';
export default auth((req: any) => {
// If the user is unauthenticated or an admin, allow the request.
if (!req.auth || req.auth.user?.role === "ADMIN") {
if (!req.auth || req.auth.user?.role === 'ADMIN') {
return NextResponse.next();
}
// Otherwise, redirect to the student app.
const studentUrl = process.env.STUDENT_URL ?? "http://localhost:3000"
const studentUrl = process.env.STUDENT_URL ?? 'http://localhost:3000';
return NextResponse.redirect(new URL(studentUrl, req.url));
}) as any;
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
}
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

View File

@@ -1,19 +1,19 @@
import "next-auth";
import 'next-auth';
declare module "next-auth" {
declare module 'next-auth' {
interface Session {
user: {
role?: "ADMIN" | "USER";
role?: 'ADMIN' | 'USER';
adminId?: number;
studentId?: number;
[key: string]: any;
};
}
}
declare module "next-auth/jwt" {
declare module 'next-auth/jwt' {
interface JWT {
role?: "ADMIN" | "USER";
role?: 'ADMIN' | 'USER';
adminId?: number;
studentId?: number;
}
}
}

View File

@@ -1,6 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ["@workspace/ui", "@workspace/db", "@workspace/auth"],
transpilePackages: ['@workspace/ui', '@workspace/db', '@workspace/auth'],
};
export default nextConfig;

View File

@@ -12,7 +12,6 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@hookform/resolvers": "^5.1.1",
"@workspace/auth": "workspace:*",
"@workspace/db": "workspace:*",
"@workspace/ui": "workspace:*",
@@ -22,9 +21,7 @@
"next-auth": "^4.24.11",
"next-themes": "^0.4.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.58.1",
"zod": "^3.24.2"
"react-dom": "^19.0.0"
},
"devDependencies": {
"@types/node": "^20",

View File

@@ -1 +1 @@
export { default } from "@workspace/ui/postcss.config";
export { default } from '@workspace/ui/postcss.config';

View File

@@ -6,7 +6,7 @@
"@/*": ["./*"],
"@workspace/ui/*": ["../../packages/ui/src/*"],
"@workspace/db/*": ["../../packages/db/src/*"],
"@workspace/auth/*": ["../../packages/auth/src/*"],
"@workspace/auth/*": ["../../packages/auth/src/*"]
},
"plugins": [
{
@@ -14,12 +14,6 @@
}
]
},
"include": [
"next-env.d.ts",
"next.config.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"include": ["next-env.d.ts", "next.config.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@@ -1,2 +1,2 @@
import { handlers } from "@workspace/auth";
import { handlers } from '@workspace/auth';
export const { GET, POST } = handlers;

View File

@@ -1,121 +1,119 @@
import localFont from "next/font/local"
import localFont from 'next/font/local';
import "@workspace/ui/globals.css"
import { Providers } from "@/components/providers"
import '@workspace/ui/globals.css';
import { Providers } from '@/components/providers';
const firaSans = localFont({
src: [
{
path: "../fonts/Fira-sans/FiraSans-Thin.ttf",
weight: "100",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-Thin.ttf',
weight: '100',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-ThinItalic.ttf",
weight: "100",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-ThinItalic.ttf',
weight: '100',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-ExtraLight.ttf",
weight: "200",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-ExtraLight.ttf',
weight: '200',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-ExtraLightItalic.ttf",
weight: "200",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-ExtraLightItalic.ttf',
weight: '200',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-Light.ttf",
weight: "300",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-Light.ttf',
weight: '300',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-LightItalic.ttf",
weight: "300",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-LightItalic.ttf',
weight: '300',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-Regular.ttf",
weight: "400",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-Regular.ttf',
weight: '400',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-Italic.ttf",
weight: "400",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-Italic.ttf',
weight: '400',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-Medium.ttf",
weight: "500",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-Medium.ttf',
weight: '500',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-MediumItalic.ttf",
weight: "500",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-MediumItalic.ttf',
weight: '500',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-SemiBold.ttf",
weight: "600",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-SemiBold.ttf',
weight: '600',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-SemiBoldItalic.ttf",
weight: "600",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-SemiBoldItalic.ttf',
weight: '600',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-Bold.ttf",
weight: "700",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-Bold.ttf',
weight: '700',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-BoldItalic.ttf",
weight: "700",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-BoldItalic.ttf',
weight: '700',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-ExtraBold.ttf",
weight: "800",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-ExtraBold.ttf',
weight: '800',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-ExtraBoldItalic.ttf",
weight: "800",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-ExtraBoldItalic.ttf',
weight: '800',
style: 'italic',
},
{
path: "../fonts/Fira-sans/FiraSans-Black.ttf",
weight: "900",
style: "normal",
path: '../fonts/Fira-sans/FiraSans-Black.ttf',
weight: '900',
style: 'normal',
},
{
path: "../fonts/Fira-sans/FiraSans-BlackItalic.ttf",
weight: "900",
style: "italic",
path: '../fonts/Fira-sans/FiraSans-BlackItalic.ttf',
weight: '900',
style: 'italic',
},
],
variable: "--font-sans",
})
variable: '--font-sans',
});
const marcellus = localFont({
src: "../fonts/Marcellus/Marcellus-Regular.ttf",
variable: "--font-marcellus",
})
src: '../fonts/Marcellus/Marcellus-Regular.ttf',
variable: '--font-marcellus',
});
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
children: React.ReactNode;
}>) {
return (
<html lang="en" suppressHydrationWarning>
<body
className={`${firaSans.variable} ${marcellus.variable} font-sans antialiased `}
>
<body className={`${firaSans.variable} ${marcellus.variable} font-sans antialiased `}>
<Providers>{children}</Providers>
</body>
</html>
)
);
}

View File

@@ -1,26 +1,26 @@
import Login from "@/components/login";
import Studs from "@/components/studs";
import { db, admins } from "@workspace/db";
import { auth, signIn, signOut } from "@workspace/auth";
import Login from '@/components/login';
import Studs from '@/components/studs';
import { db, admins } from '@workspace/db';
import { auth, signIn, signOut } from '@workspace/auth';
async function getStudents() {
"use server";
'use server';
const s = await db.select().from(admins);
console.log(s);
}
async function logIn() {
"use server";
await signIn("google");
'use server';
await signIn('google');
}
async function logOut() {
"use server";
'use server';
await signOut();
}
export default async function Page() {
const session = await auth()
const session = await auth();
return (
<div className="flex items-center justify-center min-h-svh">
<div className="flex flex-col items-center justify-center gap-4">

View File

@@ -1,151 +1,176 @@
"use client"
'use client';
import { useState } from "react"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import { Button } from "@workspace/ui/components/button"
import { Input } from "@workspace/ui/components/input"
import { Textarea } from "@workspace/ui/components/textarea"
import { Progress } from "@workspace/ui/components/progress"
import { Separator } from "@workspace/ui/components/separator"
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormDescription } from "@workspace/ui/components/form"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@workspace/ui/components/select"
import { Card, CardContent, CardHeader, CardTitle } from "@workspace/ui/components/card"
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import { Button } from '@workspace/ui/components/button';
import { Input } from '@workspace/ui/components/input';
import { Textarea } from '@workspace/ui/components/textarea';
import { Progress } from '@workspace/ui/components/progress';
import { Separator } from '@workspace/ui/components/separator';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
FormDescription,
} from '@workspace/ui/components/form';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@workspace/ui/components/select';
import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/components/card';
// Form schema
const formSchema = z.object({
// Personal Details
firstName: z.string().min(1, "First name is required"),
lastName: z.string().min(1, "Last name is required"),
firstName: z.string().min(1, 'First name is required'),
lastName: z.string().min(1, 'Last name is required'),
fathersName: z.string().optional(),
mothersName: z.string().optional(),
email: z.string().email("Invalid email address"),
rollNumber: z.string().min(1, "Roll number is required"),
phoneNumber: z.string().min(10, "Phone number must be at least 10 digits"),
address: z.string().min(1, "Address is required"),
email: z.string().email('Invalid email address'),
rollNumber: z.string().min(1, 'Roll number is required'),
phoneNumber: z.string().min(10, 'Phone number must be at least 10 digits'),
address: z.string().min(1, 'Address is required'),
// Academic Details
degree: z.string().min(1, "Degree is required"),
year: z.string().min(1, "Year is required"),
branch: z.string().min(1, "Branch is required"),
ssc: z.string().min(1, "SSC percentage is required"),
hsc: z.string().min(1, "HSC percentage is required"),
degree: z.string().min(1, 'Degree is required'),
year: z.string().min(1, 'Year is required'),
branch: z.string().min(1, 'Branch is required'),
ssc: z.string().min(1, 'SSC percentage is required'),
hsc: z.string().min(1, 'HSC percentage is required'),
// Semester Grades
sem1: z.string().min(1, "Semester 1 grade is required"),
sem1KT: z.string().min(1, "Semester 1 KT status is required"),
sem2: z.string().min(1, "Semester 2 grade is required"),
sem2KT: z.string().min(1, "Semester 2 KT status is required"),
sem1: z.string().min(1, 'Semester 1 grade is required'),
sem1KT: z.string().min(1, 'Semester 1 KT status is required'),
sem2: z.string().min(1, 'Semester 2 grade is required'),
sem2KT: z.string().min(1, 'Semester 2 KT status is required'),
// Additional Details
linkedin: z.string().url("Invalid LinkedIn URL"),
github: z.string().url("Invalid GitHub URL"),
linkedin: z.string().url('Invalid LinkedIn URL'),
github: z.string().url('Invalid GitHub URL'),
skills: z.string().optional(),
})
});
type FormData = z.infer<typeof formSchema>
type FormData = z.infer<typeof formSchema>;
const steps = [
{
id: 1,
title: "Personal Details",
fields: ["firstName", "lastName", "fathersName", "mothersName", "email", "rollNumber", "phoneNumber", "address"],
title: 'Personal Details',
fields: [
'firstName',
'lastName',
'fathersName',
'mothersName',
'email',
'rollNumber',
'phoneNumber',
'address',
],
},
{ id: 2, title: "Academic Details", fields: ["degree", "year", "branch", "ssc", "hsc"] },
{ id: 3, title: "Semester Grades", fields: ["sem1", "sem1KT", "sem2", "sem2KT"] },
{ id: 4, title: "Additional Details", fields: ["linkedin", "github", "skills"] },
]
{ id: 2, title: 'Academic Details', fields: ['degree', 'year', 'branch', 'ssc', 'hsc'] },
{ id: 3, title: 'Semester Grades', fields: ['sem1', 'sem1KT', 'sem2', 'sem2KT'] },
{ id: 4, title: 'Additional Details', fields: ['linkedin', 'github', 'skills'] },
];
export default function StudentRegistrationForm() {
const [currentStep, setCurrentStep] = useState(1)
const [isSubmitting, setIsSubmitting] = useState(false)
const [currentStep, setCurrentStep] = useState(1);
const [isSubmitting, setIsSubmitting] = useState(false);
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues: {
firstName: "",
lastName: "",
fathersName: "",
mothersName: "",
email: "",
rollNumber: "",
phoneNumber: "",
address: "",
degree: "",
year: "",
branch: "",
ssc: "",
hsc: "",
sem1: "",
sem1KT: "",
sem2: "",
sem2KT: "",
linkedin: "",
github: "",
skills: "",
firstName: '',
lastName: '',
fathersName: '',
mothersName: '',
email: '',
rollNumber: '',
phoneNumber: '',
address: '',
degree: '',
year: '',
branch: '',
ssc: '',
hsc: '',
sem1: '',
sem1KT: '',
sem2: '',
sem2KT: '',
linkedin: '',
github: '',
skills: '',
},
})
});
const validateCurrentStep = async () => {
const currentStepData = steps.find((step) => step.id === currentStep)
if (!currentStepData) return false
const currentStepData = steps.find((step) => step.id === currentStep);
if (!currentStepData) return false;
const result = await form.trigger(currentStepData.fields as any)
return result
}
const result = await form.trigger(currentStepData.fields as any);
return result;
};
const nextStep = async () => {
const isValid = await validateCurrentStep()
const isValid = await validateCurrentStep();
if (isValid && currentStep < steps.length) {
setCurrentStep(currentStep + 1)
setCurrentStep(currentStep + 1);
}
}
};
const prevStep = () => {
if (currentStep > 1) {
setCurrentStep(currentStep - 1)
setCurrentStep(currentStep - 1);
}
}
};
const onSubmit = async (data: FormData) => {
setIsSubmitting(true)
setIsSubmitting(true);
try {
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 2000))
console.log("Form submitted:", data)
alert("Form submitted successfully!")
await new Promise((resolve) => setTimeout(resolve, 2000));
console.log('Form submitted:', data);
alert('Form submitted successfully!');
} catch (error) {
console.error("Submission error:", error)
alert("Submission failed. Please try again.")
console.error('Submission error:', error);
alert('Submission failed. Please try again.');
} finally {
setIsSubmitting(false)
setIsSubmitting(false);
}
}
};
const renderStep = () => {
switch (currentStep) {
case 1:
return <PersonalDetailsStep form={form} />
return <PersonalDetailsStep form={form} />;
case 2:
return <AcademicDetailsStep form={form} />
return <AcademicDetailsStep form={form} />;
case 3:
return <SemesterGradesStep form={form} />
return <SemesterGradesStep form={form} />;
case 4:
return <AdditionalDetailsStep form={form} />
return <AdditionalDetailsStep form={form} />;
default:
return null
return null;
}
}
};
const progress = (currentStep / steps.length) * 100
const progress = (currentStep / steps.length) * 100;
return (
<div className="min-h-screen py-8">
<div className="max-w-3xl mx-auto px-4">
<Card>
<CardHeader>
<CardTitle className="text-2xl font-bold text-center">Student Registration Form</CardTitle>
<CardTitle className="text-2xl font-bold text-center">
Student Registration Form
</CardTitle>
<div className="space-y-2">
<div className="flex justify-between text-sm text-muted-foreground">
<span>
@@ -162,13 +187,18 @@ export default function StudentRegistrationForm() {
{renderStep()}
<div className="flex justify-between pt-6">
<Button type="button" variant="outline" onClick={prevStep} disabled={currentStep === 1}>
<Button
type="button"
variant="outline"
onClick={prevStep}
disabled={currentStep === 1}
>
Previous
</Button>
{currentStep === steps.length ? (
<Button type="submit" disabled={isSubmitting}>
{isSubmitting ? "Submitting..." : "Submit"}
{isSubmitting ? 'Submitting...' : 'Submit'}
</Button>
) : (
<Button type="button" onClick={nextStep}>
@@ -182,7 +212,7 @@ export default function StudentRegistrationForm() {
</Card>
</div>
</div>
)
);
}
function PersonalDetailsStep({ form }: { form: any }) {
@@ -308,30 +338,30 @@ function PersonalDetailsStep({ form }: { form: any }) {
)}
/>
</div>
)
);
}
function AcademicDetailsStep({ form }: { form: any }) {
const degreeOptions = [
{ value: "btech", label: "B.Tech" },
{ value: "be", label: "B.E." },
{ value: "bsc", label: "B.Sc" },
{ value: "bca", label: "BCA" },
]
{ value: 'btech', label: 'B.Tech' },
{ value: 'be', label: 'B.E.' },
{ value: 'bsc', label: 'B.Sc' },
{ value: 'bca', label: 'BCA' },
];
const yearOptions = [
{ value: "2024", label: "2024" },
{ value: "2025", label: "2025" },
{ value: "2026", label: "2026" },
{ value: "2027", label: "2027" },
]
{ value: '2024', label: '2024' },
{ value: '2025', label: '2025' },
{ value: '2026', label: '2026' },
{ value: '2027', label: '2027' },
];
const branchOptions = [
{ value: "cse", label: "Computer Science" },
{ value: "it", label: "Information Technology" },
{ value: "ece", label: "Electronics & Communication" },
{ value: "mechanical", label: "Mechanical" },
]
{ value: 'cse', label: 'Computer Science' },
{ value: 'it', label: 'Information Technology' },
{ value: 'ece', label: 'Electronics & Communication' },
{ value: 'mechanical', label: 'Mechanical' },
];
return (
<div className="space-y-4">
@@ -443,16 +473,16 @@ function AcademicDetailsStep({ form }: { form: any }) {
/>
</div>
</div>
)
);
}
function SemesterGradesStep({ form }: { form: any }) {
const ktOptions = [
{ value: "0", label: "0 KT" },
{ value: "1", label: "1 KT" },
{ value: "2", label: "2 KT" },
{ value: "3+", label: "3+ KT" },
]
{ value: '0', label: '0 KT' },
{ value: '1', label: '1 KT' },
{ value: '2', label: '2 KT' },
{ value: '3+', label: '3+ KT' },
];
return (
<div className="space-y-4">
@@ -538,7 +568,7 @@ function SemesterGradesStep({ form }: { form: any }) {
/>
</div>
</div>
)
);
}
function AdditionalDetailsStep({ form }: { form: any }) {
@@ -594,5 +624,5 @@ function AdditionalDetailsStep({ form }: { form: any }) {
)}
/>
</div>
)
);
}

View File

@@ -1,24 +1,20 @@
"use client";
import { signIn } from "@workspace/auth";
import { Button } from "@workspace/ui/components/button";
'use client';
import { signIn } from '@workspace/auth';
import { Button } from '@workspace/ui/components/button';
export default function Login({logIn}: {logIn: () => Promise<void>}) {
export default function Login({ logIn }: { logIn: () => Promise<void> }) {
return (
<form action={logIn}>
<Button
type="submit"
variant="outline"
className="w-full h-12"
>
<div className="absolute inset-0 bg-gradient-to-r from-primary/0 via-primary/10 to-primary/0 translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-700 ease-out pointer-events-none" />
<img
src="https://static.cdnlogo.com/logos/g/35/google-icon.svg"
alt="Google logo"
className="w-5 h-5 transition-transform duration-200"
/>
<span className="relative z-10 font-medium transition-colors duration-200 group-hover:text-foreground">
Sign in with Google
</span>
<Button type="submit" variant="outline" className="w-full h-12">
<div className="absolute inset-0 bg-gradient-to-r from-primary/0 via-primary/10 to-primary/0 translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-700 ease-out pointer-events-none" />
<img
src="https://static.cdnlogo.com/logos/g/35/google-icon.svg"
alt="Google logo"
className="w-5 h-5 transition-transform duration-200"
/>
<span className="relative z-10 font-medium transition-colors duration-200 group-hover:text-foreground">
Sign in with Google
</span>
</Button>
</form>
);

View File

@@ -1,16 +1,11 @@
"use client";
'use client';
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import * as React from 'react';
import { ThemeProvider as NextThemesProvider } from 'next-themes';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<NextThemesProvider
attribute="class"
defaultTheme="system"
enableSystem
enableColorScheme
>
<NextThemesProvider attribute="class" defaultTheme="system" enableSystem enableColorScheme>
{children}
</NextThemesProvider>
);

View File

@@ -1,5 +1,5 @@
import { Button } from "@workspace/ui/components/button";
import { auth } from "@workspace/auth";
import { Button } from '@workspace/ui/components/button';
import { auth } from '@workspace/auth';
export default async function Studs({
action,
@@ -12,7 +12,9 @@ export default async function Studs({
if (!session?.user) return null;
return (
<div className="flex gap-2">
<Button onClick={action} variant="outline">Click me</Button>
<Button onClick={action} variant="outline">
Click me
</Button>
<Button onClick={logOut}>Sign Out</Button>
</div>
);

View File

@@ -1,4 +1,4 @@
import { nextJsConfig } from "@workspace/eslint-config/next-js"
import { nextJsConfig } from '@workspace/eslint-config/next-js';
/** @type {import("eslint").Linter.Config} */
export default nextJsConfig
export default nextJsConfig;

View File

@@ -1,18 +1,18 @@
import { auth } from "@workspace/auth";
import { auth } from '@workspace/auth';
import { NextResponse } from 'next/server';
export default auth((req: any) => {
// If the user is unauthenticated or a student, allow the request.
if (!req.auth || req.auth.user?.role === "USER") {
if (!req.auth || req.auth.user?.role === 'USER') {
return NextResponse.next();
}
// Otherwise, redirect to the admin app.
const adminURL = process.env.ADMIN_URL ?? "http://localhost:3001"
const adminURL = process.env.ADMIN_URL ?? 'http://localhost:3001';
return NextResponse.redirect(new URL(adminURL, req.url));
}) as any;
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
}
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

View File

@@ -1,9 +1,9 @@
import "next-auth";
import 'next-auth';
declare module "next-auth" {
declare module 'next-auth' {
interface Session {
user: {
role?: "ADMIN" | "USER";
role?: 'ADMIN' | 'USER';
adminId?: number;
studentId?: number;
[key: string]: any;
@@ -11,10 +11,10 @@ declare module "next-auth" {
}
}
declare module "next-auth/jwt" {
declare module 'next-auth/jwt' {
interface JWT {
role?: "ADMIN" | "USER";
role?: 'ADMIN' | 'USER';
adminId?: number;
studentId?: number;
}
}
}

View File

@@ -1,6 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ["@workspace/ui", "@workspace/auth", "@workspace/db"],
transpilePackages: ['@workspace/ui', '@workspace/auth', '@workspace/db'],
};
export default nextConfig;

View File

@@ -12,6 +12,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@hookform/resolvers": "^5.1.1",
"@workspace/ui": "workspace:*",
"@workspace/db": "workspace:*",
"@workspace/auth": "workspace:*",
@@ -20,7 +21,9 @@
"next-auth": "^4.24.11",
"next-themes": "^0.4.4",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"react-hook-form": "^7.58.1",
"zod": "^3.24.2"
},
"devDependencies": {
"@types/node": "^20",

View File

@@ -1 +1 @@
export { default } from "@workspace/ui/postcss.config";
export { default } from '@workspace/ui/postcss.config';

View File

@@ -14,12 +14,6 @@
}
]
},
"include": [
"next-env.d.ts",
"next.config.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"include": ["next-env.d.ts", "next.config.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}