Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/basic-features/typescript for more information.
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.1.2",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.1.0",
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/app/admin/categories/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AdminPageSkeleton } from "@/components/dash/shared/AdminPageSkeleton";

export default function Loading() {
return <AdminPageSkeleton title="Categories" rows={8} />;
}
6 changes: 3 additions & 3 deletions apps/web/src/app/admin/categories/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Suspense } from "react";
import AdminCategoryView from "@/components/dash/admin/categories/CategoryView";

export default function Page() {
return (
<div className="mx-auto max-w-6xl pt-4 text-foreground">
Expand All @@ -8,9 +8,9 @@ export default function Page() {
Categories
</h1>
</div>
<Suspense fallback={<div>Grabbing checkin stats. One sec...</div>}>
<div className="px-5">
<AdminCategoryView />
</Suspense>
</div>
</div>
);
}
5 changes: 5 additions & 0 deletions apps/web/src/app/admin/checkins/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AdminPageSkeleton } from "@/components/dash/shared/AdminPageSkeleton";

export default function Loading() {
return <AdminPageSkeleton title="Checkins" rows={10} />;
}
10 changes: 5 additions & 5 deletions apps/web/src/app/admin/checkins/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Button } from "@/components/ui/button";
import AdminCheckinLog from "@/components/dash/shared/AdminCheckinLog";
import { getEventList } from "@/lib/queries/events";
import { Dialog, DialogTrigger } from "@/components/ui/dialog";
import { AdminPageSkeletonContent } from "@/components/dash/shared/AdminPageSkeleton";
import { TableSkeleton } from "@/components/ui/skeleton-loaders";

export default async function Page() {
const eventList = await getEventList();
Expand All @@ -16,9 +18,9 @@ export default async function Page() {
Checkins
</h1>
</div>
<div className="mx-5 flex items-center justify-between rounded-lg border p-2">
<div className="mx-5 flex items-center justify-between">
<Suspense
fallback={<div>Grabbing checkin stats. One sec...</div>}
fallback={<div className="w-full">Loading stats...</div>}
>
<CheckinsStatsSheet />
</Suspense>
Expand All @@ -37,9 +39,7 @@ export default async function Page() {
{/* <div className="border-muted">{events?.[0].name}</div> */}
<div className="rounded-xl p-5">
<div>
<Suspense
fallback={<div>Grabbing checkin log. One sec...</div>}
>
<Suspense fallback={<TableSkeleton rows={10} />}>
<AdminCheckinLog />
</Suspense>
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/admin/events/[id]/checkins/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default async function EventCheckinsPage({
<Suspense
fallback={<div>Grabbing checkin stats. One sec...</div>}
>
<CheckinsStatsSheet eventID={id} />
<CheckinsStatsSheet />
</Suspense>
<div>
<Dialog>
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/app/admin/events/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AdminPageSkeleton } from "@/components/dash/shared/AdminPageSkeleton";

export default function Loading() {
return <AdminPageSkeleton title="Events" rows={10} />;
}
5 changes: 3 additions & 2 deletions apps/web/src/app/admin/events/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DataTable } from "@/components/ui/data-table";
import EventStatsSheet from "@/components/dash/admin/events/EventStatsSheet";
import { Button } from "@/components/ui/button";
import { unstable_noStore as noStore } from "next/cache";
import { TableSkeleton } from "@/components/ui/skeleton-loaders";

async function Page() {
noStore();
Expand All @@ -19,9 +20,9 @@ async function Page() {
Events
</h1>
</div>
<div className="mx-5 flex items-center justify-between rounded-lg border p-2">
<div className="mx-5 flex items-center justify-between">
<Suspense
fallback={<div>Grabbing event stats. One sec...</div>}
fallback={<div className="w-full">Loading stats...</div>}
>
<EventStatsSheet />
</Suspense>
Expand Down
3 changes: 1 addition & 2 deletions apps/web/src/app/admin/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import FullScreenMessage from "@/components/shared/fullscreen-message";
import Navbar from "@/components/shared/navbar";
import DashNavItem from "@/components/dash/shared/DashNavItem";
import ClientToast from "@/components/shared/client-toast";
import { Suspense } from "react";
import c from "config";

export default async function AdminLayout({
Expand Down Expand Up @@ -43,7 +42,7 @@ export default async function AdminLayout({
return <DashNavItem key={name} name={name} path={path} />;
})}
</div>
<Suspense fallback={<p>Loading...</p>}>{children}</Suspense>
{children}
</>
);
}
Expand Down
35 changes: 35 additions & 0 deletions apps/web/src/app/admin/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
DashboardSectionSkeleton,
KeyMetricsSkeleton,
} from "@/components/ui/skeleton-loaders";

export default function Loading() {
return (
<div className="mx-auto max-w-7xl space-y-4 p-3 text-foreground sm:space-y-6 sm:p-4">
<div className="flex flex-col space-y-1 sm:space-y-2">
<h1 className="text-2xl font-bold tracking-tight sm:text-3xl">
Admin Overview
</h1>
<p className="text-sm text-muted-foreground sm:text-base">
Monitor key club metrics and trends
</p>
<div className="mt-1 h-px w-full bg-border sm:mt-2" />
</div>

{/* Key Metrics Summary Cards */}
<KeyMetricsSkeleton />

{/* Trends Section */}
<DashboardSectionSkeleton height="h-[300px]" />

{/* Engagement Metrics */}
<DashboardSectionSkeleton height="h-[200px]" />

{/* Activity Patterns */}
<DashboardSectionSkeleton height="h-[250px]" />

{/* Demographics */}
<DashboardSectionSkeleton height="h-[300px]" />
</div>
);
}
13 changes: 12 additions & 1 deletion apps/web/src/app/admin/members/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ import Link from "next/link";
const timeFormatString = "eee, MMM dd yyyy HH:mm bb";

const timeCell = ({ row }: { row: Row<UserWithData> }) => {
const formattedDate = formatDate(row.getValue(""), timeFormatString);
const formattedDate = formatDate(
row.getValue("joinDate"),
timeFormatString,
);
return <div>{formattedDate}</div>;
};

Expand Down Expand Up @@ -126,6 +129,14 @@ export const columns: ColumnDef<UserWithData>[] = [
return <DataTableColumnHeader column={column} title="Role" />;
},
},
{
accessorKey: "user.joinDate",
id: "joinDate",
header: ({ column }) => {
return <DataTableColumnHeader column={column} title="Join Date" />;
},
cell: ({ row }) => timeCell({ row }),
},
{
id: "actions",
enablePinning: true,
Expand Down
19 changes: 19 additions & 0 deletions apps/web/src/app/admin/members/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { StatsSkeleton, TableSkeleton } from "@/components/ui/skeleton-loaders";

export default function Loading() {
return (
<div className="mx-auto max-w-6xl pt-4 text-foreground">
<div className="mb-5 grid grid-cols-2 px-5">
<h1 className="font-foreground text-3xl font-bold tracking-tight">
Members
</h1>
</div>
<div className="px-5">
<StatsSkeleton />
</div>
<div className="rounded-xl p-5">
<TableSkeleton rows={10} />
</div>
</div>
);
}
6 changes: 1 addition & 5 deletions apps/web/src/app/admin/members/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { Suspense } from "react";
import { getUserWithData } from "@/lib/queries/users";
import { columns } from "./columns";

import { DataTable } from "@/components/ui/data-table";
import MemberStatsSheet from "@/components/dash/admin/members/MemberStatsSheet";

Expand All @@ -15,9 +13,7 @@ async function Page() {
</h1>
</div>
<div className="px-5">
<Suspense fallback={<div>...loading</div>}>
<MemberStatsSheet />
</Suspense>
<MemberStatsSheet />
</div>
<div className="rounded-xl p-5">
<DataTable
Expand Down
97 changes: 52 additions & 45 deletions apps/web/src/app/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,62 @@
import DemographicsStats from "@/components/dash/admin/overview/DemographicsStats";
import MonthlyRegistrationChart from "@/components/dash/admin/overview/MonthlyRegistrationChart";
import { Suspense } from "react";
import { Separator } from "@/components/ui/separator";
import KeyMetrics from "@/components/dash/admin/overview/KeyMetrics";
import MembershipTrends from "@/components/dash/admin/overview/MembershipTrends";
import EngagementSection from "@/components/dash/admin/overview/EngagementSection";
import ActivityPatterns from "@/components/dash/admin/overview/ActivityPatterns";
import DemographicsSection from "@/components/dash/admin/overview/DemographicsSection";
import {
getRegistrationsByMonth,
getUserClassifications,
} from "@/lib/queries/charts";
import { Suspense } from "react";
KeyMetricsSkeleton,
DashboardSectionSkeleton,
} from "@/components/ui/skeleton-loaders";

// The dashboard always fetches fresh data on each request
// Data is memoized within each render via React's built-in request deduplication
export default async function Page() {
const monthlyRegistrations = await getRegistrationsByMonth();
const classifications = await getUserClassifications();
return (
<div className="mx-auto max-w-6xl pt-4 text-foreground">
<div className="mb-5 grid grid-cols-2 px-5">
<h1 className="font-foreground text-3xl font-bold tracking-tight">
Trends
<div className="mx-auto max-w-7xl space-y-4 p-3 text-foreground sm:space-y-6 sm:p-4">
<div className="flex flex-col space-y-1 sm:space-y-2">
<h1 className="text-2xl font-bold tracking-tight sm:text-3xl">
Admin Overview
</h1>
<p className="text-sm text-muted-foreground sm:text-base">
Monitor key club metrics and trends
</p>
<Separator className="mt-1 sm:mt-2" />
</div>
<div className="grid grid-flow-col grid-cols-12">
<div className="col-span-4">
<Suspense
fallback={
<div className="text-foreground">
Retrieving registration chart. One sec...
</div>
}
>
<MonthlyRegistrationChart
registrations={monthlyRegistrations}
/>
</Suspense>
</div>
</div>
<div className="pt-5">
<div className="mb-5 grid grid-cols-2 px-5">
<h1 className="font-foreground text-3xl font-bold tracking-tight">
Demographics
</h1>
</div>
<div className="grid grid-flow-col grid-cols-12">
<Suspense
fallback={
<div className="text-foreground">
Retrieving demographics charts. One sec...
</div>
}
>
<DemographicsStats classifications={classifications} />
</Suspense>
</div>
</div>

{/* Key Metrics Summary Cards with streaming */}
<Suspense fallback={<KeyMetricsSkeleton />}>
<KeyMetrics />
</Suspense>

{/* Trends Section with streaming */}
<Suspense
fallback={<DashboardSectionSkeleton height="h-[300px]" />}
>
<MembershipTrends />
</Suspense>

{/* Engagement Metrics with streaming */}
<Suspense
fallback={<DashboardSectionSkeleton height="h-[200px]" />}
>
<EngagementSection />
</Suspense>

{/* Activity Patterns with streaming */}
<Suspense
fallback={<DashboardSectionSkeleton height="h-[250px]" />}
>
<ActivityPatterns />
</Suspense>

{/* Demographics with streaming */}
<Suspense
fallback={<DashboardSectionSkeleton height="h-[300px]" />}
>
<DemographicsSection />
</Suspense>
</div>
);
}
5 changes: 5 additions & 0 deletions apps/web/src/app/admin/semesters/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AdminPageSkeleton } from "@/components/dash/shared/AdminPageSkeleton";

export default function Loading() {
return <AdminPageSkeleton title="Semesters" rows={8} />;
}
4 changes: 3 additions & 1 deletion apps/web/src/app/admin/semesters/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Suspense } from "react";
import AdminSemesterView from "@/components/dash/admin/semesters/SemesterView";
import { AdminPageSkeletonContent } from "@/components/dash/shared/AdminPageSkeleton";

export default function Page() {
return (
<div className="mx-auto max-w-6xl pt-4 text-foreground">
Expand All @@ -8,7 +10,7 @@ export default function Page() {
Semesters
</h1>
</div>
<Suspense fallback={<div>Grabbing checkin stats. One sec...</div>}>
<Suspense fallback={<AdminPageSkeletonContent rows={8} />}>
<AdminSemesterView />
</Suspense>
</div>
Expand Down
13 changes: 13 additions & 0 deletions apps/web/src/app/events/[slug]/checkin/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Navbar from "@/components/shared/navbar";
import { CardSkeleton } from "@/components/ui/skeleton-loaders";

export default function Loading() {
return (
<div className="flex h-[100dvh] w-full flex-col">
<Navbar />
<div className="container my-4">
<CardSkeleton />
</div>
</div>
);
}
Loading
Loading