implemented modifing of tasks of an event
This commit is contained in:
@@ -19,7 +19,7 @@ export default function Account() {
|
||||
async function changePassword(e: FormEvent<HTMLFormElement>) {
|
||||
const data = Object.fromEntries(new FormData(e.currentTarget));
|
||||
|
||||
const result = await apiCall("PATCH", "users/password", undefined, data);
|
||||
const result = await apiCall("PUT", "users/password", undefined, data);
|
||||
|
||||
if (result.ok) {
|
||||
setPassword("");
|
||||
@@ -28,9 +28,9 @@ export default function Account() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="text-center text-4xl">Account</h2>
|
||||
<h2 className="mb-4 text-center text-4xl">Account</h2>
|
||||
<div>
|
||||
<Card className="max-w-md">
|
||||
<Card className="mx-auto max-w-md bg-accent-5" shadow="none">
|
||||
<CardHeader>
|
||||
<h3 className="text-2xl">Change Password</h3>
|
||||
</CardHeader>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import AddEvent from "@/components/Event/AddEvent";
|
||||
import EditEvent, { EventSubmitData } from "@/components/Event/EditEvent";
|
||||
import LocalDate from "@/components/LocalDate";
|
||||
import { apiCall, getTasks } from "@/lib";
|
||||
import { EventData } from "@/Zustand";
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
Copy,
|
||||
Edit,
|
||||
NotAvailable,
|
||||
Renew,
|
||||
TrashCan,
|
||||
} from "@carbon/icons-react";
|
||||
import {
|
||||
@@ -59,6 +61,10 @@ function availability2Color(availability?: Availability) {
|
||||
}
|
||||
|
||||
export default function AdminPanel() {
|
||||
const [showAddEvent, setShowAddEvent] = useState(false);
|
||||
const [editEvent, setEditEvent] = useState<EventData | undefined>();
|
||||
const [deleteEvent, setDeleteEvent] = useState<EventData | undefined>();
|
||||
|
||||
// get the available tasks and craft them into the headers
|
||||
const headers = useAsyncList({
|
||||
async load() {
|
||||
@@ -115,14 +121,14 @@ export default function AdminPanel() {
|
||||
});
|
||||
|
||||
// send a delete request to the backend and close the popup on success
|
||||
async function deleteEvent(eventId: number) {
|
||||
const result = await apiCall("DELETE", "event", { id: eventId });
|
||||
async function sendDeleteEvent() {
|
||||
if (deleteEvent !== undefined) {
|
||||
const result = await apiCall("DELETE", "event", { id: deleteEvent.id });
|
||||
|
||||
if (result.ok) {
|
||||
// store the received events
|
||||
events.reload();
|
||||
|
||||
setShowDeleteConfirm(false);
|
||||
if (result.ok) {
|
||||
// store the received events
|
||||
events.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,12 +147,20 @@ export default function AdminPanel() {
|
||||
</LocalDate>
|
||||
);
|
||||
case "description":
|
||||
return <span className="whitespace-pre-wrap italic">{event[key]}</span>;
|
||||
return (
|
||||
<div className="max-w-32 whitespace-pre-wrap italic">
|
||||
{event[key]}
|
||||
</div>
|
||||
);
|
||||
case "actions":
|
||||
return (
|
||||
<div className="flex justify-end">
|
||||
<ButtonGroup isIconOnly variant="light" size="sm">
|
||||
<Button>
|
||||
<Button
|
||||
onPress={() => {
|
||||
setEditEvent(event);
|
||||
}}
|
||||
>
|
||||
<Tooltip content="Edit event">
|
||||
<Edit />
|
||||
</Tooltip>
|
||||
@@ -159,8 +173,7 @@ export default function AdminPanel() {
|
||||
<Button
|
||||
color="danger"
|
||||
onPress={() => {
|
||||
setActiveEvent(event);
|
||||
setShowDeleteConfirm(true);
|
||||
setDeleteEvent(event);
|
||||
}}
|
||||
>
|
||||
<Tooltip content="Delete event">
|
||||
@@ -203,33 +216,6 @@ export default function AdminPanel() {
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
// <Select
|
||||
// aria-label={`User selection for task ${key} and event ${event.date}`}
|
||||
// variant="underlined"
|
||||
// fullWidth
|
||||
// selectedKeys={new Set([event.tasks[key as string] ?? ""])}
|
||||
// classNames={{
|
||||
// popoverContent: "w-fit",
|
||||
// value: "mr-6",
|
||||
// label: "mr-6",
|
||||
// }}
|
||||
// className="[&_*]:overflow-visible"
|
||||
// >
|
||||
// {Object.entries(event.availabilities).map(
|
||||
// ([volunteer, availability]) => (
|
||||
// <SelectItem
|
||||
// key={volunteer}
|
||||
// // color={availability2Color(availability)}
|
||||
// className={[
|
||||
// // "text-" + availability2Color(availability),
|
||||
// // availability2Tailwind(availability),
|
||||
// ].join(" ")}
|
||||
// >
|
||||
// {volunteer} ({availability})
|
||||
// </SelectItem>
|
||||
// ),
|
||||
// )}
|
||||
// </Select>
|
||||
);
|
||||
} else {
|
||||
return <NotAvailable className="mx-auto text-foreground-300" />;
|
||||
@@ -237,9 +223,16 @@ export default function AdminPanel() {
|
||||
}
|
||||
}
|
||||
|
||||
const [showAddEvent, setShowAddEvent] = useState(false);
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||
const [activeEvent, setActiveEvent] = useState<EventData | undefined>();
|
||||
async function updateEvent(data: EventSubmitData) {
|
||||
const result = await apiCall("PATCH", "events", undefined, data);
|
||||
|
||||
if (result.ok) {
|
||||
// clear the selected-event to hide the modal
|
||||
setEditEvent(undefined);
|
||||
|
||||
events.reload();
|
||||
}
|
||||
}
|
||||
|
||||
const topContent = (
|
||||
<div>
|
||||
@@ -271,7 +264,7 @@ export default function AdminPanel() {
|
||||
th: "font-subheadline text-xl text-accent-1 bg-transparent ",
|
||||
thead: "[&>tr]:first:!shadow-border",
|
||||
}}
|
||||
className="w-fit"
|
||||
className="w-fit max-w-full"
|
||||
>
|
||||
<TableHeader columns={headers.items}>
|
||||
{(task) => (
|
||||
@@ -301,45 +294,62 @@ export default function AdminPanel() {
|
||||
onSuccess={() => events.reload()}
|
||||
/>
|
||||
|
||||
{activeEvent !== undefined ? (
|
||||
<Modal
|
||||
isOpen={showDeleteConfirm}
|
||||
onOpenChange={setShowDeleteConfirm}
|
||||
shadow={"none" as "sm"}
|
||||
backdrop="blur"
|
||||
className="bg-accent-5"
|
||||
>
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<h1 className="text-2xl">Confirm event deletion</h1>
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
The event{" "}
|
||||
<span className="font-numbers text-accent-1">
|
||||
<LocalDate options={{ dateStyle: "long", timeStyle: "short" }}>
|
||||
{activeEvent.date}
|
||||
</LocalDate>
|
||||
</span>{" "}
|
||||
will be deleted.
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
startContent={<TrashCan />}
|
||||
color="danger"
|
||||
onPress={() => deleteEvent(activeEvent.id)}
|
||||
>
|
||||
Delete event
|
||||
</Button>
|
||||
<Button
|
||||
variant="bordered"
|
||||
onPress={() => setShowDeleteConfirm(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
) : null}
|
||||
<EditEvent
|
||||
isOpen={editEvent !== undefined}
|
||||
onOpenChange={(isOpen) => (!isOpen ? setEditEvent(undefined) : null)}
|
||||
onSubmit={updateEvent}
|
||||
initialState={editEvent}
|
||||
footer={
|
||||
<Button
|
||||
color="primary"
|
||||
radius="full"
|
||||
startContent={<Renew />}
|
||||
type="submit"
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
Edit Event
|
||||
</EditEvent>
|
||||
|
||||
<Modal
|
||||
isOpen={!!deleteEvent}
|
||||
onOpenChange={(isOpen) => (!isOpen ? setDeleteEvent(undefined) : null)}
|
||||
shadow={"none" as "sm"}
|
||||
backdrop="blur"
|
||||
className="bg-accent-5"
|
||||
>
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<h1 className="text-2xl">Confirm event deletion</h1>
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
The event{" "}
|
||||
<span className="font-numbers text-accent-1">
|
||||
<LocalDate options={{ dateStyle: "long", timeStyle: "short" }}>
|
||||
{deleteEvent?.date}
|
||||
</LocalDate>
|
||||
</span>{" "}
|
||||
will be deleted.
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
variant="bordered"
|
||||
onPress={() => setDeleteEvent(undefined)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
startContent={<TrashCan />}
|
||||
color="danger"
|
||||
onPress={() => sendDeleteEvent()}
|
||||
>
|
||||
Delete event
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,33 +1,7 @@
|
||||
import { useEffect, useReducer, useState } from "react";
|
||||
import { Add } from "@carbon/icons-react";
|
||||
import zustand from "../../Zustand";
|
||||
import { getLocalTimeZone, now, ZonedDateTime } from "@internationalized/date";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
DatePicker,
|
||||
Form,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Spinner,
|
||||
Textarea,
|
||||
} from "@nextui-org/react";
|
||||
import { apiCall, getTasks, Task } from "@/lib";
|
||||
|
||||
interface state {
|
||||
date: ZonedDateTime;
|
||||
description: string;
|
||||
tasks: string[];
|
||||
}
|
||||
|
||||
interface dispatchAction {
|
||||
action: "set" | "reset";
|
||||
value?: Partial<state>;
|
||||
}
|
||||
import { Button } from "@nextui-org/react";
|
||||
import EditEvent, { EventSubmitData } from "./EditEvent";
|
||||
import { apiCall } from "@/lib";
|
||||
import { AddLarge } from "@carbon/icons-react";
|
||||
|
||||
export default function AddEvent(props: {
|
||||
className?: string;
|
||||
@@ -35,138 +9,32 @@ export default function AddEvent(props: {
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
onSuccess?: () => void;
|
||||
}) {
|
||||
// initial state for the inputs
|
||||
const initialState: state = {
|
||||
date: now(getLocalTimeZone()),
|
||||
description: "",
|
||||
tasks: [],
|
||||
};
|
||||
|
||||
// handle state dispatches
|
||||
function reducer(state: state, action: dispatchAction): state {
|
||||
if (action.action === "reset") {
|
||||
return initialState;
|
||||
} else {
|
||||
return { ...state, ...action.value };
|
||||
}
|
||||
}
|
||||
const [state, dispatchState] = useReducer(reducer, initialState);
|
||||
const [tasks, setTasks] = useState<Record<number, Task>>({});
|
||||
|
||||
// get the available tasks
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setTasks(await getTasks());
|
||||
})();
|
||||
}, []);
|
||||
|
||||
// sends the addEvent request to the backend
|
||||
async function addEvent() {
|
||||
const data = {
|
||||
...state,
|
||||
tasks: state.tasks.map((task) => parseInt(task)),
|
||||
date: state.date.toAbsoluteString().slice(0, -1),
|
||||
};
|
||||
|
||||
async function addEvent(data: EventSubmitData) {
|
||||
const result = await apiCall("POST", "events", undefined, data);
|
||||
|
||||
if (result.ok) {
|
||||
zustand.getState().setEvents(await result.json());
|
||||
|
||||
props.onOpenChange(false);
|
||||
|
||||
props.onSuccess?.();
|
||||
}
|
||||
}
|
||||
|
||||
// reset the state when the modal gets closed
|
||||
useEffect(() => {
|
||||
if (!props.isOpen) {
|
||||
dispatchState({ action: "reset" });
|
||||
}
|
||||
}, [props.isOpen]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={props.isOpen}
|
||||
shadow={"none" as "sm"} // somehow "none" isn't allowed
|
||||
onOpenChange={props.onOpenChange}
|
||||
backdrop="blur"
|
||||
classNames={{
|
||||
base: "bg-accent-5 ",
|
||||
}}
|
||||
<EditEvent
|
||||
{...props}
|
||||
onSubmit={(data) => void addEvent(data)}
|
||||
footer={
|
||||
<Button
|
||||
color="primary"
|
||||
radius="full"
|
||||
startContent={<AddLarge />}
|
||||
type="submit"
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Form
|
||||
validationBehavior="native"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
void addEvent();
|
||||
}}
|
||||
>
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<h1 className="text-center text-2xl">Add Event</h1>
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<DatePicker
|
||||
isRequired
|
||||
label="Event date"
|
||||
name="date"
|
||||
variant="bordered"
|
||||
hideTimeZone
|
||||
granularity="minute"
|
||||
value={state.date}
|
||||
onChange={(dt) =>
|
||||
!!dt
|
||||
? dispatchState({ action: "set", value: { date: dt } })
|
||||
: null
|
||||
}
|
||||
/>
|
||||
<Textarea
|
||||
variant="bordered"
|
||||
placeholder="Description"
|
||||
name="description"
|
||||
value={state.description}
|
||||
onValueChange={(s) =>
|
||||
dispatchState({ action: "set", value: { description: s } })
|
||||
}
|
||||
/>
|
||||
<CheckboxGroup
|
||||
value={state.tasks}
|
||||
name="tasks"
|
||||
onValueChange={(s) =>
|
||||
dispatchState({ action: "set", value: { tasks: s } })
|
||||
}
|
||||
validate={(value) =>
|
||||
value.length > 0 ? true : "Atleast one task must be selected"
|
||||
}
|
||||
>
|
||||
{tasks !== undefined ? (
|
||||
Object.entries(tasks)
|
||||
.filter(([, task]) => !task.disabled)
|
||||
.map(([id, task]) => (
|
||||
<div key={id}>
|
||||
<Checkbox value={id}>{task.text}</Checkbox>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<Spinner label="Loading" />
|
||||
)}
|
||||
</CheckboxGroup>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
color="primary"
|
||||
radius="full"
|
||||
startContent={<Add size={32} />}
|
||||
type="submit"
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Form>
|
||||
</Modal>
|
||||
Add Event
|
||||
</EditEvent>
|
||||
);
|
||||
}
|
||||
|
||||
202
client/src/components/Event/EditEvent.tsx
Normal file
202
client/src/components/Event/EditEvent.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
import React, { useEffect, useReducer, useState } from "react";
|
||||
import {
|
||||
getLocalTimeZone,
|
||||
now,
|
||||
parseDateTime,
|
||||
toZoned,
|
||||
ZonedDateTime,
|
||||
} from "@internationalized/date";
|
||||
import {
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
DatePicker,
|
||||
Form,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Spinner,
|
||||
Textarea,
|
||||
} from "@nextui-org/react";
|
||||
import { getTasks, Task } from "@/lib";
|
||||
import { EventData } from "@/Zustand";
|
||||
|
||||
export interface EventSubmitData {
|
||||
id: number;
|
||||
date: string;
|
||||
description: string;
|
||||
tasks: number[];
|
||||
}
|
||||
|
||||
interface State {
|
||||
date: ZonedDateTime;
|
||||
description: string;
|
||||
tasks: string[];
|
||||
}
|
||||
|
||||
export default function EditEvent(props: {
|
||||
children: React.ReactNode;
|
||||
footer: React.ReactNode;
|
||||
initialState?: EventData;
|
||||
className?: string;
|
||||
isOpen: boolean;
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
onSubmit?: (data: EventSubmitData) => void;
|
||||
}) {
|
||||
const [reverseTasksMap, setReverseTasksMap] = useState<
|
||||
Record<string, string>
|
||||
>({});
|
||||
const [state, dispatchState] = useReducer(
|
||||
dispatchStateHandler,
|
||||
dispatchStateHandler({} as State, { action: "reset" }),
|
||||
);
|
||||
const [tasksMap, setTasksMap] = useState<Record<number, Task>>({});
|
||||
|
||||
// initialize the state
|
||||
function initialState(): State {
|
||||
if (props.initialState !== undefined && reverseTasksMap !== undefined) {
|
||||
const { description, date, tasks } = props.initialState;
|
||||
|
||||
return {
|
||||
description,
|
||||
date: toZoned(parseDateTime(date), getLocalTimeZone()),
|
||||
tasks: Object.keys(tasks).map((task) => reverseTasksMap[task]),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
date: now(getLocalTimeZone()),
|
||||
description: "",
|
||||
tasks: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// update the state if the initialState-prop changes
|
||||
useEffect(() => {
|
||||
if (props.initialState !== undefined) {
|
||||
dispatchState({ action: "reset" });
|
||||
}
|
||||
}, [props.initialState]);
|
||||
|
||||
// handle dispatch-calls
|
||||
function dispatchStateHandler(
|
||||
state: State,
|
||||
args: { action: "patch" | "reset"; value?: Partial<State> },
|
||||
): State {
|
||||
if (args.action === "reset") {
|
||||
return initialState();
|
||||
} else {
|
||||
return {
|
||||
...state,
|
||||
...args.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// shortcut for patching the state
|
||||
function patchState(values: Partial<State>) {
|
||||
dispatchState({ action: "patch", value: values });
|
||||
}
|
||||
|
||||
// handle state dispatches
|
||||
// get the available tasks and initialize the state with them
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const tasks = await getTasks();
|
||||
|
||||
setTasksMap(tasks);
|
||||
|
||||
setReverseTasksMap(
|
||||
Object.fromEntries(
|
||||
Object.entries(tasks).map(([id, task]) => {
|
||||
return [task.text, id];
|
||||
}),
|
||||
),
|
||||
);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
// sends the patch-event-request to the backend
|
||||
function patchEvent() {
|
||||
if (props.initialState !== undefined) {
|
||||
const { description, tasks, date } = state;
|
||||
|
||||
const data: EventSubmitData = {
|
||||
id: props.initialState?.id,
|
||||
description,
|
||||
tasks: tasks.map((task) => parseInt(task)),
|
||||
date: date.toAbsoluteString().slice(0, -1),
|
||||
};
|
||||
|
||||
props.onSubmit?.(data);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={props.isOpen}
|
||||
shadow={"none" as "sm"} // somehow "none" isn't allowed
|
||||
onOpenChange={props.onOpenChange}
|
||||
backdrop="blur"
|
||||
classNames={{
|
||||
base: "bg-accent-5 ",
|
||||
}}
|
||||
>
|
||||
<Form
|
||||
validationBehavior="native"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
void patchEvent();
|
||||
}}
|
||||
>
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<h1 className="text-center text-2xl">{props.children}</h1>
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<DatePicker
|
||||
isRequired
|
||||
label="Event date"
|
||||
name="date"
|
||||
variant="bordered"
|
||||
hideTimeZone
|
||||
granularity="minute"
|
||||
value={state.date}
|
||||
onChange={(date) => (!!date ? patchState({ date }) : null)}
|
||||
/>
|
||||
<Textarea
|
||||
variant="bordered"
|
||||
placeholder="Description"
|
||||
name="description"
|
||||
value={state.description}
|
||||
onValueChange={(description) => patchState({ description })}
|
||||
/>
|
||||
<CheckboxGroup
|
||||
name="tasks"
|
||||
value={state.tasks}
|
||||
onValueChange={(tasks) => patchState({ tasks })}
|
||||
validate={(value) =>
|
||||
value.length > 0 ? true : "Atleast one task must be selected"
|
||||
}
|
||||
>
|
||||
{tasksMap !== undefined ? (
|
||||
Object.entries(tasksMap)
|
||||
.filter(([, task]) => !task.disabled)
|
||||
.map(([id, task]) => (
|
||||
<div key={id}>
|
||||
<Checkbox value={id}>{task.text}</Checkbox>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<Spinner label="Loading" />
|
||||
)}
|
||||
</CheckboxGroup>
|
||||
</ModalBody>
|
||||
<ModalFooter>{props.footer}</ModalFooter>
|
||||
</ModalContent>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { getLocalTimeZone, parseDateTime } from "@internationalized/date";
|
||||
import { useLocale } from "@react-aria/i18n";
|
||||
|
||||
export default function LocalDate(props: {
|
||||
children: string;
|
||||
children?: string;
|
||||
className?: string;
|
||||
options: Intl.DateTimeFormatOptions;
|
||||
}) {
|
||||
@@ -13,9 +13,11 @@ export default function LocalDate(props: {
|
||||
|
||||
return (
|
||||
<span className={props.className}>
|
||||
{formatter.format(
|
||||
parseDateTime(props.children).toDate(getLocalTimeZone()),
|
||||
)}
|
||||
{props.children !== undefined
|
||||
? formatter.format(
|
||||
parseDateTime(props.children).toDate(getLocalTimeZone()),
|
||||
)
|
||||
: ""}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,32 +9,32 @@ export type APICallResult<T extends object> = Response & {
|
||||
export async function apiCall<K extends object>(
|
||||
method: "GET",
|
||||
api: string,
|
||||
params?: QueryParams,
|
||||
query?: QueryParams,
|
||||
): Promise<APICallResult<K>>;
|
||||
export async function apiCall<K extends object>(
|
||||
method: "POST" | "PATCH",
|
||||
method: "POST" | "PATCH" | "PUT",
|
||||
api: string,
|
||||
params?: QueryParams,
|
||||
query?: QueryParams,
|
||||
body?: object,
|
||||
): Promise<APICallResult<K>>;
|
||||
export async function apiCall<K extends object>(
|
||||
method: "DELETE",
|
||||
api: string,
|
||||
params?: QueryParams,
|
||||
query?: QueryParams,
|
||||
body?: object,
|
||||
): Promise<APICallResult<K>>;
|
||||
export async function apiCall<K extends object>(
|
||||
method: "GET" | "POST" | "PATCH" | "DELETE",
|
||||
method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE",
|
||||
api: string,
|
||||
params?: QueryParams,
|
||||
query?: QueryParams,
|
||||
body?: object,
|
||||
): Promise<APICallResult<K>> {
|
||||
let url = window.origin + "/api/" + api;
|
||||
|
||||
if (params) {
|
||||
if (query) {
|
||||
const urlsearchparams = new URLSearchParams(
|
||||
Object.fromEntries(
|
||||
Object.entries(params).map(([key, value]): [string, string] => {
|
||||
Object.entries(query).map(([key, value]): [string, string] => {
|
||||
if (typeof value !== "string") {
|
||||
return [key, value.toString()];
|
||||
} else {
|
||||
@@ -109,6 +109,6 @@ export async function getTasks(): Promise<Record<number, Task>> {
|
||||
|
||||
return tasks;
|
||||
} else {
|
||||
return [];
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user