From 511efd1a29de8eaa0ee5648eabe9ef52661a8a2e Mon Sep 17 00:00:00 2001 From: SharanRP Date: Mon, 30 Dec 2024 18:22:44 +0530 Subject: [PATCH 1/5] feat(db): connected supabase and and updated dashboard --- .env.template | 3 + .gitignore | 2 + app/api/cron/update-alumni/route.ts | 15 + app/dashboard/[domain]/page.tsx | 83 ++- app/dashboard/page.tsx | 195 ++++-- app/dev-club/page.tsx | 2 + app/teams/page.tsx | 387 ++++++----- components/ImageSlider.tsx | 94 +++ components/ResourceTable.tsx | 34 +- components/Sidebar.tsx | 86 ++- components/ui/images-slider.tsx | 151 +++++ data/test-data.ts | 95 +++ data/test-team-data.ts | 35 + lib/supabase-resources.ts | 131 ++++ lib/supabase.ts | 65 ++ next.config.mjs | 10 +- package-lock.json | 612 +++++++++++++++++- package.json | 10 +- pnpm-lock.yaml | 535 +++++++++++++++ scripts/create-tables.sql | 53 ++ scripts/reset-database.sql | 138 ++++ scripts/seed-database.ts | 138 ++++ supabase/.temp/cli-latest | 1 + .../migrations/20231230_create_alumni.sql | 15 + .../20231230_create_delete_all_rows.sql | 7 + .../migrations/20231230_create_exec_sql.sql | 7 + .../migrations/20231230_create_resources.sql | 50 ++ .../migrations/20231230_create_tables.sql | 118 ++++ vercel.json | 10 +- 29 files changed, 2754 insertions(+), 328 deletions(-) create mode 100644 .env.template create mode 100644 app/api/cron/update-alumni/route.ts create mode 100644 components/ImageSlider.tsx create mode 100644 components/ui/images-slider.tsx create mode 100644 data/test-data.ts create mode 100644 data/test-team-data.ts create mode 100644 lib/supabase-resources.ts create mode 100644 lib/supabase.ts create mode 100644 scripts/create-tables.sql create mode 100644 scripts/reset-database.sql create mode 100644 scripts/seed-database.ts create mode 100644 supabase/.temp/cli-latest create mode 100644 supabase/migrations/20231230_create_alumni.sql create mode 100644 supabase/migrations/20231230_create_delete_all_rows.sql create mode 100644 supabase/migrations/20231230_create_exec_sql.sql create mode 100644 supabase/migrations/20231230_create_resources.sql create mode 100644 supabase/migrations/20231230_create_tables.sql diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..d6927a0 --- /dev/null +++ b/.env.template @@ -0,0 +1,3 @@ +NEXT_PUBLIC_SUPABASE_URL=https://xlkupdypwxnxyxilizgx.supabase.co +NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inhsa3VwZHlwd3hueHl4aWxpemd4Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzU1NDE1MjksImV4cCI6MjA1MTExNzUyOX0.-WQMkmqKoUn68To-lDuKtnfvL-EUOTutdPJsxCGKoXQ +NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inhsa3VwZHlwd3hueHl4aWxpemd4Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTczNTU0MTUyOSwiZXhwIjoyMDUxMTE3NTI5fQ.QRoSQyxhbqOxMJPYDiQg9sG6VLT6qIduv3YgEbWaPWA \ No newline at end of file diff --git a/.gitignore b/.gitignore index 933241d..5f29e07 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,8 @@ yarn-error.log* .env*.local .env.production .env.development +.env +.env.example # vercel .vercel diff --git a/app/api/cron/update-alumni/route.ts b/app/api/cron/update-alumni/route.ts new file mode 100644 index 0000000..8f8f1e5 --- /dev/null +++ b/app/api/cron/update-alumni/route.ts @@ -0,0 +1,15 @@ +import { NextResponse } from 'next/server'; +import { checkAndUpdateAlumniStatus } from '@/lib/supabase'; + +export async function GET() { + try { + await checkAndUpdateAlumniStatus(); + return NextResponse.json({ success: true, message: 'Alumni status updated successfully' }); + } catch (error) { + console.error('Error updating alumni status:', error); + return NextResponse.json( + { success: false, message: 'Failed to update alumni status' }, + { status: 500 } + ); + } +} diff --git a/app/dashboard/[domain]/page.tsx b/app/dashboard/[domain]/page.tsx index b56fe5c..53c3425 100644 --- a/app/dashboard/[domain]/page.tsx +++ b/app/dashboard/[domain]/page.tsx @@ -1,30 +1,61 @@ "use client"; -// import { useSession } from "next-auth/react"; import { useParams } from "next/navigation"; -import { domains } from "@/config/navigation"; import { motion } from "framer-motion"; import { Card } from "@/components/ui/card"; import { ScrollArea } from "@/components/ui/scroll-area"; import ResourceTable from "@/components/ResourceTable"; +import { useEffect, useState } from "react"; +import { getDomainBySlug, getSubjectsByDomain, type Domain } from "@/lib/supabase-resources"; +import { Code, Database, FileText, Video } from "lucide-react"; -interface Domain { - name: string; - resources: string; - icon: React.ComponentType; - gradient: string; - description: string; - categories: string[]; - } +const domainIcons: { [key: string]: any } = { + 'web-development': Code, + 'machine-learning': Database, + 'mobile-development': FileText, + 'cloud-computing': Video, +}; export default function DomainPage() { -// const { data: session } = useSession(); const params = useParams(); - const currentDomain = domains.find( - (d: Domain) => d.resources === params.domain - ); + const [domain, setDomain] = useState(null); + const [subjects, setSubjects] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchDomainData = async () => { + try { + setLoading(true); + const domainSlug = params.domain as string; + const [domainData, subjectsData] = await Promise.all([ + getDomainBySlug(domainSlug), + getSubjectsByDomain(domainSlug) + ]); + setDomain(domainData); + setSubjects(subjectsData); + } catch (error) { + console.error('Error fetching domain data:', error); + } finally { + setLoading(false); + } + }; + + if (params.domain) { + fetchDomainData(); + } + }, [params.domain]); + + if (loading) { + return ( +
+
+
+ ); + } + + if (!domain) return null; - if (!currentDomain) return null; + const DomainIcon = domainIcons[domain.slug] || Code; return (
@@ -36,10 +67,10 @@ export default function DomainPage() { {/* Welcome Section */}

- {currentDomain.name} + {domain.name}

- Browse through {currentDomain.name.toLowerCase()} resources curated for VJTI students. + Browse through {domain.name.toLowerCase()} resources curated for VJTI students.

@@ -47,15 +78,15 @@ export default function DomainPage() {
-
- +
+

Resources

- {currentDomain.description} + {domain.description}

@@ -63,18 +94,18 @@ export default function DomainPage() {
- {currentDomain.categories.map((category: string) => ( -
+ {subjects.map((subject) => ( +

- {category} + {subject.name}

@@ -85,4 +116,4 @@ export default function DomainPage() {
); -} \ No newline at end of file +} \ No newline at end of file diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index dab6750..4aac44a 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -4,18 +4,91 @@ import { useSession } from "next-auth/react"; import { usePathname } from "next/navigation"; import { motion } from "framer-motion"; import { Card } from "@/components/ui/card"; -// import { ScrollArea } from "@/components/ui/scroll-area"; -// import ResourceTable from "@/components/ResourceTable"; -import { domains } from "@/config/navigation"; +import { useEffect, useState } from "react"; +import { getDomains, getSubjectsByDomain, getResourcesBySubject, type Domain, type Subject, type Resource } from "@/lib/supabase-resources"; import Link from "next/link"; import { cn } from "@/lib/utils"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Badge } from "@/components/ui/badge"; +import { BookOpen, Code, FileText, Video } from "lucide-react"; export default function Dashboard() { const { data: session } = useSession(); const pathname = usePathname(); - const currentDomain = domains.find( - domain => `/dashboard/${domain.resources}` === pathname - ); + const [domains, setDomains] = useState([]); + const [subjects, setSubjects] = useState([]); + const [resources, setResources] = useState([]); + const [selectedDomain, setSelectedDomain] = useState(""); + const [selectedSubject, setSelectedSubject] = useState(""); + const [loading, setLoading] = useState(true); + + const fetchData = async () => { + try { + const domainsData = await getDomains(); + setDomains(domainsData); + if (domainsData.length > 0) { + setSelectedDomain(domainsData[0].slug); + } + } catch (error) { + console.error("Error fetching domains:", error); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchData(); + }, []); + + const fetchSubjects = async () => { + try { + const subjectsData = await getSubjectsByDomain(selectedDomain); + setSubjects(subjectsData); + if (subjectsData.length > 0) { + setSelectedSubject(subjectsData[0].id); + } + } catch (error) { + console.error("Error fetching subjects:", error); + } + }; + + useEffect(() => { + if (selectedDomain) { + fetchSubjects(); + } + }, [selectedDomain]); + + const fetchResources = async () => { + try { + const resourcesData = await getResourcesBySubject(selectedSubject); + setResources(resourcesData); + } catch (error) { + console.error("Error fetching resources:", error); + } + }; + + useEffect(() => { + if (selectedSubject) { + fetchResources(); + } + }, [selectedSubject]); + + const getResourceIcon = (type: string) => { + switch (type) { + case 'video': + return