changed names of database columns
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
@@ -1,17 +1,19 @@
|
||||
"use client";
|
||||
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { Task } from "./lib";
|
||||
import { Availability } from "./app/admin/(availabilities)/AvailabilityEditor";
|
||||
|
||||
export interface EventData {
|
||||
id: number;
|
||||
export interface BaseEvent {
|
||||
eventID: number;
|
||||
date: string;
|
||||
tasks: TaskAssignment[];
|
||||
description: string;
|
||||
}
|
||||
|
||||
export type EventData = BaseEvent & {
|
||||
tasks: TaskAssignment[];
|
||||
};
|
||||
|
||||
interface TaskAssignment {
|
||||
taskID: number;
|
||||
taskName: string;
|
||||
@@ -40,28 +42,27 @@ const initialState = {
|
||||
};
|
||||
|
||||
const zustand = create<Zustand>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
...initialState,
|
||||
reset: (newZustand) => {
|
||||
console.debug("reset");
|
||||
set({
|
||||
...initialState,
|
||||
...newZustand,
|
||||
});
|
||||
},
|
||||
patch: (patch) => set({ ...get(), ...patch }),
|
||||
}),
|
||||
{
|
||||
name: "golunteer-storage",
|
||||
partialize: (state) =>
|
||||
Object.fromEntries(
|
||||
Object.entries(state).filter(([key]) =>
|
||||
["user", "tasksList", "tasksMap"].includes(key),
|
||||
),
|
||||
),
|
||||
// persist(
|
||||
(set, get) => ({
|
||||
...initialState,
|
||||
reset: (newZustand) => {
|
||||
set({
|
||||
...initialState,
|
||||
...newZustand,
|
||||
});
|
||||
},
|
||||
),
|
||||
patch: (patch) => set({ ...get(), ...patch }),
|
||||
}),
|
||||
// {
|
||||
// name: "golunteer-storage",
|
||||
// partialize: (state) =>
|
||||
// Object.fromEntries(
|
||||
// Object.entries(state).filter(([key]) =>
|
||||
// ["user", "tasksList", "tasksMap"].includes(key),
|
||||
// ),
|
||||
// ),
|
||||
// },
|
||||
// ),
|
||||
);
|
||||
|
||||
export default zustand;
|
||||
|
||||
@@ -30,10 +30,7 @@ export default function Header({ sites }: { sites: SiteLink[] }) {
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const result = await apiCall<{ pendingEvents: number }>(
|
||||
"GET",
|
||||
"events/user/pending",
|
||||
);
|
||||
const result = await apiCall<number>("GET", "events/user/pending/count");
|
||||
|
||||
if (result.ok) {
|
||||
setPendingEvents(await result.json());
|
||||
|
||||
29
client/src/app/MyEvents.tsx
Normal file
29
client/src/app/MyEvents.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
"use client";
|
||||
|
||||
import { apiCall } from "@/lib";
|
||||
import { EventData } from "@/Zustand";
|
||||
import { useAsyncList } from "@react-stately/data";
|
||||
|
||||
export default function MyEvents() {
|
||||
const events = useAsyncList({
|
||||
async load() {
|
||||
const result = await apiCall<EventData[]>("GET", "events/user/assigned");
|
||||
|
||||
if (result.ok) {
|
||||
return {
|
||||
items: await result.json(),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
items: [],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{events.items.map((e) => e.date)}</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,33 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import { Add } from "@carbon/icons-react";
|
||||
import { useState } from "react";
|
||||
import AddEvent from "../components/Event/AddEvent";
|
||||
import { Button } from "@heroui/react";
|
||||
|
||||
export default function EventVolunteer() {
|
||||
const [showAddItemDialogue, setShowAddItemDialogue] = useState(false);
|
||||
import MyEvents from "./MyEvents";
|
||||
import PengingEvents from "./PendingEvents";
|
||||
|
||||
export default function Overview() {
|
||||
return (
|
||||
<div className="relative flex-1">
|
||||
<h2 className="mb-4 text-center text-4xl">Overview</h2>
|
||||
<div className="flex flex-wrap justify-center gap-4"></div>
|
||||
|
||||
<Button
|
||||
color="primary"
|
||||
isIconOnly
|
||||
radius="full"
|
||||
className="absolute bottom-0 right-0"
|
||||
onPress={() => setShowAddItemDialogue(true)}
|
||||
>
|
||||
<Add size={32} />
|
||||
</Button>
|
||||
|
||||
<AddEvent
|
||||
className="border-2 border-accent-3"
|
||||
isOpen={showAddItemDialogue}
|
||||
onOpenChange={setShowAddItemDialogue}
|
||||
/>
|
||||
<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>
|
||||
<PengingEvents />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
77
client/src/app/PendingEvents.tsx
Normal file
77
client/src/app/PendingEvents.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
"use client";
|
||||
|
||||
import AvailabilityChip from "@/components/AvailabilityChip";
|
||||
import Event from "@/components/Event/Event";
|
||||
import { apiCall, getAvailabilities } from "@/lib";
|
||||
import { BaseEvent } from "@/Zustand";
|
||||
import { Select, SelectItem } from "@heroui/react";
|
||||
import { useAsyncList } from "@react-stately/data";
|
||||
|
||||
type EventAvailability = BaseEvent & {
|
||||
availability: number;
|
||||
};
|
||||
|
||||
export default function PengingEvents() {
|
||||
// get the events the user hasn't yet inserted his availability for
|
||||
const events = useAsyncList({
|
||||
async load() {
|
||||
const result = await apiCall<EventAvailability[]>(
|
||||
"GET",
|
||||
"events/user/pending",
|
||||
);
|
||||
|
||||
if (result.ok) {
|
||||
return {
|
||||
items: await result.json(),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
items: [],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// the individual, selectable availabilities
|
||||
const availabilities = useAsyncList({
|
||||
async load() {
|
||||
return {
|
||||
items: (await getAvailabilities()).filter((a) => a.enabled),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex justify-center gap-4">
|
||||
{events.items.map((e) => (
|
||||
<Event key={e.eventID} event={e}>
|
||||
<Select
|
||||
items={availabilities.items}
|
||||
label="Availability"
|
||||
variant="bordered"
|
||||
className="mt-auto"
|
||||
isMultiline
|
||||
renderValue={(availability) => (
|
||||
<div>
|
||||
{availability.map((a) =>
|
||||
!!a.data ? (
|
||||
<AvailabilityChip key={a.key} availability={a.data} />
|
||||
) : null,
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{(availability) => (
|
||||
<SelectItem
|
||||
key={availability.availabilityID}
|
||||
textValue={availability.availabilityName}
|
||||
>
|
||||
<AvailabilityChip availability={availability} />
|
||||
</SelectItem>
|
||||
)}
|
||||
</Select>
|
||||
</Event>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -40,7 +40,7 @@ export default function Availabilities() {
|
||||
|
||||
switch (sortDescriptor.column) {
|
||||
case "text":
|
||||
cmp = a.name.localeCompare(b.name);
|
||||
cmp = a.availabilityName.localeCompare(b.availabilityName);
|
||||
break;
|
||||
case "enabled":
|
||||
if (a.enabled && !b.enabled) {
|
||||
@@ -78,9 +78,11 @@ export default function Availabilities() {
|
||||
availabilities.reload();
|
||||
}
|
||||
|
||||
async function sendDeleteAvailability(id: number | undefined) {
|
||||
if (id !== undefined) {
|
||||
const result = await apiCall("DELETE", "availabilities", { id });
|
||||
async function sendDeleteAvailability(availabilityID: number | undefined) {
|
||||
if (availabilityID !== undefined) {
|
||||
const result = await apiCall("DELETE", "availabilities", {
|
||||
availabilityID,
|
||||
});
|
||||
|
||||
if (result.ok) {
|
||||
reload();
|
||||
@@ -133,7 +135,7 @@ export default function Availabilities() {
|
||||
</TableHeader>
|
||||
<TableBody items={availabilities.items}>
|
||||
{(availability) => (
|
||||
<TableRow key={availability.name}>
|
||||
<TableRow key={availability.availabilityName}>
|
||||
<TableCell>
|
||||
<AvailabilityChip availability={availability} />
|
||||
</TableCell>
|
||||
@@ -190,7 +192,9 @@ export default function Availabilities() {
|
||||
!isOpen ? setDeleteAvailability(undefined) : null
|
||||
}
|
||||
itemName="Availability"
|
||||
onDelete={() => sendDeleteAvailability(deleteAvailability?.id)}
|
||||
onDelete={() =>
|
||||
sendDeleteAvailability(deleteAvailability?.availabilityID)
|
||||
}
|
||||
>
|
||||
{!!deleteAvailability ? (
|
||||
<>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import ColorSelector from "@/components/Colorselector";
|
||||
import { AllString } from "@/lib";
|
||||
import {
|
||||
Checkbox,
|
||||
Form,
|
||||
@@ -12,9 +13,9 @@ import {
|
||||
import React, { FormEvent, useEffect, useState } from "react";
|
||||
|
||||
export interface Availability {
|
||||
name: string;
|
||||
availabilityName: string;
|
||||
color: string;
|
||||
id: number | undefined;
|
||||
availabilityID: number | undefined;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
@@ -26,7 +27,7 @@ export default function AvailabilityEditor(props: {
|
||||
onOpenChange?: (isOpen: boolean) => void;
|
||||
onSubmit?: (e: Availability) => void;
|
||||
}) {
|
||||
const [name, setName] = useState(props.value?.name ?? "");
|
||||
const [name, setName] = useState(props.value?.availabilityName ?? "");
|
||||
const [color, setColor] = useState(props.value?.color ?? "Red");
|
||||
const [enabled, setEnabled] = useState(props.value?.enabled ?? true);
|
||||
|
||||
@@ -40,15 +41,13 @@ export default function AvailabilityEditor(props: {
|
||||
}, [props.isOpen]);
|
||||
|
||||
function submit(e: FormEvent<HTMLFormElement>) {
|
||||
const formData = Object.fromEntries(new FormData(e.currentTarget)) as {
|
||||
name: string;
|
||||
color: string;
|
||||
enabled: string;
|
||||
};
|
||||
const formData = Object.fromEntries(
|
||||
new FormData(e.currentTarget),
|
||||
) as AllString<Exclude<Availability, "availabilityID">>;
|
||||
|
||||
props.onSubmit?.({
|
||||
...formData,
|
||||
id: props.value?.id,
|
||||
availabilityID: props.value?.availabilityID,
|
||||
enabled: formData.enabled == "true",
|
||||
});
|
||||
}
|
||||
@@ -77,7 +76,7 @@ export default function AvailabilityEditor(props: {
|
||||
<Input
|
||||
value={name}
|
||||
onValueChange={setName}
|
||||
name="name"
|
||||
name="availabilityName"
|
||||
label="Name"
|
||||
isRequired
|
||||
variant="bordered"
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function EditAvailability(props: {
|
||||
|
||||
return (
|
||||
<AvailabilityEditor
|
||||
key={props.value?.id}
|
||||
key={props.value?.availabilityID}
|
||||
header={
|
||||
<>
|
||||
Edit Availability{" "}
|
||||
|
||||
@@ -20,12 +20,12 @@ export default function EditTask(props: {
|
||||
|
||||
return (
|
||||
<TaskEditor
|
||||
key={props.value?.name}
|
||||
key={props.value?.taskName}
|
||||
header={
|
||||
<>
|
||||
Edit Task{" "}
|
||||
<span className="font-numbers font-normal italic">
|
||||
"{props.value?.name}"
|
||||
"{props.value?.taskName}"
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Task } from "@/lib";
|
||||
import { AllString, Task } from "@/lib";
|
||||
import {
|
||||
Checkbox,
|
||||
Form,
|
||||
@@ -19,7 +19,7 @@ export default function TaskEditor(props: {
|
||||
onOpenChange?: (isOpen: boolean) => void;
|
||||
onSubmit?: (e: Task) => void;
|
||||
}) {
|
||||
const [name, setName] = useState(props.value?.name ?? "");
|
||||
const [name, setName] = useState(props.value?.taskName ?? "");
|
||||
const [enabled, setEnabled] = useState(props.value?.enabled ?? true);
|
||||
|
||||
// clear the inputs on closing
|
||||
@@ -31,14 +31,13 @@ export default function TaskEditor(props: {
|
||||
}, [props.isOpen]);
|
||||
|
||||
function submit(e: FormEvent<HTMLFormElement>) {
|
||||
const formData = Object.fromEntries(new FormData(e.currentTarget)) as {
|
||||
name: string;
|
||||
enabled: string;
|
||||
};
|
||||
const formData = Object.fromEntries(
|
||||
new FormData(e.currentTarget),
|
||||
) as AllString<Exclude<Task, "taskID">>;
|
||||
|
||||
props.onSubmit?.({
|
||||
...formData,
|
||||
id: props.value?.id,
|
||||
taskID: props.value?.taskID,
|
||||
enabled: formData.enabled == "true",
|
||||
});
|
||||
}
|
||||
@@ -68,7 +67,7 @@ export default function TaskEditor(props: {
|
||||
<Input
|
||||
value={name}
|
||||
onValueChange={setName}
|
||||
name="name"
|
||||
name="taskName"
|
||||
label="Name"
|
||||
isRequired
|
||||
variant="bordered"
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function Tasks() {
|
||||
|
||||
switch (sortDescriptor.column) {
|
||||
case "text":
|
||||
cmp = a.name.localeCompare(b.name);
|
||||
cmp = a.taskName.localeCompare(b.taskName);
|
||||
break;
|
||||
case "enabled":
|
||||
if (a.enabled && !b.enabled) {
|
||||
@@ -76,9 +76,9 @@ export default function Tasks() {
|
||||
tasks.reload();
|
||||
}
|
||||
|
||||
async function sendDeleteTask(id: number | undefined) {
|
||||
if (id !== undefined) {
|
||||
const result = await apiCall("DELETE", "tasks", { id });
|
||||
async function sendDeleteTask(taskID: number | undefined) {
|
||||
if (taskID !== undefined) {
|
||||
const result = await apiCall("DELETE", "tasks", { taskID });
|
||||
|
||||
if (result.ok) {
|
||||
tasks.reload();
|
||||
@@ -130,8 +130,8 @@ export default function Tasks() {
|
||||
</TableHeader>
|
||||
<TableBody items={tasks.items}>
|
||||
{(task) => (
|
||||
<TableRow key={task.id}>
|
||||
<TableCell>{task.name}</TableCell>
|
||||
<TableRow key={task.taskID}>
|
||||
<TableCell>{task.taskName}</TableCell>
|
||||
<TableCell>
|
||||
<Checkbox isSelected={task.enabled} />
|
||||
</TableCell>
|
||||
@@ -181,13 +181,13 @@ export default function Tasks() {
|
||||
isOpen={!!deleteTask}
|
||||
onOpenChange={(isOpen) => (!isOpen ? setDeleteTask(undefined) : null)}
|
||||
itemName="Task"
|
||||
onDelete={() => sendDeleteTask(deleteTask?.id)}
|
||||
onDelete={() => sendDeleteTask(deleteTask?.taskID)}
|
||||
>
|
||||
{!!deleteTask ? (
|
||||
<>
|
||||
The task{" "}
|
||||
<span className="font-numbers text-accent-1">
|
||||
{deleteTask.name}
|
||||
{deleteTask.taskName}
|
||||
</span>{" "}
|
||||
will be deleted.
|
||||
</>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { classNames, validatePassword as validatePassword } from "@/lib";
|
||||
import {
|
||||
AllString,
|
||||
classNames,
|
||||
validatePassword as validatePassword,
|
||||
} from "@/lib";
|
||||
import zustand, { User, UserAddModify } from "@/Zustand";
|
||||
import {
|
||||
Checkbox,
|
||||
@@ -29,11 +33,9 @@ export default function UserEditor(props: {
|
||||
|
||||
// update the user in the backend
|
||||
async function submit(e: FormEvent<HTMLFormElement>) {
|
||||
const formData = Object.fromEntries(new FormData(e.currentTarget)) as {
|
||||
userName: string;
|
||||
password: string;
|
||||
admin: string;
|
||||
};
|
||||
const formData = Object.fromEntries(
|
||||
new FormData(e.currentTarget),
|
||||
) as AllString<UserAddModify>;
|
||||
|
||||
const data = {
|
||||
...formData,
|
||||
|
||||
@@ -62,8 +62,8 @@ export default function AdminPanel() {
|
||||
...tasks
|
||||
.filter((task) => task.enabled)
|
||||
.map((task) => ({
|
||||
label: task.name,
|
||||
key: task.id ?? -1,
|
||||
label: task.taskName,
|
||||
key: task.taskID ?? -1,
|
||||
align: "center",
|
||||
})),
|
||||
{ key: "actions", label: "Action", align: "center" },
|
||||
@@ -116,7 +116,9 @@ export default function AdminPanel() {
|
||||
// send a delete request to the backend and close the popup on success
|
||||
async function sendDeleteEvent() {
|
||||
if (deleteEvent !== undefined) {
|
||||
const result = await apiCall("DELETE", "event", { id: deleteEvent.id });
|
||||
const result = await apiCall("DELETE", "event", {
|
||||
eventID: deleteEvent.eventID,
|
||||
});
|
||||
|
||||
if (result.ok) {
|
||||
// store the received events
|
||||
@@ -262,7 +264,7 @@ export default function AdminPanel() {
|
||||
</TableHeader>
|
||||
<TableBody items={events.items} emptyContent={"No events scheduled"}>
|
||||
{(event) => (
|
||||
<TableRow key={event.id}>
|
||||
<TableRow key={event.eventID}>
|
||||
{(columnKey) => (
|
||||
<TableCell>{getKeyValue(event, columnKey)}</TableCell>
|
||||
)}
|
||||
|
||||
@@ -15,13 +15,11 @@ export default function Events() {
|
||||
|
||||
const events = useAsyncList<EventData>({
|
||||
async load() {
|
||||
const result = await apiCall("GET", "events/assignments");
|
||||
const result = await apiCall<EventData[]>("GET", "events/assignments");
|
||||
|
||||
if (result.ok) {
|
||||
const data = await result.json();
|
||||
|
||||
console.debug(data);
|
||||
|
||||
return {
|
||||
items: data,
|
||||
};
|
||||
@@ -62,6 +60,7 @@ export default function Events() {
|
||||
className="border-2 border-accent-3"
|
||||
isOpen={showAddItemDialogue}
|
||||
onOpenChange={setShowAddItemDialogue}
|
||||
onSuccess={events.reload}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import EventVolunteer from "./Overview";
|
||||
import Overview from "./Overview";
|
||||
|
||||
export default function Home() {
|
||||
return <EventVolunteer />;
|
||||
return <Overview />;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function AvailabilityChip({
|
||||
}}
|
||||
className={className}
|
||||
>
|
||||
{availability.name}
|
||||
{availability.availabilityName}
|
||||
</Chip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function EditEvent(props: {
|
||||
return (
|
||||
<EventEditor
|
||||
value={props.value}
|
||||
key={props.value?.id}
|
||||
key={props.value?.eventID}
|
||||
header="Edit Event"
|
||||
isOpen={props.isOpen}
|
||||
onOpenChange={props.onOpenChange}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import LocalDate from "../LocalDate";
|
||||
import { EventData } from "@/Zustand";
|
||||
import { BaseEvent } from "@/Zustand";
|
||||
import { Card, CardBody, CardHeader, Divider } from "@heroui/react";
|
||||
import React from "react";
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function Event({
|
||||
event,
|
||||
children,
|
||||
}: {
|
||||
event: EventData;
|
||||
event: BaseEvent;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
|
||||
@@ -18,10 +18,12 @@ import {
|
||||
Spinner,
|
||||
Textarea,
|
||||
} from "@heroui/react";
|
||||
import zustand, { EventData } from "@/Zustand";
|
||||
import { EventData } from "@/Zustand";
|
||||
import { useAsyncList } from "@react-stately/data";
|
||||
import { getTasks } from "@/lib";
|
||||
|
||||
export interface EventSubmitData {
|
||||
id: number;
|
||||
eventID: number;
|
||||
date: string;
|
||||
description: string;
|
||||
tasks: number[];
|
||||
@@ -47,12 +49,19 @@ export default function EventEditor(props: {
|
||||
const [eventTasks, setEventTasks] = useState<string[]>(
|
||||
props.value?.tasks.map((k) => k.taskID.toString()) ?? [],
|
||||
);
|
||||
const tasks = zustand((state) => state.tasks);
|
||||
|
||||
const tasks = useAsyncList({
|
||||
async load() {
|
||||
return {
|
||||
items: await getTasks(),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
function onSubmit() {
|
||||
if (!!props.onSubmit && !!date) {
|
||||
props.onSubmit({
|
||||
id: props.value?.id ?? -1,
|
||||
eventID: props.value?.eventID ?? -1,
|
||||
date: date.toAbsoluteString(),
|
||||
description,
|
||||
tasks: eventTasks.map((t) => parseInt(t)),
|
||||
@@ -109,12 +118,12 @@ export default function EventEditor(props: {
|
||||
}
|
||||
>
|
||||
{!!tasks ? (
|
||||
tasks
|
||||
tasks.items
|
||||
?.filter((task) => task.enabled)
|
||||
.map((task) => (
|
||||
<div key={task.id}>
|
||||
<Checkbox value={task.id?.toString()}>
|
||||
{task.name}
|
||||
<div key={task.taskID}>
|
||||
<Checkbox value={task.taskID?.toString()}>
|
||||
{task.taskName}
|
||||
</Checkbox>
|
||||
</div>
|
||||
))
|
||||
|
||||
@@ -2,30 +2,32 @@ import { DateFormatter as IntlDateFormatter } from "@internationalized/date";
|
||||
import zustand from "./Zustand";
|
||||
import { Availability } from "./app/admin/(availabilities)/AvailabilityEditor";
|
||||
|
||||
export type AllString<T> = { [K in keyof T]: string };
|
||||
|
||||
type QueryParams = Record<string, string | { toString(): string }>;
|
||||
|
||||
export type APICallResult<T extends object> = Response & {
|
||||
export type APICallResult<T> = Omit<Response, "json"> & {
|
||||
json: () => Promise<T>;
|
||||
};
|
||||
|
||||
export async function apiCall<K extends object>(
|
||||
export async function apiCall<K>(
|
||||
method: "GET",
|
||||
api: string,
|
||||
query?: QueryParams,
|
||||
): Promise<APICallResult<K>>;
|
||||
export async function apiCall<K extends object>(
|
||||
export async function apiCall<K>(
|
||||
method: "POST" | "PATCH" | "PUT",
|
||||
api: string,
|
||||
query?: QueryParams,
|
||||
body?: object,
|
||||
): Promise<APICallResult<K>>;
|
||||
export async function apiCall<K extends object>(
|
||||
export async function apiCall<K>(
|
||||
method: "DELETE",
|
||||
api: string,
|
||||
query?: QueryParams,
|
||||
body?: object,
|
||||
): Promise<APICallResult<K>>;
|
||||
export async function apiCall<K extends object>(
|
||||
export async function apiCall<K>(
|
||||
method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE",
|
||||
api: string,
|
||||
query?: QueryParams,
|
||||
@@ -96,8 +98,8 @@ export function validatePassword(password: string): string[] {
|
||||
}
|
||||
|
||||
export interface Task {
|
||||
id: number | undefined;
|
||||
name: string;
|
||||
taskID: number | undefined;
|
||||
taskName: string;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
@@ -105,7 +107,7 @@ export async function getTask(name: string): Promise<Task | undefined> {
|
||||
// get the tasks
|
||||
const tasks = await getTasks();
|
||||
|
||||
return tasks.find((t) => t.name === name);
|
||||
return tasks.find((t) => t.taskName === name);
|
||||
}
|
||||
|
||||
export async function getTasks(): Promise<Task[]> {
|
||||
@@ -136,7 +138,7 @@ export async function getAvailabilities(): Promise<Availability[]> {
|
||||
if (!!state.availabilities) {
|
||||
return state.availabilities;
|
||||
} else {
|
||||
const result = await apiCall<Task[]>("GET", "availabilities");
|
||||
const result = await apiCall<Availability[]>("GET", "availabilities");
|
||||
|
||||
if (result.ok) {
|
||||
const tasks = await result.json();
|
||||
|
||||
Reference in New Issue
Block a user