Skip to content

Commit a21abce

Browse files
Tutor Registration Form (#92)
* Implement basic form * add TODO for ts-ignore * fix type issues with data and events * simplify types in table, start submitAction work * Implement form reset on submission * rework event id selection * fix validation message errors * remove unneeded features from event-table * add submission feedback * remove code duplication in table pagination * add styling, translate to german * implement onSubmit error handling * prototype the backend push * fix: users query for availabilities * add better handling of email display * translate submit button, reduce mobile padding * fix intendation in tutor mutation --------- Co-authored-by: Daniel Heidemann <[email protected]>
1 parent caa99dc commit a21abce

15 files changed

+632
-210
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,83 @@
1-
"use client";
2-
3-
import { ColumnDef } from "@tanstack/react-table";
4-
import { Checkbox } from "@/components/ui/checkbox";
5-
import { eventBroker } from "@/lib/eventBroker";
1+
import { DataTableColumnHeader } from "@/components/data-table-column-header";
62
import { Badge } from "@/components/ui/badge";
3+
import { Checkbox } from "@/components/ui/checkbox";
74
import { Event } from "@/lib/gql/generated/graphql";
5+
import { formatDateToDDMM, formatDateToHHMM } from "@/lib/utils";
6+
import { ColumnDef } from "@tanstack/react-table";
87

98
export const columns: ColumnDef<Event>[] = [
109
{
11-
accessorKey: "isSelected",
12-
header: "",
10+
id: "select",
11+
header: ({ table }) => (
12+
<Checkbox
13+
checked={
14+
table.getIsAllPageRowsSelected() ||
15+
(table.getIsSomePageRowsSelected() && "indeterminate")
16+
}
17+
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
18+
aria-label="Alle auswählen"
19+
/>
20+
),
1321
cell: ({ row }) => (
1422
<Checkbox
15-
className={"mx-auto"}
1623
checked={row.getIsSelected()}
1724
onCheckedChange={(value) => {
18-
row.toggleSelected(!!value);
19-
20-
if (row.getIsSelected()) {
21-
eventBroker.removeEvent(row.original.ID);
22-
} else {
23-
eventBroker.addEvent(row.original.ID);
24-
}
25+
row.toggleSelected(!!value)
2526
}}
26-
aria-label="Ich kann diese Veranstaltung halten"
27+
aria-label="Reihe auswählen"
2728
/>
2829
),
2930
},
3031
{
3132
accessorKey: "title",
32-
header: () => <div className="text-left">Veranstaltung</div>,
33-
cell: ({ row }) => (
34-
<div>
35-
<div className={"mb-0.5"}>{row.original.title}</div>
36-
<Badge color={row.original.type.color ?? ""}>{row.original.type.name}</Badge>
37-
</div>
33+
header: ({ column }) => (
34+
<DataTableColumnHeader column={column} title="Titel" />
3835
),
36+
cell: ({ row }) => row.original.title,
3937
},
4038
{
41-
accessorKey: "from",
42-
header: () => <div className="text-left">Datum</div>,
39+
accessorKey: "date",
40+
header: ({ column }) => (
41+
<DataTableColumnHeader column={column} title="Datum" />
42+
),
4343
cell: ({ row }) => {
44-
const date = new Date(row.getValue("from"));
45-
return date.toLocaleDateString();
44+
return formatDateToDDMM(new Date(row.original.from));
4645
},
4746
},
4847
{
4948
accessorKey: "from",
50-
header: () => <div className="text-left">Von</div>,
49+
header: ({ column }) => (
50+
<DataTableColumnHeader column={column} title="Von" />
51+
),
5152
cell: ({ row }) => {
52-
const time = new Date(row.getValue("from"));
53-
return time.toLocaleTimeString([], {
54-
hour: "2-digit",
55-
minute: "2-digit",
56-
});
53+
return formatDateToHHMM(new Date(row.original.from));
5754
},
5855
},
5956
{
6057
accessorKey: "to",
61-
header: () => <div className="text-left">Bis</div>,
58+
header: ({ column }) => (
59+
<DataTableColumnHeader column={column} title="Bis" />
60+
),
6261
cell: ({ row }) => {
63-
const time = new Date(row.getValue("to"));
64-
return time.toLocaleTimeString([], {
65-
hour: "2-digit",
66-
minute: "2-digit",
67-
});
62+
return formatDateToHHMM(new Date(row.original.to));
6863
},
6964
},
65+
{
66+
accessorKey: "type",
67+
header: "Art",
68+
cell: ({ row }) => (
69+
<Badge variant="event" color={row.original.type.color ?? ""}>
70+
{row.original.type.name}
71+
</Badge>
72+
),
73+
},
74+
{
75+
accessorKey: "topic",
76+
header: "Thema",
77+
cell: ({ row }) => (
78+
<Badge variant="event" color={row.original.topic.color ?? ""}>
79+
{row.original.topic.name}
80+
</Badge>
81+
),
82+
},
7083
];

frontend/app/(form-tutor)/form-tutor/data-table.tsx

-78
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import {
2+
ColumnDef,
3+
ColumnFiltersState,
4+
SortingState,
5+
VisibilityState,
6+
flexRender,
7+
getCoreRowModel,
8+
getFilteredRowModel,
9+
getSortedRowModel,
10+
useReactTable, RowSelectionState,
11+
} from "@tanstack/react-table";
12+
13+
import {
14+
Table,
15+
TableBody,
16+
TableCell,
17+
TableHead,
18+
TableHeader,
19+
TableRow,
20+
} from "@/components/ui/table";
21+
import React from "react";
22+
import { Input } from "@/components/ui/input";
23+
import {Event} from "@/lib/gql/generated/graphql"
24+
import {DataTablePagination} from "@/components/data-table-pagination";
25+
26+
interface DataTableProps {
27+
columns: ColumnDef<Event>[];
28+
data: Event[];
29+
rowSelection: RowSelectionState;
30+
setRowSelection: React.Dispatch<React.SetStateAction<RowSelectionState>>;
31+
}
32+
33+
export function EventTable({
34+
columns,
35+
data,
36+
rowSelection,
37+
setRowSelection,
38+
}: DataTableProps) {
39+
40+
const [sorting, setSorting] = React.useState<SortingState>([]);
41+
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
42+
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
43+
const table = useReactTable({
44+
data,
45+
columns,
46+
// Makes the row IDs use the Event IDs
47+
getRowId: row => String(row.ID),
48+
getCoreRowModel: getCoreRowModel(),
49+
onSortingChange: setSorting,
50+
getSortedRowModel: getSortedRowModel(),
51+
onColumnFiltersChange: setColumnFilters,
52+
getFilteredRowModel: getFilteredRowModel(),
53+
onColumnVisibilityChange: setColumnVisibility,
54+
onRowSelectionChange: setRowSelection,
55+
state: {
56+
sorting,
57+
columnFilters,
58+
columnVisibility,
59+
rowSelection,
60+
},
61+
});
62+
63+
64+
return (
65+
<div className="space-y-4">
66+
<div className="flex items-center">
67+
<Input
68+
placeholder="Veranstaltungstitel filtern..."
69+
value={(table.getColumn("title")?.getFilterValue() as string) ?? ""}
70+
onChange={(event) =>
71+
table.getColumn("title")?.setFilterValue(event.target.value)
72+
}
73+
className="max-w-sm"
74+
/>
75+
</div>
76+
<div className="rounded-md border overflow-hidden">
77+
<Table>
78+
<TableHeader>
79+
{table.getHeaderGroups().map((headerGroup) => (
80+
<TableRow key={headerGroup.id}>
81+
{headerGroup.headers.map((header) => {
82+
return (
83+
<TableHead className={'text-center'} key={header.id}>
84+
{header.isPlaceholder
85+
? null
86+
: flexRender(
87+
header.column.columnDef.header,
88+
header.getContext()
89+
)}
90+
</TableHead>
91+
);
92+
})}
93+
</TableRow>
94+
))}
95+
</TableHeader>
96+
<TableBody>
97+
{table.getRowModel().rows?.length ? (
98+
table.getRowModel().rows.map((row) => (
99+
<TableRow
100+
key={row.id}
101+
data-state={row.getIsSelected() && "selected"}
102+
>
103+
{row.getVisibleCells().map((cell) => (
104+
<TableCell className={'[&:not(:first-child)]:pl-8'} key={cell.id}>
105+
{flexRender(
106+
cell.column.columnDef.cell,
107+
cell.getContext()
108+
)}
109+
</TableCell>
110+
))}
111+
</TableRow>
112+
))
113+
) : (
114+
<TableRow>
115+
<TableCell
116+
colSpan={columns.length}
117+
className="h-24 text-center"
118+
>
119+
Keine Ergebnisse.
120+
</TableCell>
121+
</TableRow>
122+
)}
123+
</TableBody>
124+
</Table>
125+
</div>
126+
<DataTablePagination table={table} enableSelectionCounter={false} />
127+
</div>
128+
);
129+
}

frontend/app/(form-tutor)/form-tutor/layout.tsx

-21
This file was deleted.
+17-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1-
"use client"
1+
"use client";
2+
3+
import { useState } from "react";
4+
import TutorRegistrationForm from "@/app/(form-tutor)/form-tutor/tutor-registration-form";
5+
import { SuccceededSubmissionWindow } from "@/app/(form-tutor)/form-tutor/succeeded-submission-window";
6+
7+
export default function TutorRegistration() {
8+
const [submissionSuccess, setSubmissionSuccess] = useState<boolean>(false)
9+
const [userMail, setUserMail] = useState<string>("")
210

3-
export default function FormTutor() {
411
return (
5-
<>
6-
<h1 className="text-xl">Deine Verfügbarkeiten</h1>
7-
</>
8-
);
12+
<div className={'min-h-[100vh] min-w-[100vw] pt-28 p-5 flex flex-col items-center justify-center'}>
13+
{(!submissionSuccess) ? (
14+
<TutorRegistrationForm setSubmissionSuccess={setSubmissionSuccess} setUserMail={setUserMail} />
15+
) : (
16+
<SuccceededSubmissionWindow userMail={userMail} />
17+
)}
18+
</div>
19+
)
920
}

0 commit comments

Comments
 (0)