bun --bun run dev + haldus leht init

This commit is contained in:
2025-07-21 16:26:47 +03:00
parent efc0e34ae7
commit f87f8374f4
25 changed files with 4770 additions and 789 deletions

View File

@@ -0,0 +1,102 @@
// Fonts
import { vipnagorgialla } from "@/components/Vipnagorgialla";
// Database
import { db } from "@/db/drizzle";
import Link from "next/link";
// User interface
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
// Later on we can use a i8 solution?
function translateRole(role: string): string {
switch (role) {
case "CAPTAIN":
return "Kapten";
case "TEAMMATE":
return "Meeskonnaliige";
default:
return role;
}
}
export default async function AdminTeams() {
// Fetch teams with their members and member users
const teams = await db.query.teams.findMany({
with: {
members: {
with: {
user: true,
},
},
},
});
return (
<div className="flex flex-col min-h-[90vh] p-12 pt-18">
<div className="flex items-center gap-4">
<Link href={"/haldus"}>
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] translate-y-2.5 hover:-translate-x-2 dark:hover:text-[#EEE5E5] hover:text-[#2A2C3F] transition">
arrow_left_alt
</span>
</Link>
<h1
className={`text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`}
>
Haldus - Meeskonnad
</h1>
</div>
<div className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5]">
<div>
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">ID</TableHead>
<TableHead>Nimi</TableHead>
<TableHead>Liikmed</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{teams.map((team: any) => (
<TableRow key={team.id}>
<TableCell className="font-medium">{team.id}</TableCell>
<TableCell>{team.name}</TableCell>
<TableCell>
<div className="flex flex-col gap-1">
{team.members && team.members.length > 0 ? (
team.members.map((member: any) => (
<div
key={member.id}
className="flex items-center gap-2 text-sm"
>
<span className="font-semibold">
{member.user.firstName} {member.user.lastName}
</span>
<span className="text-gray-500">
({translateRole(member.role)})
</span>
</div>
))
) : (
<span className="text-gray-500">Liikmeid puuduvad</span>
)}
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
</div>
);
}

168
src/app/haldus/page.tsx Normal file
View File

@@ -0,0 +1,168 @@
// Fonts
import { vipnagorgialla } from "@/components/Vipnagorgialla";
// Database
import { db } from "@/db/drizzle";
import { syncFientaEvent } from "@/lib/fienta";
// Enviornment variables
import("dotenv");
// User interface
import {
Users,
IdCardLanyard,
DatabaseBackup,
CheckCircle2Icon,
X,
} from "lucide-react";
import Link from "next/link";
import { revalidatePath } from "next/cache";
import { redirect, RedirectType } from "next/navigation";
import { Button } from "@/components/ui/button";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { DataTable } from "@/components/haldus/data-table";
import { columns } from "@/components/haldus/columns";
async function syncAction() {
"use server";
await syncFientaEvent(process.env.EVENT_ID!, process.env.FIENTA_API_KEY!);
// Revalidate due to data change
revalidatePath("/haldus");
redirect("/haldus?success=true");
}
async function dismissAlert() {
"use server";
redirect("/haldus", RedirectType.replace);
}
const SuccessAlertDB = () => {
return (
<Alert className="flex items-start mt-8">
<CheckCircle2Icon className="mt-0.5" />
<div className="flex-1">
<AlertTitle>Toiming oli edukas!</AlertTitle>
<AlertDescription>Andmebaasi andmed on uuendatud.</AlertDescription>
</div>
<form action={dismissAlert} className="ml-2">
<Button
type="submit"
variant="ghost"
size="icon"
className="cursor-pointer"
>
<X className="" />
</Button>
</form>
</Alert>
);
};
export default async function Admin({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
const alarmStatus = await searchParams;
const showSuccess = alarmStatus.success === "true";
// Fetch users
const usersData = await db.query.users.findMany({
with: {
members: {
with: {
team: true,
},
},
},
});
const teamsData = await db.query.teams.findMany();
return (
<div className="flex flex-col min-h-[90vh] p-12 pt-18">
{showSuccess && <SuccessAlertDB />}
<div className="flex items-center gap-4">
<Link href={"/"}>
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] translate-y-2.5 hover:-translate-x-2 dark:hover:text-[#EEE5E5] hover:text-[#2A2C3F] transition">
arrow_left_alt
</span>
</Link>
<h1
className={`text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`}
>
Haldus
</h1>
</div>
<div className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5]">
<div className="pl-2 flex gap-8 pb-4">
<div className="flex text-lg md:text-2xl flex-row items-center">
<Users className="mr-2" />
Kasutajaid: {usersData.length}
</div>
<Link href="/haldus/meeskonnad" className="flex items-center">
<div className="flex text-lg md:text-2xl flex-row items-center">
<IdCardLanyard className="mr-2" />
Meeskondasid: {teamsData.length}
</div>
</Link>
<AlertDialog>
<AlertDialogTrigger asChild>
<div className="ml-auto">
<Button
variant="ghost"
size="icon"
className="size-12 cursor-pointer"
>
<DatabaseBackup className="scale-150" />
</Button>
</div>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogTitle>
Kas soovite värskendada andmebaasi?
</AlertDialogTitle>
<AlertDialogDescription>
See tõmbab Fientast praegused andmed ning asendab{" "}
<span className="text-red-600 font-semibold">KÕIK</span>{" "}
olemasolevad andmed andmebaasis!
<br />
<br />
Kui sa ei ole kindel, vajuta &quot;Tühista&quot;.
</AlertDialogDescription>
<AlertDialogFooter>
<AlertDialogCancel className="cursor-pointer">
Tühista
</AlertDialogCancel>
<form action={syncAction}>
<AlertDialogAction type="submit" className="cursor-pointer">
Värskenda
</AlertDialogAction>
</form>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
<div>
<DataTable columns={columns} data={usersData} />
</div>
</div>
</div>
);
}

View File

@@ -1,16 +1,15 @@
// Head metadata
import type { Metadata } from "next";
import Head from 'next/head';
import Head from "next/head";
// Provides the theme context to the app
import { ThemeProvider } from "@/components/Theme-provider"
import { ThemeProvider } from "@/components/Theme-provider";
import "./globals.css";
import "material-symbols";
// Fonts
import { Work_Sans } from "next/font/google";
import SidebarParent from "@/components/SidebarParent";
import Footer from "@/components/Footer";
@@ -35,20 +34,23 @@ export default function RootLayout({
<Head>
<title>TipiLAN</title>
<meta property="og:title" content="TipiLAN 2025" key="title" />
<meta name="description" content="TipiLAN 2025 Eesti suurim tudengite korraldatud LAN!" />
<meta
name="description"
content="TipiLAN 2025 Eesti suurim tudengite korraldatud LAN!"
/>
</Head>
<body
className={`${workSans.className} antialiased bg-[#EEE5E5] dark:bg-[#0E0F19]`}
>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<SidebarParent />
{children}
<Footer />
<SidebarParent />
{children}
<Footer />
</ThemeProvider>
</body>
</html>