diff --git a/apps/web/app/admin/page.tsx b/apps/web/app/admin/page.tsx index 23b827b..d306298 100644 --- a/apps/web/app/admin/page.tsx +++ b/apps/web/app/admin/page.tsx @@ -1,555 +1,869 @@ "use client"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useMemo } from "react"; import { useRouter } from "next/navigation"; import { signOut } from "@/lib/auth-client"; import { trpc } from "@/lib/trpc"; import { PIN_CATEGORIES, getPinColor } from "@/data/pin-categories"; import { useTheme } from "@/lib/ThemeContext"; +import Link from "next/link"; export default function AdminDashboard() { - const router = useRouter(); - - const { data, isLoading } = trpc.user.getCurrent.useQuery(); - - const [isSidebarOpen, setIsSidebarOpen] = useState(false); - const { theme, toggleTheme } = useTheme(); - const [activeSection, setActiveSection] = useState("overview"); - - useEffect(() => { - const observer = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting) { - setActiveSection(entry.target.id); - } - }); - }, - { - root: document.querySelector('.content-area'), - rootMargin: "-10% 0px -70% 0px" - } - ); - - const sections = document.querySelectorAll(".dashboard-section"); - sections.forEach((section) => observer.observe(section)); - - return () => observer.disconnect(); - }, []); - - const scrollToSection = (sectionId: string) => { - const element = document.getElementById(sectionId); - if (element) { - element.scrollIntoView({ behavior: 'smooth', block: 'start' }); - setActiveSection(sectionId); - if (window.innerWidth <= 768) setIsSidebarOpen(false); - } - }; - - const goToMap = () => router.push("/"); - - const handleSignOut = async () => { - await signOut(); - router.refresh(); - }; - - const globalPinStats = { - totalPins: 1240, - verifiedPins: 1105, - pendingPins: 85, - rejectedPins: 50, - categoryBreakdown: { - academic: 450, - food: 320, - social: 150, - transit: 200, - utility: 120, - } - }; - - const globalVerificationRate = Math.round((globalPinStats.verifiedPins / globalPinStats.totalPins) * 100) || 0; - - const globalUserStats = { - totalUsers: 342, - totalComments: 1840, - avgPins: 3.6, - avgComments: 5.3, - newUsers7Days: 14, - newUsers30Days: 45, - }; - - const globalPendingPins = [ - { id: "gp1", title: "Palma Hall Annex", lat: 14.6534, lng: 121.0691, type: "academic", submittedBy: "u1" }, - { id: "gp2", title: "KNL Tricycle Terminal", lat: 14.6552, lng: 121.0621, type: "transit", submittedBy: "u2" }, - { id: "gp3", title: "Gyud Food", lat: 14.6542, lng: 121.0665, type: "food", submittedBy: "u3" }, - ]; - - const globalVerifiedPins = [ - { id: "v1", title: "Main Library", lat: 14.6540, lng: 121.0660, type: "academic", submittedBy: "u1" }, - { id: "v2", title: "Area 2 Kiosk 4", lat: 14.6530, lng: 121.0685, type: "food", submittedBy: "u2" }, - { id: "v3", title: "AS Parking", lat: 14.6538, lng: 121.0688, type: "utility", submittedBy: "u3" }, - ]; - - const recentUsers = [ - { id: "u1", name: "User 1", email: "user1@up.edu.ph", joinedAt: "2 hours ago" }, - { id: "u2", name: "User 2", email: "user2@up.edu.ph", joinedAt: "5 hours ago" }, - { id: "u3", name: "User 3", email: "user3@up.edu.ph", joinedAt: "1 day ago" }, - { id: "u4", name: "User 4", email: "user4@up.edu.ph", joinedAt: "2 days ago" }, - ]; - - const topUsers = [ - { id: "u1", name: "User 1", pinCount: 142, rank: 1 }, - { id: "u2", name: "User 2", pinCount: 89, rank: 2 }, - { id: "u3", name: "User 3", pinCount: 75, rank: 3 }, - { id: "u4", name: "User 4", pinCount: 60, rank: 4 }, - ]; - - return ( -
- {/* --- MOBILE OVERLAY --- */} - {isSidebarOpen && ( -
setIsSidebarOpen(false)} - /> - )} - - {/* --- SIDEBAR --- */} - - -
- {/* --- HEADER --- */} -
-
- -

Admin Dashboard

-
-
- - {/* --- MAIN --- */} -
-
-
-

- {isLoading ? "LOADING..." : `Welcome, ${data?.name ? data.name.toUpperCase() : "ADMIN"}!`} -

-

You have accessed the restricted area. 🚨

-
- -
-
-

OVERVIEW

-
-
-
-

OVERALL PIN STATISTICS

-
-
- - {/* Top Stats Grid */} -
-
- TOTAL PINS IN MAP - {globalPinStats.totalPins} -
-
- AWAITING ACTION - - {globalPinStats.pendingPins} - -
-
- -
-
- GLOBAL VERIFICATION - - {globalVerificationRate}% - -
-
-
-
-
- {globalPinStats.verifiedPins} VERIFIED - {globalPinStats.pendingPins} PENDING - {globalPinStats.rejectedPins} REJECTED -
-
- -
- CATEGORY BREAKDOWN -
- {PIN_CATEGORIES.map((category) => { - const count = globalPinStats.categoryBreakdown[category.id as keyof typeof globalPinStats.categoryBreakdown] || 0; - const percentage = globalPinStats.totalPins > 0 ? (count / globalPinStats.totalPins) * 100 : 0; - - return ( -
-
- {category.label} - {count} -
-
-
-
-
- ); - })} -
-
-
-
- -
-
-

OVERALL USER STATISTICS

-
- -
-
-
- TOTAL USERS - {globalUserStats.totalUsers} -
-
- TOTAL COMMENTS - {globalUserStats.totalComments} -
-
- AVERAGE PINS / USER - {globalUserStats.avgPins} -
-
- AVERAGE COMMENTS / USER - {globalUserStats.avgComments} -
-
- NEW USERS FOR THE LAST WEEK - {globalUserStats.newUsers7Days} -
-
- NEW USERS FOR THE LAST MONTH - {globalUserStats.newUsers30Days} -
-
-
-
-
-
- -
-

PIN MANAGEMENT

-
-
-
-

PENDING PIN VERIFICATIONS

-
- -
-
- {globalPendingPins.map((pin) => { - const color = getPinColor(pin.type); - return ( -
-
-
- {pin.title.charAt(0).toUpperCase()} -
- -
- {pin.title} - - By {pin.submittedBy} • {pin.lat.toFixed(4)}, {pin.lng.toFixed(4)} - -
-
- -
- - - - - -
- -
- ); - })} -
-
-
- -
-
-

RECENTLY VERIFIED PINS

-
- -
-
- {globalVerifiedPins.map((pin) => { - const color = getPinColor(pin.type); - return ( -
-
-
- {pin.title.charAt(0).toUpperCase()} -
- -
- {pin.title} - - By {pin.submittedBy} • {pin.lat.toFixed(4)}, {pin.lng.toFixed(4)} - -
-
- -
- -
-
- ); - })} -
-
-
-
-
- -
-

USER MANAGEMENT

-
-
-
-

NEWEST USERS

-
-
-
- {recentUsers.map((user) => ( -
-
-
- {user.name.charAt(0).toUpperCase()} -
-
- {user.name} - - {user.email} • {user.joinedAt} - -
-
- - - -
- ))} -
-
-
- -
-
-

TOP USERS BY PINS

-
-
-
- {topUsers.map((user, index) => ( -
- -
-
- {user.name.charAt(0).toUpperCase()} -
-
- {user.name} - Rank #{user.rank} Operator -
-
- -
- - {user.pinCount} - - PINS -
- -
- ))} -
-
-
-
-
- -
-
-
-
- - -
- ); -} \ No newline at end of file +
+ ); +} diff --git a/apps/web/app/dashboard/page.tsx b/apps/web/app/dashboard/page.tsx index edf1f13..50afe41 100644 --- a/apps/web/app/dashboard/page.tsx +++ b/apps/web/app/dashboard/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import { signOut } from "@/lib/auth-client"; import { trpc } from "@/lib/trpc"; @@ -8,413 +8,591 @@ import { PIN_CATEGORIES, getPinColor } from "@/data/pin-categories"; import { useTheme } from "@/lib/ThemeContext"; export default function Dashboard() { - const router = useRouter(); - const { data, isLoading } = trpc.user.getCurrent.useQuery(); - const [isSidebarOpen, setIsSidebarOpen] = useState(false); - - const [isEditingBio, setIsEditingBio] = useState(false); - const [bioInput, setBioInput] = useState(""); - const { theme, toggleTheme } = useTheme(); - - const handleSaveBio = () => { - // TODO: Hook this up to trpc.user.updateBio.useMutation() - console.log("Saving new bio:", bioInput); - setIsEditingBio(false); - }; - - const goToMap = () => router.push("/"); - const goToAdmin = () => router.push("/admin"); - - const handleSignOut = async () => { - await signOut(); - router.refresh(); - }; - - const mockStats = { - totalPins: 42, - verifiedPins: 38, - pendingPins: 3, - rejectedPins: 1, - comments: 128, - categoryBreakdown: { - academic: 12, - food: 15, - social: 5, - transit: 8, - utility: 2, - }, - pendingList: [ - { id: "p1", title: "Quezon Hall", lat: 14.6549, lng: 121.0645, type: "academic" }, - { id: "p2", title: "Area 2", lat: 14.6532, lng: 121.0681, type: "food" }, - { id: "p3", title: "Sunken Garden", lat: 14.6544, lng: 121.0673, type: "social" }, - ], - recentList: [ - { id: "r1", title: "CS Library", lat: 14.6538, lng: 121.0694, type: "academic" }, - { id: "r2", title: "TOKI Jeepney Stop", lat: 14.6551, lng: 121.0621, type: "transit" }, - ] - }; - - const verificationRate = Math.round((mockStats.verifiedPins / mockStats.totalPins) * 100) || 0; - - return ( -
- {/* --- MOBILE OVERLAY --- */} - {isSidebarOpen && ( -
setIsSidebarOpen(false)} - /> - )} - - {/* --- SIDEBAR --- */} - - -
- {/* --- HEADER --- */} -
-
- -

User Dashboard

-
-
- - {/* --- MAIN --- */} -
-
-
-

- {isLoading ? "LOADING..." : `Welcome, ${data?.name ? data.name.toUpperCase() : "UNKNOWN"}!`} -

-

You made it!

-
- - {/* --- DASHBOARD GRID --- */} -
- -
-
-

YOUR PROFILE

- - {(data as any)?.role === "ADMIN" ? "ADMIN" : "REGULAR USER"} - -
- -
-
-
-
- {data?.name ? data.name.charAt(0).toUpperCase() : "O"} -
-
-
- {data?.name || "UNKNOWN NAME"} - {(data as any)?.email || "UNKNOWN EMAIL"} -
-
- -
-
- BIO - {!isEditingBio && ( - - )} -
- - {isEditingBio ? ( -
-