implemented assigning users to events

This commit is contained in:
z1glr
2025-01-23 21:29:08 +00:00
parent 19e7d2b366
commit 00ab161261
8 changed files with 229 additions and 42 deletions

View File

@@ -10,8 +10,12 @@ export default function MyEvents() {
const result = await apiCall<EventData[]>("GET", "events/user/assigned");
if (result.ok) {
const data = await result.json();
console.debug(data);
return {
items: await result.json(),
items: data,
};
} else {
return {

View File

@@ -3,7 +3,7 @@
import AddEvent from "@/components/Event/AddEvent";
import EditEvent from "@/components/Event/EditEvent";
import LocalDate from "@/components/LocalDate";
import { apiCall, getTasks } from "@/lib";
import { apiCall, getAvailabilities, getTasks } from "@/lib";
import { EventData } from "@/Zustand";
import {
Add,
@@ -21,6 +21,7 @@ import {
Dropdown,
DropdownItem,
DropdownMenu,
DropdownSection,
DropdownTrigger,
Modal,
ModalBody,
@@ -37,14 +38,21 @@ import {
Tooltip,
} from "@heroui/react";
import { useAsyncList } from "@react-stately/data";
import React, { Key, useState } from "react";
import React, { Key, ReactElement, useEffect, useState } from "react";
import { Availability } from "../admin/(availabilities)/AvailabilityEditor";
import AvailabilityChip from "@/components/AvailabilityChip";
type EventWithAvailabilities = EventData & { availabilities: string[] };
type EventWithAvailabilities = EventData & {
availabilities: Record<string, string[]>;
};
export default function AdminPanel() {
const [showAddEvent, setShowAddEvent] = useState(false);
const [editEvent, setEditEvent] = useState<EventData | undefined>();
const [deleteEvent, setDeleteEvent] = useState<EventData | undefined>();
const [availabilityMap, setAvailabilityMap] = useState<
Record<number, Availability>
>({});
// get the available tasks and craft them into the headers
const headers = useAsyncList<{
@@ -113,6 +121,51 @@ export default function AdminPanel() {
},
});
// retrieve the availabilites and store them in a map
useEffect(() => {
(async () => {
setAvailabilityMap(
Object.fromEntries(
(await getAvailabilities()).map((a) => [a.availabilityID, a]),
),
);
})();
}, []);
function getAvailabilityById(availabilityID: number): Availability {
return availabilityMap[availabilityID];
}
// send a command to the backend to assign a volunteer to a task
async function sendVolunteerAssignment(
eventID: number,
taskID: number,
userName: string,
) {
const result = await apiCall(
"PUT",
"events/assignments",
{ eventID, taskID },
userName,
);
if (result.ok) {
events.reload();
}
}
// sends a command to the backend to remove an volunteer-assignment
async function removeVolunteerAssignment(eventID: number, taskID: number) {
const result = await apiCall("DELETE", "events/assignments", {
eventID,
taskID,
});
if (result.ok) {
events.reload();
}
}
// send a delete request to the backend and close the popup on success
async function sendDeleteEvent() {
if (deleteEvent !== undefined) {
@@ -123,6 +176,9 @@ export default function AdminPanel() {
if (result.ok) {
// store the received events
events.reload();
// close the delete-confirmaton
setDeleteEvent(undefined);
}
}
}
@@ -184,9 +240,13 @@ export default function AdminPanel() {
return (
<Dropdown>
<DropdownTrigger>
{!!event.tasks.find((t) => t.taskID === key)?.userName ? (
<Chip onClose={() => alert("implement")}>
{event.tasks.find((t) => t.taskID === key)?.taskName}
{!!event.tasks.find((t) => t.taskID == key)?.userName ? (
<Chip
onClose={() =>
removeVolunteerAssignment(event.eventID, key as number)
}
>
{event.tasks.find((t) => t.taskID == key)?.userName}
</Chip>
) : (
<Button isIconOnly size="sm" radius="md" variant="flat">
@@ -194,19 +254,50 @@ export default function AdminPanel() {
</Button>
)}
</DropdownTrigger>
<DropdownMenu>
<DropdownMenu
onAction={(a) =>
sendVolunteerAssignment(
event.eventID,
key as number,
a as string,
)
}
>
{Object.entries(event.availabilities).map(
([volunteer, availability]) => (
<DropdownItem
key={volunteer}
// color={availability2Color(availability)}
className={[
// "text-" + availability2Color(availability),
// availability2Tailwind(availability),
].join(" ")}
(
[availabilityId, volunteers],
iAvailability,
aAvailabilities,
) => (
<DropdownSection
key={availabilityId}
showDivider={iAvailability < aAvailabilities.length - 1}
classNames={{
base: "flex flex-col justify-start",
heading: "mx-auto",
}}
className="justi"
title={
(
<AvailabilityChip
availability={getAvailabilityById(
parseInt(availabilityId),
)}
/>
) as ReactElement & string
}
>
{volunteer} ({availability})
</DropdownItem>
{volunteers.map((v) => (
<DropdownItem
key={v}
classNames={{
base: "", // this empty class is needed, else some styles are applied
}}
>
{v}
</DropdownItem>
))}
</DropdownSection>
),
)}
</DropdownMenu>

View File

@@ -6,11 +6,11 @@ export default function AvailabilityChip({
availability,
className,
}: {
availability: Availability;
availability?: Availability;
className?: string;
classNames?: ChipProps["classNames"];
}) {
return (
return !!availability ? (
<Chip
classNames={{
base: `bg-${color2Tailwind(availability.color)}`,
@@ -19,5 +19,5 @@ export default function AvailabilityChip({
>
{availability.availabilityName}
</Chip>
);
) : null;
}

View File

@@ -59,7 +59,7 @@ export async function apiCall<K>(
},
credentials: "include",
method,
body: body !== undefined ? JSON.stringify(body) : undefined,
body: prepareBody(body),
});
return response;
@@ -79,6 +79,19 @@ function getContentType(type: string): string {
}
}
function prepareBody(
body: object | number | string | boolean | undefined,
): BodyInit | undefined {
switch (typeof body) {
case "object":
return JSON.stringify(body);
case "undefined":
return undefined;
default:
return body.toString();
}
}
export function classNames(classNames: Record<string, boolean>): string {
return Object.entries(classNames)
.map(([classString, value]) => {
@@ -157,11 +170,11 @@ export async function getAvailabilities(): Promise<Availability[]> {
const result = await apiCall<Availability[]>("GET", "availabilities");
if (result.ok) {
const tasks = await result.json();
const availabilities = await result.json();
state.patch({ availabilities: tasks });
state.patch({ availabilities: availabilities });
return tasks;
return availabilities;
} else {
return [];
}