Skip to content

Commit

Permalink
login/registration
Browse files Browse the repository at this point in the history
  • Loading branch information
dheidemann committed Oct 9, 2024
1 parent 77a3628 commit 6b8a2b1
Show file tree
Hide file tree
Showing 25 changed files with 1,646 additions and 194 deletions.
2 changes: 0 additions & 2 deletions frontend/app/form-tutor/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { Header } from "@/components/header";
import {TutorRegistrationForm} from "@/components/tutor-registration-form";

export default function Page() {
return (
<div className="w-full h-full">
<Header></Header>
<div className="w-full mt-3">
<h1 className="text-center font-bold text-2xl mb-2"> Anmeldung Vorkurstutor:in </h1>
<TutorRegistrationForm/>
Expand Down
2 changes: 2 additions & 0 deletions frontend/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import {Toaster} from "@/components/ui/sonner";
import Header from "@/components/header";

const inter = Inter({ subsets: ["latin"] });

Expand All @@ -17,6 +18,7 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<Header />
<body className={inter.className}>{children}</body>
<Toaster />
</html>
Expand Down
12 changes: 5 additions & 7 deletions frontend/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import Header from "@/components/header";
import React, { Suspense } from "react";
import { Skeleton } from "@/components/ui/skeleton";
import Planner from "./ui/planner";
import { Providers } from "./providers";

export default function Home() {
return (
<main>
<Header />

<Suspense fallback={<Skeleton className="h-10 w-[200px]" />}>
<Suspense fallback={<Skeleton className="h-10 w-[200px]" />}>
<Providers>
<Planner />
</Suspense>
</main>
</Providers>
</Suspense>
);
}
35 changes: 35 additions & 0 deletions frontend/app/providers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"use client"

import {ReactNode, createContext, useContext, useState} from "react";
import { User } from "@/lib/gql/generated/graphql"

type UserContextType = {
user: User | null;
setUser: (user: User | null) => void
}

const UserContext = createContext<UserContextType | undefined>(undefined);

export const useUser = () => {
const context = useContext(UserContext);
if (!context) {
throw new Error("useUser must be used within a UserProvider");
}
return context
}

const UserProvider = ({ children }: { children: ReactNode }) => {
const [user, setUser] = useState<User | null>(null);

return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
};

export const Providers = ({ children }: { children: ReactNode }) => {
return (
<UserProvider>{children}</UserProvider>
)
}
130 changes: 119 additions & 11 deletions frontend/app/ui/event-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
"use client";

import {
AddStudentApplicationForEventMutationVariables,
AddStudentRegistrationForEventDocument,
AddStudentRegistrationForEventMutation,
AddStudentRegistrationForEventMutationVariables,
EventCloseupDocument,
EventCloseupQuery,
EventCloseupQueryVariables,
NewUserToEventRegistration,
UserEventRegistrationDocument,
UserEventRegistrationQuery,
UserEventRegistrationQueryVariables,
UserToEventRegistration,
} from "@/lib/gql/generated/graphql";
import { client } from "@/lib/graphClient";
import React, { useEffect, useState } from "react";
import { Mail, Building2, ArrowDownToDot } from "lucide-react";
import { Mail, Building2, ArrowDownToDot, Loader2 } from "lucide-react";

import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
Expand All @@ -16,6 +25,7 @@ import {
DialogContent,
DialogDescription,
DialogTitle,
Dialog,
} from "@/components/ui/dialog";
import { Badge } from "@/components/ui/badge";
import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
Expand All @@ -25,12 +35,21 @@ import {
HoverCardContent,
} from "@/components/ui/hover-card";
import MapPreview from "@/components/map-preview";
import { useUser } from "../providers";
import { SignInDialog } from "@/components/sign-in-dialog";
import { DialogTrigger } from "@radix-ui/react-dialog";

export default function EventDialog({ id }: { id: number }) {
const { user } = useUser();
const [loading, setLoading] = useState(true);
const [regLoading, setRegLoading] = useState(false);
const [registration, setRegistration] = useState<
UserEventRegistrationQuery["registrations"][0] | null
>(null);
const [event, setEvent] = useState<EventCloseupQuery["events"][0] | null>(
null
);
const [registrations, setRegistrations] = useState<number[]>([]);

useEffect(() => {
if (!id) return;
Expand All @@ -50,13 +69,70 @@ export default function EventDialog({ id }: { id: number }) {

if (eventData.events.length) {
setEvent(eventData.events[0]);
setRegistrations(
eventData.events[0].tutorsAssigned?.map(
(t) => t.registrations ?? 0
) || []
);
setLoading(false);
}
};

fetchData();
}, [id]);

useEffect(() => {
if (!user) return;

const fetchData = async () => {
setRegLoading(true);

const vars: UserEventRegistrationQueryVariables = {
id: id,
email: user.mail,
};

await new Promise((resolve) => setTimeout(resolve, 250));

const regData = await client.request<UserEventRegistrationQuery>(
UserEventRegistrationDocument,
vars
);

if (regData.registrations.length) {
setRegistration(regData.registrations[0]);
}

setRegLoading(false);
};

fetchData();
}, [user, id]);

const registerForEvent = async (reg: NewUserToEventRegistration) => {
setRegLoading(true);
await new Promise((resolve) => setTimeout(resolve, 250));

const vars: AddStudentRegistrationForEventMutationVariables = {
registration: reg,
};

const regData =
await client.request<AddStudentRegistrationForEventMutation>(
AddStudentRegistrationForEventDocument,
vars
);

setRegistration(regData.addStudentRegistrationForEvent);
setRegLoading(false);
};

const increaseRegistration = (index: number) => {
setRegistrations((prevRegistrations) =>
prevRegistrations.map((reg, i) => (i === index ? reg + 1 : reg))
);
};

return (
<DialogContent className="sm:max-w-[550px]">
{loading ? (
Expand All @@ -81,24 +157,35 @@ export default function EventDialog({ id }: { id: number }) {
</div>
</DialogDescription>
</DialogHeader>

{!user && (
<div>
<span>Bitte </span>
<Dialog>
<DialogTrigger asChild>
<span className="cursor-pointer text-blue-500 hover:underline">anmelden</span>
</DialogTrigger>
<SignInDialog />
</Dialog>
<span>, um dich eintragen zu können.</span>
</div>
)}
<div className="rounded-md border">
<Table>
<TableBody>
{event?.tutorsAssigned?.map((e) => {
const registrations = e.registrations ?? 0;
{event?.tutorsAssigned?.map((e, i) => {
const capacity = e.room?.capacity ?? 1;
const utilization = (registrations / capacity) * 100;
const utilization = (registrations[i] / capacity) * 100;

return (
<TableRow key={e.room?.number}>
<div
className={
"absolute inset-0 z-0 rounded-md bg-" +
(utilization < 100 ? "green" : "red") +
"-200"
}
className="absolute inset-0 z-0 rounded-md"
style={{
width: `${utilization}%`,
backgroundColor: `${
utilization < 100 ? "#BBF7D0" : "#FECACA"
}`,
}}
/>
<TableCell className="relative z-10">
Expand Down Expand Up @@ -195,8 +282,29 @@ export default function EventDialog({ id }: { id: number }) {
</p>
</TableCell>
<TableCell className="relative z-10">
<Button disabled={utilization == 100} variant="outline">
Eintragen
<Button
disabled={utilization == 100 || !user || regLoading}
variant="outline"
onClick={() => {
registerForEvent({
userMail: user?.mail ?? "",
eventID: event.ID,
roomNumber: e.room?.number ?? "",
buildingID: e.room?.building.ID ?? 0,
});
increaseRegistration(i);
}}
>
{regLoading && (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
)}
{registration
? registration.room.number === e.room?.number &&
registration.room.building.ID ===
e.room.building.ID
? "Abmelden"
: "Wechseln"
: "Anmelden"}
</Button>
</TableCell>
</TableRow>
Expand Down
52 changes: 32 additions & 20 deletions frontend/components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
import * as React from "react"
import {LucideExternalLink} from "lucide-react";
"use client"

import { LucideExternalLink } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { useEffect, useState } from "react";

// TODO: add actual links
const Header = () => {
return (
<div className="w-full h-20 flex flex-row items-center justify-between px-5">
<Image src="/logo.png" alt="Logo der Fachschaft" width="200" height="20" />
<span className="flex flex-row items-center justify-between w-fit">
<Link href="/" className="mr-3">Kontakt</Link>
<Link href="/" className="flex flex-row items-center">Homepage
<LucideExternalLink className="ml-1" size="14"/>
</Link>
</span>
</div>
);
}

export default Header;
export default function Header() {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, []);

if (!isClient) {
return null;
}

Header.displayName = "Header"

export {Header}
return (
<header className="w-full h-20 flex flex-row items-center justify-between px-5">
<Image
src="/logo.png"
alt="Logo der Fachschaft"
width="200"
height="20"
/>
<span className="flex flex-row items-center justify-between w-fit">
<Link href="/" className="mr-3">
Kontakt
</Link>
<Link href="/" className="flex flex-row items-center">
Homepage
<LucideExternalLink className="ml-1" size="14" />
</Link>
</span>
</header>
);
}
Loading

0 comments on commit 6b8a2b1

Please sign in to comment.