"use client"; import LocalDate from "@/components/LocalDate"; import { apiCall, getAvailabilities, getUsers, QueryParams } from "@/lib"; import zustand, { BaseEvent } from "@/Zustand"; import { NotAvailable } from "@carbon/icons-react"; import { DateValue, Table, TableBody, TableCell, TableColumn, TableColumnProps, TableHeader, TableRow, } from "@heroui/react"; import { useAsyncList } from "@react-stately/data"; import React, { Key, useEffect, useState } from "react"; import { Availability } from "../admin/(availabilities)/AvailabilityEditor"; import { EventWithAvailabilities } from "../assignments/page"; import AvailabilityChip from "@/components/AvailabilityChip"; import AvailabilitySelector from "@/components/Event/AvailabilitySelector"; import DateEventsSince from "@/components/DateEventsSince"; import { getLocalTimeZone, today } from "@internationalized/date"; import FilterPopover from "@/components/FilterPopover"; type EventWithUserAvailabilityMap = BaseEvent & { availabilities: Record; }; export default function Availabilities() { const [sinceDate, setSinceDate] = useState( today(getLocalTimeZone()), ); const [availabilityMap, setAvailabilityMap] = useState< Record >({}); const user = zustand((state) => state.user); // get the users and craft them into the headers const headers = useAsyncList<{ key: string | number; label: string; align?: string; }>({ async load() { const users = await getUsers(); const headers = { items: [ { key: "date", label: "Date" }, { key: "description", label: "Description" }, { key: user?.userName ?? "me", label: "Me", align: "center" }, ...users .filter((eventUser) => eventUser.userName !== user?.userName) .map((user) => ({ label: user.userName, key: user.userName ?? -1, align: "center", })), ], }; return headers; }, }); // get the individual events const events = useAsyncList({ async load() { let params: QueryParams | undefined = undefined; if (sinceDate) { params = { since: sinceDate, }; } const result = await apiCall( "GET", "events/availabilities", params, ); if (result.ok) { const data = await result.json(); // convert the availabilities to a map const eventAvailabilities: EventWithUserAvailabilityMap[] = data.map( (event) => { const availabilities: Record = {}; Object.entries(event.availabilities).forEach( ([availability, users]) => { users.forEach((u) => (availabilities[u] = availability)); }, ); return { ...event, availabilities, }; }, ); return { items: eventAvailabilities, }; } else { return { items: [] }; } }, async sort({ items, sortDescriptor }) { return { items: items.sort((a, b) => { let cmp = 0; // if it is the date-column, convert to a date if (sortDescriptor.column === "date") { const first = a[sortDescriptor.column]; const second = b[sortDescriptor.column]; cmp = first < second ? -1 : 1; } if (sortDescriptor.direction === "descending") { cmp *= -1; } return cmp; }), }; }, }); // retrieve the availabilites and store them in a map useEffect(() => { (async () => { setAvailabilityMap( Object.fromEntries( (await getAvailabilities()).map((a) => [a.availabilityID, a]), ), ); })(); }, []); // eslint-disable-next-line react-hooks/exhaustive-deps useEffect(() => void events.reload(), [sinceDate]); function getAvailabilityById(availabilityID: number): Availability { return availabilityMap[availabilityID]; } function getKeyValue( event: EventWithUserAvailabilityMap, key: Key, ): React.ReactNode { switch (key) { case "date": return ( {event[key]} ); case "description": return (
{event[key]}
); case user?.userName ?? "me": const availability = parseInt(event.availabilities[key as string]); return ( ); default: if (event.availabilities[key as string] === undefined) { return ; } else { return ( {getAvailabilityById( parseInt(event.availabilities[key as string]), )} ); } } } const topContent = (
); return (

Availabilities

tr]:first:!shadow-border", }} onSortChange={events.sort} className="w-fit max-w-full" > {(task) => ( ["align"]} > {task.label} )} {(event) => ( {(columnKey) => ( {getKeyValue(event, columnKey)} )} )}
); }