From fbc276f2e7491a5c8c938a4cd0d1eb8e3bd86f4d Mon Sep 17 00:00:00 2001 From: Armaan Thakur Date: Tue, 4 Mar 2025 22:14:06 +0530 Subject: [PATCH] feat: Completed all 3 pages with API integration --- components.json | 21 ++ package-lock.json | 166 ++++++++++- package.json | 15 +- public/student-ppf.png | Bin 0 -> 9197 bytes public/university-logo.png | Bin 0 -> 18778 bytes public/user-logo.png | Bin 0 -> 8836 bytes src/app/(auth)/login/page.tsx | 71 ++++- .../(root)/(dashboard)/_components/header.tsx | 44 +++ .../(root)/(dashboard)/_components/index.ts | 1 - .../_components/instituteOverview.tsx | 87 ++++++ .../_components/studentsOverview.tsx | 135 +++++++++ .../_components/teacherOverview.tsx | 121 ++++++++ src/app/(root)/(dashboard)/page.tsx | 21 +- .../[batchId]/students/components/loading.tsx | 12 + .../students/components/studentsData.tsx | 69 +++++ .../batches/[batchId]/students/page.tsx | 178 +++++++++++ src/app/(root)/batches/components/batches.tsx | 282 ++++++++++++++++++ src/app/(root)/batches/components/filter.tsx | 75 +++++ src/app/(root)/batches/components/loading.tsx | 29 ++ src/app/(root)/batches/page.tsx | 73 +++++ .../teacher/[teacherId]/_components/index.ts | 1 - src/app/(root)/teacher/[teacherId]/page.tsx | 9 - .../api/batches/[batchId]/students/route.ts | 38 +++ src/app/api/batches/route.ts | 123 ++++++++ src/app/globals.css | 95 +++--- src/app/layout.tsx | 5 +- src/components/ui/button.tsx | 57 ++++ src/components/ui/card.tsx | 76 +++++ src/components/ui/input.tsx | 22 ++ src/components/ui/progress.tsx | 29 ++ src/lib/utils.ts | 6 + src/types/batches.ts | 24 ++ src/types/students.ts | 8 + tailwind.config.ts | 242 ++++++++------- 34 files changed, 1963 insertions(+), 172 deletions(-) create mode 100644 components.json create mode 100644 public/student-ppf.png create mode 100644 public/university-logo.png create mode 100644 public/user-logo.png create mode 100644 src/app/(root)/(dashboard)/_components/header.tsx delete mode 100644 src/app/(root)/(dashboard)/_components/index.ts create mode 100644 src/app/(root)/(dashboard)/_components/instituteOverview.tsx create mode 100644 src/app/(root)/(dashboard)/_components/studentsOverview.tsx create mode 100644 src/app/(root)/(dashboard)/_components/teacherOverview.tsx create mode 100644 src/app/(root)/batches/[batchId]/students/components/loading.tsx create mode 100644 src/app/(root)/batches/[batchId]/students/components/studentsData.tsx create mode 100644 src/app/(root)/batches/[batchId]/students/page.tsx create mode 100644 src/app/(root)/batches/components/batches.tsx create mode 100644 src/app/(root)/batches/components/filter.tsx create mode 100644 src/app/(root)/batches/components/loading.tsx create mode 100644 src/app/(root)/batches/page.tsx delete mode 100644 src/app/(root)/teacher/[teacherId]/_components/index.ts delete mode 100644 src/app/(root)/teacher/[teacherId]/page.tsx create mode 100644 src/app/api/batches/[batchId]/students/route.ts create mode 100644 src/app/api/batches/route.ts create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/card.tsx create mode 100644 src/components/ui/input.tsx create mode 100644 src/components/ui/progress.tsx create mode 100644 src/lib/utils.ts create mode 100644 src/types/batches.ts create mode 100644 src/types/students.ts diff --git a/components.json b/components.json new file mode 100644 index 0000000..0e8b633 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e801e52..6fac074 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,10 +8,17 @@ "name": "leadlly.admin.web.next", "version": "0.1.0", "dependencies": { + "@next/font": "^14.2.15", + "@radix-ui/react-progress": "^1.1.2", + "@radix-ui/react-slot": "^1.1.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.477.0", "next": "15.0.3", "react": "19.0.0-rc-66855b96-20241106", "react-dom": "19.0.0-rc-66855b96-20241106", - "tailwind-merge": "^2.3.0", + "react-icons": "^5.5.0", + "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7" }, "devDependencies": { @@ -477,6 +484,15 @@ "integrity": "sha512-t9Xy32pjNOvVn2AS+Utt6VmyrshbpfUMhIjFO60gI58deSo/KgLOp31XZ4O+kY/Is8WAGYwA5gR7kOb1eORDBA==", "license": "MIT" }, + "node_modules/@next/font": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/font/-/font-14.2.15.tgz", + "integrity": "sha512-QopYhBmCDDrNDynbi+ZD1hDZXmQXVFo7TmAFp4DQgO/kogz1OLbQ92hPigJbj572eZ3GaaVxNIyYVn3/eAsehg==", + "license": "MIT", + "peerDependencies": { + "next": "*" + } + }, "node_modules/@next/swc-darwin-arm64": { "version": "15.0.3", "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.0.3.tgz", @@ -650,6 +666,101 @@ "node": ">=14" } }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.2.tgz", + "integrity": "sha512-u1IgJFQ4zNAUTjGdDL5dcl/U8ntOR6jsnhxKb5RKp5Ozwl88xKR9EqRZOe/Mk8tnx0x5tNUe2F+MzsyjqMg0MA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -679,14 +790,14 @@ "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.12", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -697,7 +808,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/react": "*" @@ -867,12 +978,33 @@ "node": ">= 6" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -955,7 +1087,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/detect-libc": { @@ -1248,6 +1380,15 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/lucide-react": { + "version": "0.477.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.477.0.tgz", + "integrity": "sha512-yCf7aYxerFZAbd8jHJxjwe1j7jEMPptjnaOqdYeirFnEy85cNR3/L+o0I875CYFYya+eEVzZSbNuRk8BZPDpVw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -1701,6 +1842,15 @@ "react": "19.0.0-rc-66855b96-20241106" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -2045,9 +2195,9 @@ } }, "node_modules/tailwind-merge": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.4.tgz", - "integrity": "sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", "license": "MIT", "funding": { "type": "github", diff --git a/package.json b/package.json index b2efd97..8de3b1a 100644 --- a/package.json +++ b/package.json @@ -9,18 +9,25 @@ "lint": "next lint" }, "dependencies": { + "@next/font": "^14.2.15", + "@radix-ui/react-progress": "^1.1.2", + "@radix-ui/react-slot": "^1.1.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.477.0", + "next": "15.0.3", "react": "19.0.0-rc-66855b96-20241106", "react-dom": "19.0.0-rc-66855b96-20241106", - "next": "15.0.3", - "tailwind-merge": "^2.3.0", + "react-icons": "^5.5.0", + "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7" }, "devDependencies": { - "typescript": "^5", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "postcss": "^8", - "tailwindcss": "^3.4.1" + "tailwindcss": "^3.4.1", + "typescript": "^5" } } diff --git a/public/student-ppf.png b/public/student-ppf.png new file mode 100644 index 0000000000000000000000000000000000000000..7394f76ac1d60ab17cb7bb5958cf039176d0034a GIT binary patch literal 9197 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 { return ( -
Login
- ) -} +
+ ); +}; + +export default Login; diff --git a/src/app/(root)/(dashboard)/_components/header.tsx b/src/app/(root)/(dashboard)/_components/header.tsx new file mode 100644 index 0000000..c2d368e --- /dev/null +++ b/src/app/(root)/(dashboard)/_components/header.tsx @@ -0,0 +1,44 @@ +import { Button } from "@/components/ui/button"; +import React from "react"; + +const Header = () => { + return ( +
+

+ Institute Overview & Management +

+ +
+ ); +}; + +export default Header; diff --git a/src/app/(root)/(dashboard)/_components/index.ts b/src/app/(root)/(dashboard)/_components/index.ts deleted file mode 100644 index 28d4d61..0000000 --- a/src/app/(root)/(dashboard)/_components/index.ts +++ /dev/null @@ -1 +0,0 @@ -// here we can add all the components that we want to use in the dashboard (you can rename this file to whatever you want) \ No newline at end of file diff --git a/src/app/(root)/(dashboard)/_components/instituteOverview.tsx b/src/app/(root)/(dashboard)/_components/instituteOverview.tsx new file mode 100644 index 0000000..a4535d0 --- /dev/null +++ b/src/app/(root)/(dashboard)/_components/instituteOverview.tsx @@ -0,0 +1,87 @@ +import React from "react"; +import Image from "next/image"; + +const InstituteOverview = () => { + return ( +
+
+
+ Institute Logo +
+ +
+

Established in 2001

+

+ Chaitanya Bharathi Institute +

+

+ Institute Code:{" "} + 21XYZ1234 +

+
+ +
+
+ + + +
+
Address:
+ + 123, Main Street, City, Country + +
+
+ +
+ + + +
+
Contact:
+ +1234567890 +
+
+ +
+ + + + +
+
Email:
+ info@institute.com +
+
+
+
+
+ ); +}; + +export default InstituteOverview; diff --git a/src/app/(root)/(dashboard)/_components/studentsOverview.tsx b/src/app/(root)/(dashboard)/_components/studentsOverview.tsx new file mode 100644 index 0000000..cb71b42 --- /dev/null +++ b/src/app/(root)/(dashboard)/_components/studentsOverview.tsx @@ -0,0 +1,135 @@ +import React from "react"; +import Link from "next/link"; +import Image from "next/image"; + +interface StudentsOverviewProps { + totalStudents: number; + activeCourses: number; + averageAttendance: number; + performanceIndex: number; +} + +const StudentsOverview = () => { + return ( +
+

Students Overview

+ +
+
+

Total Students

+
+ + + + + 2284 +
+
+ +
+

Active Courses

+
+ + + + + 84 +
+
+ +
+

Average Attendance

+
+ + + + + + + 98% +
+
+ +
+

Performance Index

+
+ + + + + 9.0/10 +
+
+
+ + + 📚 + View Students + +
+ ); +}; + +export default StudentsOverview; diff --git a/src/app/(root)/(dashboard)/_components/teacherOverview.tsx b/src/app/(root)/(dashboard)/_components/teacherOverview.tsx new file mode 100644 index 0000000..0258469 --- /dev/null +++ b/src/app/(root)/(dashboard)/_components/teacherOverview.tsx @@ -0,0 +1,121 @@ +import React from "react"; +import Link from "next/link"; + +interface TeachersOverviewProps { + totalTeachers: number; + departments: number; + activeClasses: number; + satisfactionRate: number; +} + +const TeachersOverview = () => { + return ( +
+

Teachers Overview

+ +
+
+

Total Teachers

+
+ + + + + 24 +
+
+ +
+

Departments

+
+ + + + + 14 +
+
+ +
+

Active Classes

+
+ + + + + 98 +
+
+ +
+

Satisfaction Rate

+
+ + + + + 9.0/10 +
+
+
+ + + 🎓 + View Teachers + +
+ ); +}; + +export default TeachersOverview; diff --git a/src/app/(root)/(dashboard)/page.tsx b/src/app/(root)/(dashboard)/page.tsx index 75a3155..93f0fe3 100644 --- a/src/app/(root)/(dashboard)/page.tsx +++ b/src/app/(root)/(dashboard)/page.tsx @@ -1,7 +1,22 @@ -import Image from "next/image"; +import InstituteOverview from "./_components/instituteOverview"; +import StudentsOverview from "./_components/studentsOverview"; +import TeachersOverview from "./_components/teacherOverview"; +import Header from "./_components/header"; -export default function Home() { +export default function DashboardPage() { return ( - <>Home +
+ {/* Header Section */} +
+ + {/* Institute Overview */} + + + {/* Student & Teacher Overview Section */} +
+ + +
+
); } diff --git a/src/app/(root)/batches/[batchId]/students/components/loading.tsx b/src/app/(root)/batches/[batchId]/students/components/loading.tsx new file mode 100644 index 0000000..ad42c87 --- /dev/null +++ b/src/app/(root)/batches/[batchId]/students/components/loading.tsx @@ -0,0 +1,12 @@ +import React from 'react' + +const Loading = () => { + return ( +
+

Fetching students...

+
+
+ ) +} + +export default Loading diff --git a/src/app/(root)/batches/[batchId]/students/components/studentsData.tsx b/src/app/(root)/batches/[batchId]/students/components/studentsData.tsx new file mode 100644 index 0000000..cb82790 --- /dev/null +++ b/src/app/(root)/batches/[batchId]/students/components/studentsData.tsx @@ -0,0 +1,69 @@ +import { Card, CardContent } from '@/components/ui/card'; +import { Progress } from '@/components/ui/progress'; +import { Student } from '@/types/students'; +import Image from 'next/image'; +import React from 'react' + +const StudentsData = ({studentsData, filter} : {studentsData:Student[], filter: string}) => { + return ( +
+ {studentsData + .filter((student) => filter === "all" || student.status === filter) + .map((student, index) => { + const statusColors: Record = { + excellent: "bg-[#00FF132E]", + optimal: "bg-[#FFF2D5]", + inefficient: "bg-[#FFDBDA]", + }; + + const progressColors: Record = { + excellent: "bg-green-500", + optimal: "bg-orange-500", + inefficient: "bg-red-500", + }; + + return ( + + +
+ profile + + 😜 + +
+
+

{student.name}

+

+ Class: {student.class} +

+
+ Level: + +
+
+
+
+ ); + })} +
+ ) +} + +export default StudentsData diff --git a/src/app/(root)/batches/[batchId]/students/page.tsx b/src/app/(root)/batches/[batchId]/students/page.tsx new file mode 100644 index 0000000..6c8eecc --- /dev/null +++ b/src/app/(root)/batches/[batchId]/students/page.tsx @@ -0,0 +1,178 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { options, Student } from "@/types/students"; +import Image from "next/image"; +import { useParams } from "next/navigation"; +import { useEffect, useState } from "react"; +import StudentsData from "./components/studentsData"; +import Loading from "./components/loading"; + +export default function StudentsInfo() { + const [filter, setFilter] = useState("all"); + const [studentsData, setStudentsData] = useState([]); + const [loading, setLoading] = useState(true); + const params = useParams(); + + const batchId = params?.batchId as string; + + const fetchStudents = async (batchId: string) => { + try { + const response = await fetch(`/api/batches/${batchId}/students`); + if (!response.ok) { + throw new Error("Failed to fetch students"); + } + const data = await response.json(); + return data.students; + } catch (error) { + console.error("Error fetching students:", error); + return []; + } + }; + + useEffect(() => { + if (!batchId) return; + + const getStudents = async () => { + setLoading(true); + const data = await fetchStudents(batchId); + setStudentsData(data); + setLoading(false); + }; + + getStudents(); + }, [batchId]); + + return ( +
+
+

Students Info

+
+
+
+ + + +
+ +
+ + + +
+
+
+ + + + +
+
+ user +
+
+
+ +
+
+
+ + + + +
+
+ +
+ {options.map((option) => ( + + ))} + + + +
+
+ + {loading ? ( + + ) : ( + + )} +
+ ); +} diff --git a/src/app/(root)/batches/components/batches.tsx b/src/app/(root)/batches/components/batches.tsx new file mode 100644 index 0000000..7527922 --- /dev/null +++ b/src/app/(root)/batches/components/batches.tsx @@ -0,0 +1,282 @@ +import { BatchesData } from "@/types/batches"; +import Link from "next/link"; +import React from "react"; + +interface BatchesProps { + batchesData: BatchesData; +} + +const Batches: React.FC = ({ batchesData }) => { + return ( +
+ {batchesData.standards.map((standard, index) => ( +
+
+

{standard.name}

+ +
+ + {!standard?.batches || standard.batches.length === 0 ? ( + // Loading spinner when data is unavailable +
+ + + + +
+ ) : ( +
+ {standard.batches.map((batch) => ( +
+
+
+ {batch.name === "Omega" ? ( + + + + + + + + + + + + + + ) : batch.name === "Sigma" ? ( + + + + + ) : ( + + + + + + + + )} +
+
+

+ {batch.name} +

+

{batch.standard}

+
+ + Active + +
+ +
+

+ + Subject:{" "} + + {batch.subjects.join(", ")} +

+

+ + Total Students:{" "} + + {batch.totalStudents} +

+
+ +
+

+ {batch.totalStudents}/{batch.maxStudents} +

+
+
+
+
+ +
+
+

-By {batch.teacher}

+
+ + + View More + +
+
+ ))} +
+ )} +
+ ))} +
+ ); +}; + +export default Batches; diff --git a/src/app/(root)/batches/components/filter.tsx b/src/app/(root)/batches/components/filter.tsx new file mode 100644 index 0000000..e7d9e43 --- /dev/null +++ b/src/app/(root)/batches/components/filter.tsx @@ -0,0 +1,75 @@ +import { BatchFilters } from '@/types/batches' +import React from 'react' + +type FilterBatchesProps = { + filters: BatchFilters; + handleFilterChange: (e: React.ChangeEvent) => void; + }; + +const FilterBatches = ({ filters, handleFilterChange }: FilterBatchesProps) => { + return ( +
+
+
+ + + + + Filter by : + +
+ +
+ + + + + +
+
+
+ ) +} + +export default FilterBatches diff --git a/src/app/(root)/batches/components/loading.tsx b/src/app/(root)/batches/components/loading.tsx new file mode 100644 index 0000000..bacc86f --- /dev/null +++ b/src/app/(root)/batches/components/loading.tsx @@ -0,0 +1,29 @@ +import React from 'react' + +const Loading = () => { + return ( +
+ + + + +
+ ) +} + +export default Loading diff --git a/src/app/(root)/batches/page.tsx b/src/app/(root)/batches/page.tsx new file mode 100644 index 0000000..d77e3a3 --- /dev/null +++ b/src/app/(root)/batches/page.tsx @@ -0,0 +1,73 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import FilterBatches from "./components/filter"; +import Loading from "./components/loading"; +import Batches from "./components/batches"; +import { BatchesData, BatchFilters } from "@/types/batches"; + +export default function BatchesPage() { + const [batchesData, setBatchesData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [filters, setFilters] = useState({ + standard: "", + subject: "", + teacher: "", + }); + + const buildQueryParams = () => { + const queryParams = new URLSearchParams(); + Object.entries(filters).forEach(([key, value]) => { + if (value) queryParams.append(key, value); + }); + return queryParams.toString(); + }; + + // Fetch batches data + const fetchBatches = async () => { + try { + setLoading(true); + const response = await fetch(`/api/batches?${buildQueryParams()}`); + + if (!response.ok) throw new Error("Failed to fetch batches"); + + const data = await response.json(); + setBatchesData(data); + setError(null); + } catch (err) { + setError("Error loading batches. Please try again later."); + console.error("Error fetching batches:", err); + } finally { + setTimeout(() => setLoading(false), 200); + } + }; + + useEffect(() => { + fetchBatches(); + }, [filters]); + + const handleFilterChange = (e: React.ChangeEvent) => { + setFilters((prev) => ({ ...prev, [e.target.name]: e.target.value })); + }; + + return ( +
+

Student Batches of Institute

+ + + {loading && } + {error &&

{error}

} + {batchesData && !loading && batchesData.standards.length > 0 ? ( + + ) : ( + !loading && + !error && ( +
+

No batches found. Try adjusting your filters.

+
+ ) + )} +
+ ); +} diff --git a/src/app/(root)/teacher/[teacherId]/_components/index.ts b/src/app/(root)/teacher/[teacherId]/_components/index.ts deleted file mode 100644 index 28d4d61..0000000 --- a/src/app/(root)/teacher/[teacherId]/_components/index.ts +++ /dev/null @@ -1 +0,0 @@ -// here we can add all the components that we want to use in the dashboard (you can rename this file to whatever you want) \ No newline at end of file diff --git a/src/app/(root)/teacher/[teacherId]/page.tsx b/src/app/(root)/teacher/[teacherId]/page.tsx deleted file mode 100644 index 3017769..0000000 --- a/src/app/(root)/teacher/[teacherId]/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' - -const TeacherPage = () => { - return ( -
TeacherPage
- ) -} - -export default TeacherPage \ No newline at end of file diff --git a/src/app/api/batches/[batchId]/students/route.ts b/src/app/api/batches/[batchId]/students/route.ts new file mode 100644 index 0000000..2e0feb2 --- /dev/null +++ b/src/app/api/batches/[batchId]/students/route.ts @@ -0,0 +1,38 @@ +import { NextRequest, NextResponse } from 'next/server'; + +// Mock data for students +const getStudentsData = (batchId: string) => { + return { + students: [ + { name: "Abhinav Mishra", class: 11, level: 80, status: "excellent" }, + { name: "Abhinav Mishra", class: 11, level: 50, status: "optimal" }, + { name: "Abhinav Mishra", class: 11, level: 20, status: "inefficient" }, + { name: "Abhinav Mishra", class: 11, level: 80, status: "excellent" }, + { name: "Abhinav Mishra", class: 11, level: 30, status: "inefficient" }, + { name: "Abhinav Mishra", class: 11, level: 50, status: "optimal" }, + { name: "Abhinav Mishra", class: 11, level: 40, status: "optimal" }, + { name: "Abhinav Mishra", class: 11, level: 90, status: "excellent" }, + { name: "Abhinav Mishra", class: 11, level: 30, status: "inefficient" }, + { name: "Abhinav Mishra", class: 11, level: 20, status: "inefficient" }, + { name: "Abhinav Mishra", class: 11, level: 40, status: "optimal" }, + { name: "Abhinav Mishra", class: 11, level: 90, status: "excellent" }, + ] + }; +}; + +export async function GET( + request: NextRequest, + { params }: { params: { batchId: string } } +) { + const batchId = params.batchId; + if (!batchId) { + return NextResponse.json({ error: 'Batch ID is required' }, { status: 400 }); + } + + try { + const data = getStudentsData(batchId); + return NextResponse.json(data, { status: 200 }); + } catch (error) { + return NextResponse.json({ error: 'Failed to fetch students' }, { status: 500 }); + } +} diff --git a/src/app/api/batches/route.ts b/src/app/api/batches/route.ts new file mode 100644 index 0000000..621b4fe --- /dev/null +++ b/src/app/api/batches/route.ts @@ -0,0 +1,123 @@ +import { NextRequest, NextResponse } from 'next/server'; + +// Mock data for batches - in a real app, this would come from a database +const batchesData = { + standards: [ + { + name: "11th standard", + batches: [ + { + id: "11-omega-1", + name: "Omega", + standard: "11th Class", + subjects: ["Chemistry", "Physics", "Biology"], + totalStudents: 120, + maxStudents: 180, + teacher: "Dr. Sarah Wilson" + }, + { + id: "11-sigma-1", + name: "Sigma", + standard: "11th Class", + subjects: ["Mathematics", "Chemistry", "Physics"], + totalStudents: 120, + maxStudents: 180, + teacher: "Dr. Sarah Wilson" + }, + { + id: "11-omega-2", + name: "Omega2", + standard: "11th Class", + subjects: ["Physics"], + totalStudents: 120, + maxStudents: 180, + teacher: "Dr. Sarah Wilson" + } + ] + }, + { + name: "12th standard", + batches: [ + { + id: "12-omega-1", + name: "Omega", + standard: "11th Class", + subjects: ["Chemistry"], + totalStudents: 120, + maxStudents: 180, + teacher: "Dr. Sarah Wilson" + }, + { + id: "12-sigma-1", + name: "Sigma", + standard: "11th Class", + subjects: ["Mathematics"], + totalStudents: 120, + maxStudents: 180, + teacher: "Dr. Sarah Wilson" + }, + { + id: "12-omega-2", + name: "Omega2", + standard: "11th Class", + subjects: ["Physics"], + totalStudents: 120, + maxStudents: 180, + teacher: "Dr. Sarah Wilson" + } + ] + } + ] +}; + +export async function GET(request: NextRequest) { + try { + // Get query parameters for filtering + const searchParams = request.nextUrl.searchParams; + const standard = searchParams.get('standard'); + const subject = searchParams.get('subject'); + const teacher = searchParams.get('teacher'); + + // Filter the data based on query parameters + let filteredData = { ...batchesData }; + + if (standard) { + filteredData.standards = filteredData.standards.filter( + std => std.name.toLowerCase().includes(standard.toLowerCase()) + ); + } + + // Filter batches within each standard + filteredData.standards = filteredData.standards.map(std => { + let filteredBatches = [...std.batches]; + + if (subject) { + filteredBatches = filteredBatches.filter(batch => + batch.subjects.some(s => s.toLowerCase().includes(subject.toLowerCase())) + ); + } + + if (teacher) { + filteredBatches = filteredBatches.filter(batch => + batch.teacher.toLowerCase().includes(teacher.toLowerCase()) + ); + } + + return { + ...std, + batches: filteredBatches + }; + }); + + // Remove standards with no batches after filtering + filteredData.standards = filteredData.standards.filter(std => std.batches.length > 0); + + return NextResponse.json(filteredData, { status: 200 }); + } catch (error) { + console.error('Error fetching batches:', error); + return NextResponse.json( + { error: 'Failed to fetch batches' }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index 9817d51..ae5f5d0 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -5,66 +5,81 @@ @layer base { :root { --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; + --foreground: 0 0% 3.9%; --card: 0 0% 100%; - --card-foreground: 222.2 84% 4.9%; + --card-foreground: 0 0% 3.9%; --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; + --popover-foreground: 0 0% 3.9%; - --primary: 265 88% 64%; - --primary-foreground: 210 40% 98%; + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; --sidebar-background: 262, 86%, 46%, 0.03; - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; + --destructive-foreground: 0 0% 98%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 222.2 84% 4.9%; + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; --radius: 0.5rem; + + --chart-1: 12 76% 61%; + + --chart-2: 173 58% 39%; + + --chart-3: 197 37% 24%; + + --chart-4: 43 74% 66%; + + --chart-5: 27 87% 67%; } .dark { - --background: 222.2 84% 4.9%; - --foreground: 210 40% 98%; + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; - --card: 222.2 84% 4.9%; - --card-foreground: 210 40% 98%; + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; - --popover: 222.2 84% 4.9%; - --popover-foreground: 210 40% 98%; + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; - --primary: 210 40% 98%; - --primary-foreground: 222.2 47.4% 11.2%; + --primary: 0 0% 98%; + --primary-foreground: 0 0% 9%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 210 40% 98%; + --secondary: 0 0% 14.9%; + --secondary-foreground: 0 0% 98%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 210 40% 98%; + --accent: 0 0% 14.9%; + --accent-foreground: 0 0% 98%; --destructive: 0 62.8% 30.6%; - --destructive-foreground: 210 40% 98%; - - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 212.7 26.8% 83.9%; + --destructive-foreground: 0 0% 98%; + + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; } } @@ -109,3 +124,15 @@ scrollbar-width: none; /* Firefox */ } } + +.flex-center{ + display: flex; + justify-content: center; + align-items: center; +} + +.flex-between{ + display: flex; + justify-content: space-between; + align-items: center; +} \ No newline at end of file diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d178a3f..63a5ec6 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,11 +1,13 @@ import type { Metadata } from "next"; import localFont from "next/font/local"; import "./globals.css"; +import { Mada } from "next/font/google"; export const metadata: Metadata = { title: "Leadlly | Admin", description: "Admin dashboard by leadlly", }; +const mada = Mada({ subsets: ["latin"], weight: ["400", "700"] }); // Load Mada font export default function RootLayout({ children, @@ -14,8 +16,7 @@ export default function RootLayout({ }>) { return ( - + {children} diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..65d4fcd --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,57 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", + outline: + "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-10 rounded-md px-8", + icon: "h-9 w-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx new file mode 100644 index 0000000..cabfbfc --- /dev/null +++ b/src/components/ui/card.tsx @@ -0,0 +1,76 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx new file mode 100644 index 0000000..69b64fb --- /dev/null +++ b/src/components/ui/input.tsx @@ -0,0 +1,22 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Input = React.forwardRef>( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/src/components/ui/progress.tsx b/src/components/ui/progress.tsx new file mode 100644 index 0000000..eb92507 --- /dev/null +++ b/src/components/ui/progress.tsx @@ -0,0 +1,29 @@ +"use client" + +import * as React from "react" +import * as ProgressPrimitive from "@radix-ui/react-progress" + +import { cn } from "@/lib/utils" + +const Progress = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { indicatorColor?: string } +>(({ className, value, indicatorColor = "bg-primary", ...props }, ref) => ( + + + +)) + +Progress.displayName = ProgressPrimitive.Root.displayName + +export { Progress } diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/src/types/batches.ts b/src/types/batches.ts new file mode 100644 index 0000000..20e670e --- /dev/null +++ b/src/types/batches.ts @@ -0,0 +1,24 @@ +export interface Batch { + id: string; + name: string; + standard: string; + subjects: string[]; + totalStudents: number; + maxStudents: number; + teacher: string; +} + +export interface Standard { + name: string; + batches: Batch[]; +} + +export interface BatchesData { + standards: Standard[]; +} + +export type BatchFilters = { + standard: string; + subject: string; + teacher: string; +}; diff --git a/src/types/students.ts b/src/types/students.ts new file mode 100644 index 0000000..b2db81e --- /dev/null +++ b/src/types/students.ts @@ -0,0 +1,8 @@ +export interface Student { + name: string; + class: number; + level: number; + status: "excellent" | "optimal" | "inefficient"; +} + +export const options = ["all", "excellent", "optimal", "inefficient"]; \ No newline at end of file diff --git a/tailwind.config.ts b/tailwind.config.ts index 2873606..3cb74f6 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -11,111 +11,143 @@ const config = { ], prefix: '', theme: { - container: { - center: true, - padding: '2rem', - screens: { - '2xl': '1400px', - }, - }, - extend: { - screens: { - '2xl': '1400px', - }, - fontFamily: { - sans: ['var(--font-sans)', ...fontFamily.sans], - }, - colors: { - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - 'sidebar-background': 'hsla(262, 86%, 46%, 0.03)', - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))', - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))', - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))', - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))', - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))', - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))', - }, - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))', - }, - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)', - }, - keyframes: { - 'accordion-down': { - from: { height: '0' }, - to: { height: 'var(--radix-accordion-content-height)' }, - }, - 'accordion-up': { - from: { height: 'var(--radix-accordion-content-height)' }, - to: { height: '0' }, - }, - 'collapsible-down': { - from: { height: '0' }, - to: { height: 'var(--radix-collapsible-content-height)' }, - }, - 'collapsible-up': { - from: { height: 'var(--radix-collapsible-content-height)' }, - to: { height: '0' }, - }, - 'caret-blink': { - '0%,70%,100%': { opacity: '1' }, - '20%,50%': { opacity: '0' }, - }, - }, - animation: { - 'accordion-down': 'accordion-down 0.2s ease-out', - 'accordion-up': 'accordion-up 0.2s ease-out', - 'collapsible-down': 'collapsible-down 0.2s ease-out', - 'collapsible-up': 'collapsible-up 0.2s ease-out', - 'caret-blink': 'caret-blink 1.25s ease-out infinite', - }, - fontSize: { - 'page-title': ['40px', '52px'], - }, - width: { - sidebar: '261px', - }, - height: { - 'main-height': 'calc(100dvh - 24px)', - }, - boxShadow: { - custom: '0px 17px 37px 0px rgba(165, 92, 255, 0.0)', - dialog: '0 0 21.5px 2px rgba(0, 0, 0, 0.29)', - tracker_subject_overview: - '2px 1px 10.5px 0 rgba(151, 83, 245, 0.18), inset 0 0 32px -7px rgba(151, 83, 245, 0.18)', - question: ' 0px 0px 26.7px -10px #00000033', - 'custom-back': 'inset 1.68px 3.37px 5.31px 0 rgba(0, 0, 0, 0.1)', - 'section':' 0px 1px 24.8px -9px #00000038', - 'card':'0px 1.15px 21.31px 0px #FF990012' - }, - }, - }, + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px' + } + }, + extend: { + screens: { + '2xl': '1400px' + }, + fontFamily: { + sans: [ + 'var(--font-sans)', + ...fontFamily.sans + ] + }, + colors: { + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + 'sidebar-background': 'hsla(262, 86%, 46%, 0.03)', + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))' + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))' + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))' + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))' + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))' + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))' + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))' + }, + chart: { + '1': 'hsl(var(--chart-1))', + '2': 'hsl(var(--chart-2))', + '3': 'hsl(var(--chart-3))', + '4': 'hsl(var(--chart-4))', + '5': 'hsl(var(--chart-5))' + } + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)' + }, + keyframes: { + 'accordion-down': { + from: { + height: '0' + }, + to: { + height: 'var(--radix-accordion-content-height)' + } + }, + 'accordion-up': { + from: { + height: 'var(--radix-accordion-content-height)' + }, + to: { + height: '0' + } + }, + 'collapsible-down': { + from: { + height: '0' + }, + to: { + height: 'var(--radix-collapsible-content-height)' + } + }, + 'collapsible-up': { + from: { + height: 'var(--radix-collapsible-content-height)' + }, + to: { + height: '0' + } + }, + 'caret-blink': { + '0%,70%,100%': { + opacity: '1' + }, + '20%,50%': { + opacity: '0' + } + } + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', + 'collapsible-down': 'collapsible-down 0.2s ease-out', + 'collapsible-up': 'collapsible-up 0.2s ease-out', + 'caret-blink': 'caret-blink 1.25s ease-out infinite' + }, + fontSize: { + 'page-title': [ + '40px', + '52px' + ] + }, + width: { + sidebar: '261px' + }, + height: { + 'main-height': 'calc(100dvh - 24px)' + }, + boxShadow: { + custom: '0px 17px 37px 0px rgba(165, 92, 255, 0.0)', + dialog: '0 0 21.5px 2px rgba(0, 0, 0, 0.29)', + tracker_subject_overview: '2px 1px 10.5px 0 rgba(151, 83, 245, 0.18), inset 0 0 32px -7px rgba(151, 83, 245, 0.18)', + question: ' 0px 0px 26.7px -10px #00000033', + 'custom-back': 'inset 1.68px 3.37px 5.31px 0 rgba(0, 0, 0, 0.1)', + section: ' 0px 1px 24.8px -9px #00000038', + card: '0px 1.15px 21.31px 0px #FF990012' + } + } + }, plugins: [require('tailwindcss-animate')], } satisfies Config;