more brainstorming
This commit is contained in:
106
client/src/components/Event/AddEvent.tsx
Normal file
106
client/src/components/Event/AddEvent.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import { useState } from "react";
|
||||
import { Add } from "@carbon/icons-react";
|
||||
import zustand, { EventData, Task, Tasks } from "../../Zustand";
|
||||
import { Button } from "@nextui-org/button";
|
||||
import { Checkbox, CheckboxGroup } from "@nextui-org/checkbox";
|
||||
import { DatePicker } from "@nextui-org/date-picker";
|
||||
import { getLocalTimeZone, now, ZonedDateTime } from "@internationalized/date";
|
||||
import { Textarea } from "@nextui-org/input";
|
||||
import {
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
} from "@nextui-org/modal";
|
||||
|
||||
interface state {
|
||||
date: ZonedDateTime;
|
||||
description: string;
|
||||
tasks: Task[];
|
||||
}
|
||||
|
||||
export default function AddEvent(props: {
|
||||
className?: string;
|
||||
isOpen: boolean;
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
}) {
|
||||
const [state, setState] = useState<state>({
|
||||
date: now(getLocalTimeZone()),
|
||||
description: "",
|
||||
tasks: [],
|
||||
});
|
||||
|
||||
function addEvent() {
|
||||
const eventData: EventData = {
|
||||
date: state.date.toString(),
|
||||
description: state.description,
|
||||
id: zustand.getState().events.slice(-1)[0].id + 1,
|
||||
tasks: {},
|
||||
volunteers: {},
|
||||
};
|
||||
|
||||
// add all the tasks
|
||||
state.tasks.forEach((task) => {
|
||||
eventData.tasks[task] = undefined;
|
||||
});
|
||||
|
||||
zustand.getState().addEvent(eventData);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={props.isOpen}
|
||||
shadow={"none" as "sm"} // somehow "none" isn't allowed
|
||||
onOpenChange={props.onOpenChange}
|
||||
backdrop="blur"
|
||||
classNames={{
|
||||
base: "bg-accent-5 ",
|
||||
}}
|
||||
>
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<h1 className="text-2xl">Add Event</h1>
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<DatePicker
|
||||
label="Event date"
|
||||
variant="bordered"
|
||||
hideTimeZone
|
||||
granularity="minute"
|
||||
value={state.date}
|
||||
onChange={(dt) => (!!dt ? setState({ ...state, date: dt }) : null)}
|
||||
/>
|
||||
<Textarea
|
||||
variant="bordered"
|
||||
placeholder="Description"
|
||||
value={state.description}
|
||||
onValueChange={(desc) => setState({ ...state, description: desc })}
|
||||
/>
|
||||
<CheckboxGroup
|
||||
value={state.tasks}
|
||||
onValueChange={(newTasks) =>
|
||||
setState({ ...state, tasks: newTasks })
|
||||
}
|
||||
>
|
||||
{Tasks.map((task, ii) => (
|
||||
<div key={ii}>
|
||||
<Checkbox value={task}>{task}</Checkbox>
|
||||
</div>
|
||||
))}
|
||||
</CheckboxGroup>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
color="primary"
|
||||
radius="full"
|
||||
startContent={<Add size={32} />}
|
||||
onPress={addEvent}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
50
client/src/components/Event/Event.tsx
Normal file
50
client/src/components/Event/Event.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Card, CardBody, CardHeader } from "@nextui-org/card";
|
||||
import { Divider } from "@nextui-org/divider";
|
||||
import LocalDate from "./LocalDate";
|
||||
import { EventData } from "@/Zustand";
|
||||
|
||||
export default function Event(props: EventData) {
|
||||
return (
|
||||
<Card
|
||||
classNames={{
|
||||
base: "bg-accent-4 w-64",
|
||||
body: "flex flex-col gap-4",
|
||||
}}
|
||||
shadow="none"
|
||||
>
|
||||
<CardHeader>
|
||||
<h3 className="bold mb-1 text-2xl">
|
||||
<LocalDate
|
||||
options={{
|
||||
dateStyle: "short",
|
||||
timeStyle: "short",
|
||||
timeZone: "Europe/Berlin", // TODO: check with actual backend
|
||||
}}
|
||||
>
|
||||
{props.date.toDate()}
|
||||
</LocalDate>
|
||||
</h3>
|
||||
</CardHeader>
|
||||
<Divider />
|
||||
<CardBody>
|
||||
<div>{props.description}</div>
|
||||
|
||||
<table>
|
||||
<caption>
|
||||
<h4>Task assignment</h4>
|
||||
</caption>
|
||||
<tbody>
|
||||
{Object.entries(props.tasks).map(([task, person], ii) => (
|
||||
<tr key={ii}>
|
||||
<th className="pr-4 text-left">{task}</th>
|
||||
<td>
|
||||
{person ?? <span className="text-highlight">missing</span>}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
16
client/src/components/Event/LocalDate.tsx
Normal file
16
client/src/components/Event/LocalDate.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
"use local";
|
||||
|
||||
import { DateFormatter } from "@/Zustand";
|
||||
import { useLocale } from "@react-aria/i18n";
|
||||
|
||||
export default function LocalDate(props: {
|
||||
children: Date;
|
||||
className?: string;
|
||||
options: Intl.DateTimeFormatOptions;
|
||||
}) {
|
||||
const formatter = new DateFormatter(useLocale().locale, props.options);
|
||||
|
||||
return (
|
||||
<span className={props.className}>{formatter.format(props.children)}</span>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user