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 0000000..7394f76 Binary files /dev/null and b/public/student-ppf.png differ diff --git a/public/university-logo.png b/public/university-logo.png new file mode 100644 index 0000000..71556e2 Binary files /dev/null and b/public/university-logo.png differ diff --git a/public/user-logo.png b/public/user-logo.png new file mode 100644 index 0000000..3ebc317 Binary files /dev/null and b/public/user-logo.png differ diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index 45e3e9d..c38d225 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -1,9 +1,70 @@ -import React from 'react' +import Link from "next/link"; +import React from "react"; +import { FaGoogle } from "react-icons/fa"; const Login = () => { return ( -
Login
- ) -} +
+
+

+ Welcome Back +

-export default Login \ No newline at end of file + {/* Email & Password Form */} +
+
+ + +
+
+ + +
+ + + +
+ + {/* Divider */} +
+
+

OR

+
+
+ + {/* Google Login */} + + + {/* Footer */} +

+ Don't have an account?{" "} + + Sign up + +

+
+
+ ); +}; + +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;