added my assigned events overview
This commit is contained in:
@@ -14,7 +14,7 @@ export type EventData = BaseEvent & {
|
||||
tasks: TaskAssignment[];
|
||||
};
|
||||
|
||||
interface TaskAssignment {
|
||||
export interface TaskAssignment {
|
||||
taskID: number;
|
||||
taskName: string;
|
||||
userName: string | null;
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import AssignmentTable from "@/components/Event/AssignmentTable";
|
||||
import Event from "@/components/Event/Event";
|
||||
import { apiCall } from "@/lib";
|
||||
import { EventData } from "@/Zustand";
|
||||
import zustand, { EventData } from "@/Zustand";
|
||||
import { useAsyncList } from "@react-stately/data";
|
||||
|
||||
export default function MyEvents() {
|
||||
const user = zustand((state) => state.user);
|
||||
|
||||
const events = useAsyncList({
|
||||
async load() {
|
||||
const result = await apiCall<EventData[]>("GET", "events/user/assigned");
|
||||
@@ -26,8 +30,12 @@ export default function MyEvents() {
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{events.items.map((e) => e.date)}</h2>
|
||||
<div className="flex justify-center gap-4">
|
||||
{events.items.map((e) => (
|
||||
<Event key={e.eventID} event={e}>
|
||||
<AssignmentTable tasks={e.tasks} highlightUser={user?.userName} />
|
||||
</Event>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ export default function Overview() {
|
||||
|
||||
<h1 className="mb-4 text-center text-4xl">My Events</h1>
|
||||
<MyEvents />
|
||||
<h1 className="mb-4 text-center text-4xl">
|
||||
events that I don't have entered an availability yet
|
||||
<h1 className="mb-4 mt-8 text-center text-4xl">
|
||||
events that I don't have entered an availability yet
|
||||
</h1>
|
||||
<PengingEvents />
|
||||
</div>
|
||||
|
||||
116
client/src/app/assignments/VolunteerSelector.tsx
Normal file
116
client/src/app/assignments/VolunteerSelector.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import AvailabilityChip from "@/components/AvailabilityChip";
|
||||
import { TaskAssignment } from "@/Zustand";
|
||||
import { AddLarge } from "@carbon/icons-react";
|
||||
import {
|
||||
Button,
|
||||
Chip,
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
DropdownMenu,
|
||||
DropdownSection,
|
||||
DropdownTrigger,
|
||||
} from "@heroui/react";
|
||||
import { EventWithAvailabilities } from "./page";
|
||||
import { ReactElement } from "react";
|
||||
import { Availability } from "../admin/(availabilities)/AvailabilityEditor";
|
||||
import { apiCall, classNames } from "@/lib";
|
||||
|
||||
export default function VolunteerSelector({
|
||||
event,
|
||||
task,
|
||||
getAvailabilityById,
|
||||
onReloadRequest,
|
||||
}: {
|
||||
event: EventWithAvailabilities;
|
||||
task: TaskAssignment;
|
||||
getAvailabilityById: (availabilityID: number) => Availability;
|
||||
onReloadRequest: () => void;
|
||||
}) {
|
||||
async function sendVolunteerAssignment(
|
||||
eventID: number,
|
||||
taskID: number,
|
||||
userName: string,
|
||||
) {
|
||||
const result = await apiCall(
|
||||
"PUT",
|
||||
"events/assignments",
|
||||
{ eventID, taskID },
|
||||
userName,
|
||||
);
|
||||
|
||||
if (result.ok) {
|
||||
onReloadRequest();
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
onReloadRequest();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
{!!event.tasks.find((t) => t.taskID == task.taskID)?.userName ? (
|
||||
<Chip
|
||||
onClose={() =>
|
||||
removeVolunteerAssignment(event.eventID, task.taskID)
|
||||
}
|
||||
>
|
||||
{event.tasks.find((t) => t.taskID == task.taskID)?.userName}
|
||||
</Chip>
|
||||
) : (
|
||||
<Button isIconOnly size="sm" radius="md" variant="flat">
|
||||
<AddLarge className="mx-auto" />
|
||||
</Button>
|
||||
)}
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu
|
||||
onAction={(a) =>
|
||||
sendVolunteerAssignment(event.eventID, task.taskID, a as string)
|
||||
}
|
||||
>
|
||||
{Object.entries(event.availabilities).map(
|
||||
([availabilityId, volunteers], iAvailability, aAvailabilities) => (
|
||||
<DropdownSection
|
||||
key={availabilityId}
|
||||
showDivider={iAvailability < aAvailabilities.length - 1}
|
||||
classNames={{
|
||||
base: "flex flex-col justify-start",
|
||||
heading: "mx-auto",
|
||||
}}
|
||||
title={
|
||||
(
|
||||
<AvailabilityChip
|
||||
availability={getAvailabilityById(parseInt(availabilityId))}
|
||||
/>
|
||||
) as ReactElement & string
|
||||
}
|
||||
>
|
||||
{volunteers.map((v) => (
|
||||
<DropdownItem
|
||||
key={v}
|
||||
classNames={{
|
||||
base: "", // this empty class is needed, else some styles are applied
|
||||
title: classNames({
|
||||
"text-primary font-bold": v === task.userName,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
{v}
|
||||
</DropdownItem>
|
||||
))}
|
||||
</DropdownSection>
|
||||
),
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import { apiCall, getAvailabilities, getTasks } from "@/lib";
|
||||
import { EventData } from "@/Zustand";
|
||||
import {
|
||||
Add,
|
||||
AddLarge,
|
||||
Copy,
|
||||
Edit,
|
||||
NotAvailable,
|
||||
@@ -17,12 +16,6 @@ import {
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Chip,
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
DropdownMenu,
|
||||
DropdownSection,
|
||||
DropdownTrigger,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
@@ -38,11 +31,11 @@ import {
|
||||
Tooltip,
|
||||
} from "@heroui/react";
|
||||
import { useAsyncList } from "@react-stately/data";
|
||||
import React, { Key, ReactElement, useEffect, useState } from "react";
|
||||
import React, { Key, useEffect, useState } from "react";
|
||||
import { Availability } from "../admin/(availabilities)/AvailabilityEditor";
|
||||
import AvailabilityChip from "@/components/AvailabilityChip";
|
||||
import VolunteerSelector from "./VolunteerSelector";
|
||||
|
||||
type EventWithAvailabilities = EventData & {
|
||||
export type EventWithAvailabilities = EventData & {
|
||||
availabilities: Record<string, string[]>;
|
||||
};
|
||||
|
||||
@@ -137,35 +130,6 @@ export default function AdminPanel() {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -236,72 +200,16 @@ export default function AdminPanel() {
|
||||
);
|
||||
default:
|
||||
// only show the selector, if the task is needed for the event
|
||||
if (event.tasks?.some((t) => t.taskID == key)) {
|
||||
const task = event.tasks.find((t) => t.taskID == key);
|
||||
|
||||
if (!!task) {
|
||||
return (
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
{!!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">
|
||||
<AddLarge className="mx-auto" />
|
||||
</Button>
|
||||
)}
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu
|
||||
onAction={(a) =>
|
||||
sendVolunteerAssignment(
|
||||
event.eventID,
|
||||
key as number,
|
||||
a as string,
|
||||
)
|
||||
}
|
||||
>
|
||||
{Object.entries(event.availabilities).map(
|
||||
(
|
||||
[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
|
||||
}
|
||||
>
|
||||
{volunteers.map((v) => (
|
||||
<DropdownItem
|
||||
key={v}
|
||||
classNames={{
|
||||
base: "", // this empty class is needed, else some styles are applied
|
||||
}}
|
||||
>
|
||||
{v}
|
||||
</DropdownItem>
|
||||
))}
|
||||
</DropdownSection>
|
||||
),
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
<VolunteerSelector
|
||||
event={event}
|
||||
task={task}
|
||||
getAvailabilityById={getAvailabilityById}
|
||||
onReloadRequest={events.reload}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <NotAvailable className="mx-auto text-foreground-300" />;
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
import { classNames } from "@/lib";
|
||||
import { EventData } from "@/Zustand";
|
||||
|
||||
export default function AssignmentTable({
|
||||
tasks,
|
||||
highlightTask,
|
||||
highlightUser,
|
||||
}: {
|
||||
tasks: EventData["tasks"];
|
||||
highlightUser?: string;
|
||||
highlightTask?: string;
|
||||
}) {
|
||||
return (
|
||||
<table>
|
||||
<tbody>
|
||||
{tasks.map((task) => (
|
||||
<tr key={task.taskID}>
|
||||
<tr
|
||||
key={task.taskID}
|
||||
className={classNames({
|
||||
"text-danger":
|
||||
task.userName === highlightUser ||
|
||||
task.taskName === highlightTask,
|
||||
})}
|
||||
>
|
||||
<th className="pr-4 text-left">{task.taskName}</th>
|
||||
<td>
|
||||
{task.userName ?? (
|
||||
|
||||
Reference in New Issue
Block a user