added viewing all availabilities in events view
This commit is contained in:
@@ -2,4 +2,5 @@
|
|||||||
|
|
||||||
- readme text
|
- readme text
|
||||||
- check enter pressing on modals
|
- check enter pressing on modals
|
||||||
- add control-enter on event-description
|
- add availability table
|
||||||
|
- add availability notes
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ type EventWithAvailabilities struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type EventWithAssignmentsUserAvailability struct {
|
type EventWithAssignmentsUserAvailability struct {
|
||||||
EventWithAssignments
|
EventWithAvailabilities
|
||||||
Availability *int `json:"availability" db:"availabilityID"`
|
Availability *int `json:"availability" db:"availabilityID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,10 +71,10 @@ func (e EventData) WithAssignments() (EventWithAssignments, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e EventWithAssignments) WithUserAvailability(userName string) (EventWithAssignmentsUserAvailability, error) {
|
func (e EventWithAvailabilities) WithUserAvailability(userName string) (EventWithAssignmentsUserAvailability, error) {
|
||||||
// get the availability of the user
|
// get the availability of the user
|
||||||
event := EventWithAssignmentsUserAvailability{
|
event := EventWithAssignmentsUserAvailability{
|
||||||
EventWithAssignments: e,
|
EventWithAvailabilities: e,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := db.DB.Select(&event, "SELECT availabilityID FROM USER_AVAILABILITIES WHERE eventID = $1 AND userName = $2", e.EventID, userName); err != nil {
|
if err := db.DB.Select(&event, "SELECT availabilityID FROM USER_AVAILABILITIES WHERE eventID = $1 AND userName = $2", e.EventID, userName); err != nil {
|
||||||
|
|||||||
@@ -130,11 +130,11 @@ func (userName UserName) WithUserAvailability() ([]events.EventWithAssignmentsUs
|
|||||||
} else {
|
} else {
|
||||||
// get the assignments for every event
|
// get the assignments for every event
|
||||||
for ii, event := range events {
|
for ii, event := range events {
|
||||||
if eventWithAssignments, err := event.EventWithAssignments.EventData.WithAssignments(); err != nil {
|
if eventWithAssignments, err := event.EventWithAssignments.EventData.WithAvailabilities(); err != nil {
|
||||||
// remove the current event from the events
|
// remove the current event from the events
|
||||||
events = append(events[:ii], events[ii+1:]...)
|
events = append(events[:ii], events[ii+1:]...)
|
||||||
} else {
|
} else {
|
||||||
events[ii].EventWithAssignments = eventWithAssignments
|
events[ii].EventWithAvailabilities = eventWithAssignments
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,10 +80,6 @@ func (a *Handler) getEventsAssignments() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Handler) getEventsAvailabilities() {
|
func (a *Handler) getEventsAvailabilities() {
|
||||||
// check for admin
|
|
||||||
if !a.Admin {
|
|
||||||
a.Status = fiber.StatusForbidden
|
|
||||||
} else {
|
|
||||||
if events, err := events.WithAvailabilities(); err != nil {
|
if events, err := events.WithAvailabilities(); err != nil {
|
||||||
a.Status = fiber.StatusInternalServerError
|
a.Status = fiber.StatusInternalServerError
|
||||||
|
|
||||||
@@ -92,7 +88,6 @@ func (a *Handler) getEventsAvailabilities() {
|
|||||||
a.Data = events
|
a.Data = events
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Handler) getEventUserAssignmentAvailability() {
|
func (a *Handler) getEventUserAssignmentAvailability() {
|
||||||
// retrieve the assignments
|
// retrieve the assignments
|
||||||
|
|||||||
4288
client/package-lock.json
generated
4288
client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@carbon/icons-react": "^11.53.0",
|
"@carbon/icons-react": "^11.53.0",
|
||||||
"@heroui/react": "^2.6.14",
|
"@heroui/react": "^2.6.14",
|
||||||
"@internationalized/date": "3.6.0",
|
"@internationalized/date": "3.7.0",
|
||||||
"@mantine/hooks": "^7.16.2",
|
"@mantine/hooks": "^7.16.2",
|
||||||
"@react-aria/i18n": "^3.12.4",
|
"@react-aria/i18n": "^3.12.4",
|
||||||
"@react-stately/data": "^3.12.0",
|
"@react-stately/data": "^3.12.0",
|
||||||
|
|||||||
@@ -14,11 +14,17 @@ export type EventAvailability = BaseEvent & {
|
|||||||
availability: number;
|
availability: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EventAvailabilities = BaseEvent & {
|
||||||
|
availabilities: Record<number, string[]>;
|
||||||
|
};
|
||||||
|
|
||||||
export type EventData = BaseEvent & {
|
export type EventData = BaseEvent & {
|
||||||
tasks: TaskAssignment[];
|
tasks: TaskAssignment[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EventDataWithAvailability = EventData & EventAvailability;
|
export type EventDataWithAvailabilityAvailabilities = EventData &
|
||||||
|
EventAvailability &
|
||||||
|
EventAvailabilities;
|
||||||
|
|
||||||
export interface TaskAssignment {
|
export interface TaskAssignment {
|
||||||
taskID: number;
|
taskID: number;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import AssignmentTable from "@/components/Event/AssignmentTable";
|
import AssignmentGrid from "@/components/Event/AssignmentTable";
|
||||||
import Event from "@/components/Event/Event";
|
import Event from "@/components/Event/Event";
|
||||||
import Loading from "@/components/Loading";
|
import Loading from "@/components/Loading";
|
||||||
import { apiCall } from "@/lib";
|
import { apiCall } from "@/lib";
|
||||||
@@ -37,7 +37,7 @@ export default function MyEvents() {
|
|||||||
<div className="flex flex-wrap justify-center gap-4">
|
<div className="flex flex-wrap justify-center gap-4">
|
||||||
{events.items.map((e) => (
|
{events.items.map((e) => (
|
||||||
<Event key={e.eventID} event={e}>
|
<Event key={e.eventID} event={e}>
|
||||||
<AssignmentTable
|
<AssignmentGrid
|
||||||
className="mt-auto"
|
className="mt-auto"
|
||||||
tasks={e.tasks}
|
tasks={e.tasks}
|
||||||
highlightUser={user?.userName}
|
highlightUser={user?.userName}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ export default function Availabilities() {
|
|||||||
{(availability) => (
|
{(availability) => (
|
||||||
<TableRow key={availability.availabilityName}>
|
<TableRow key={availability.availabilityName}>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<AvailabilityChip availability={availability} />
|
<AvailabilityChip>{availability}</AvailabilityChip>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Checkbox isSelected={availability.enabled} />
|
<Checkbox isSelected={availability.enabled} />
|
||||||
@@ -199,7 +199,7 @@ export default function Availabilities() {
|
|||||||
{!!deleteAvailability ? (
|
{!!deleteAvailability ? (
|
||||||
<>
|
<>
|
||||||
The availability{" "}
|
The availability{" "}
|
||||||
<AvailabilityChip availability={deleteAvailability} /> will be
|
<AvailabilityChip>{deleteAvailability}</AvailabilityChip>
|
||||||
deleted.
|
deleted.
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function EditAvailability(props: {
|
|||||||
<>
|
<>
|
||||||
Edit Availability{" "}
|
Edit Availability{" "}
|
||||||
{!!props.value ? (
|
{!!props.value ? (
|
||||||
<AvailabilityChip availability={props.value} className="ms-4" />
|
<AvailabilityChip className="ms-4">{props.value}</AvailabilityChip>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,9 +129,9 @@ export default function VolunteerSelector({
|
|||||||
}}
|
}}
|
||||||
title={
|
title={
|
||||||
(
|
(
|
||||||
<AvailabilityChip
|
<AvailabilityChip>
|
||||||
availability={getAvailabilityById(parseInt(availabilityId))}
|
{getAvailabilityById(parseInt(availabilityId))}
|
||||||
/>
|
</AvailabilityChip>
|
||||||
) as ReactElement & string
|
) as ReactElement & string
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,24 +1,50 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import AddEvent from "@/components/Event/AddEvent";
|
import AddEvent from "@/components/Event/AddEvent";
|
||||||
import AssignmentTable from "@/components/Event/AssignmentTable";
|
import AssigmentTable from "@/components/Event/AssignmentTable";
|
||||||
import Event from "@/components/Event/Event";
|
import Event from "@/components/Event/Event";
|
||||||
import { apiCall, getUserTasks } from "@/lib";
|
import { apiCall, getAvailabilities, getUserTasks } from "@/lib";
|
||||||
import zustand, { EventDataWithAvailability } from "@/Zustand";
|
import zustand, { EventDataWithAvailabilityAvailabilities } from "@/Zustand";
|
||||||
import { Add } from "@carbon/icons-react";
|
import { Add, Filter } from "@carbon/icons-react";
|
||||||
import { Button, Tab, Tabs } from "@heroui/react";
|
import {
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
CheckboxGroup,
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
Tab,
|
||||||
|
Tabs,
|
||||||
|
} from "@heroui/react";
|
||||||
import { useAsyncList } from "@react-stately/data";
|
import { useAsyncList } from "@react-stately/data";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import AvailabilitySelector from "@/components/Event/AvailabilitySelector";
|
import AvailabilitySelector from "@/components/Event/AvailabilitySelector";
|
||||||
|
import AvailabilityTable from "@/components/Event/AvailabilityTable";
|
||||||
|
|
||||||
|
const filterValues: { text: string; value: string }[] = [
|
||||||
|
{
|
||||||
|
text: "Description",
|
||||||
|
value: "description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Availabilities",
|
||||||
|
value: "availabilities",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Tasks",
|
||||||
|
value: "tasks",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export default function Events() {
|
export default function Events() {
|
||||||
const [showAddItemDialogue, setShowAddItemDialogue] = useState(false);
|
const [showAddItemDialogue, setShowAddItemDialogue] = useState(false);
|
||||||
const [filter, setFilter] = useState<string | number>("");
|
const [filter, setFilter] = useState<string | number>("all");
|
||||||
|
const [contentFilter, setContentFilter] = useState(["description", "tasks"]);
|
||||||
|
|
||||||
const user = zustand((state) => state.user);
|
const user = zustand((state) => state.user);
|
||||||
|
|
||||||
const events = useAsyncList({
|
const events = useAsyncList({
|
||||||
async load() {
|
async load() {
|
||||||
const result = await apiCall<EventDataWithAvailability[]>(
|
const result = await apiCall<EventDataWithAvailabilityAvailabilities[]>(
|
||||||
"GET",
|
"GET",
|
||||||
"events/user/assignmentAvailability",
|
"events/user/assignmentAvailability",
|
||||||
);
|
);
|
||||||
@@ -45,7 +71,15 @@ export default function Events() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function showEvent(event: EventDataWithAvailability): boolean {
|
const availabilites = useAsyncList({
|
||||||
|
async load() {
|
||||||
|
return {
|
||||||
|
items: await getAvailabilities(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function showEvent(event: EventDataWithAvailabilityAvailabilities): boolean {
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
case "assigned":
|
case "assigned":
|
||||||
return event.tasks.some((t) => {
|
return event.tasks.some((t) => {
|
||||||
@@ -60,7 +94,9 @@ export default function Events() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAvailabilitySelector(event: EventDataWithAvailability): boolean {
|
function showAvailabilitySelector(
|
||||||
|
event: EventDataWithAvailabilityAvailabilities,
|
||||||
|
): boolean {
|
||||||
return event.tasks.some((t) => userTasks.items.includes(t.taskID));
|
return event.tasks.some((t) => userTasks.items.includes(t.taskID));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +104,7 @@ export default function Events() {
|
|||||||
<div className="relative flex flex-1 flex-col gap-4">
|
<div className="relative flex flex-1 flex-col gap-4">
|
||||||
<h2 className="text-center text-4xl">Upcoming Events</h2>
|
<h2 className="text-center text-4xl">Upcoming Events</h2>
|
||||||
|
|
||||||
|
<div className="relative flex w-full">
|
||||||
<Tabs
|
<Tabs
|
||||||
selectedKey={filter}
|
selectedKey={filter}
|
||||||
onSelectionChange={setFilter}
|
onSelectionChange={setFilter}
|
||||||
@@ -78,20 +115,57 @@ export default function Events() {
|
|||||||
<Tab key="pending" title="Pending" />
|
<Tab key="pending" title="Pending" />
|
||||||
<Tab key="assigned" title="Assigned" />
|
<Tab key="assigned" title="Assigned" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
<div className="absolute right-0">
|
||||||
|
<Popover placement="bottom-end">
|
||||||
|
<PopoverTrigger>
|
||||||
|
<Button isIconOnly>
|
||||||
|
<Filter className="cursor-pointer" />
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<CheckboxGroup
|
||||||
|
value={contentFilter}
|
||||||
|
onValueChange={setContentFilter}
|
||||||
|
>
|
||||||
|
{filterValues.map((f) => (
|
||||||
|
<Checkbox key={f.value} value={f.value}>
|
||||||
|
{f.text}
|
||||||
|
</Checkbox>
|
||||||
|
))}
|
||||||
|
</CheckboxGroup>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mx-auto flex flex-wrap gap-4">
|
<div className="mx-auto flex flex-wrap gap-4">
|
||||||
{events.items.filter(showEvent).map((e) => (
|
{availabilites.items.length > 0
|
||||||
<Event key={e.eventID} event={e}>
|
? events.items.filter(showEvent).map((e) => (
|
||||||
<AssignmentTable
|
<Event
|
||||||
|
key={e.eventID}
|
||||||
|
event={e}
|
||||||
|
hideDescription={!contentFilter.includes("description")}
|
||||||
|
>
|
||||||
|
<div className="mt-auto flex flex-col gap-4">
|
||||||
|
{contentFilter.includes("availabilities") ? (
|
||||||
|
<AvailabilityTable availabilities={e.availabilities} />
|
||||||
|
) : null}
|
||||||
|
{contentFilter.includes("tasks") ? (
|
||||||
|
<AssigmentTable
|
||||||
highlightUser={user?.userName}
|
highlightUser={user?.userName}
|
||||||
tasks={e.tasks}
|
tasks={e.tasks}
|
||||||
className="mt-auto"
|
|
||||||
/>
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
{showAvailabilitySelector(e) ? (
|
{showAvailabilitySelector(e) ? (
|
||||||
<AvailabilitySelector event={e} startSelection={e.availability} />
|
<AvailabilitySelector
|
||||||
|
event={e}
|
||||||
|
startSelection={e.availability}
|
||||||
|
/>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</Event>
|
</Event>
|
||||||
))}
|
))
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{user?.admin ? (
|
{user?.admin ? (
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
12
client/src/app/icon.svg
Executable file
12
client/src/app/icon.svg
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="32px" height="32px" viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:none;}
|
||||||
|
</style>
|
||||||
|
<title>calendar</title>
|
||||||
|
<path d="M26,4h-4V2h-2v2h-8V2h-2v2H6C4.9,4,4,4.9,4,6v20c0,1.1,0.9,2,2,2h20c1.1,0,2-0.9,2-2V6C28,4.9,27.1,4,26,4z M26,26H6V12h20
|
||||||
|
V26z M26,10H6V6h4v2h2V6h8v2h2V6h4V10z"/>
|
||||||
|
<rect id="_Transparent_Rectangle_" class="st0" width="32" height="32"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 693 B |
@@ -3,21 +3,21 @@ import { color2Tailwind } from "./Colorselector";
|
|||||||
import { Availability } from "@/app/admin/(availabilities)/AvailabilityEditor";
|
import { Availability } from "@/app/admin/(availabilities)/AvailabilityEditor";
|
||||||
|
|
||||||
export default function AvailabilityChip({
|
export default function AvailabilityChip({
|
||||||
availability,
|
children,
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
availability?: Availability;
|
children?: Availability;
|
||||||
className?: string;
|
className?: string;
|
||||||
classNames?: ChipProps["classNames"];
|
classNames?: ChipProps["classNames"];
|
||||||
}) {
|
}) {
|
||||||
return !!availability ? (
|
return !!children ? (
|
||||||
<Chip
|
<Chip
|
||||||
classNames={{
|
classNames={{
|
||||||
base: `bg-${color2Tailwind(availability.color)}`,
|
base: `bg-${color2Tailwind(children.color)}`,
|
||||||
}}
|
}}
|
||||||
className={className}
|
className={className}
|
||||||
>
|
>
|
||||||
{availability.availabilityName}
|
{children.availabilityName}
|
||||||
</Chip>
|
</Chip>
|
||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
import { classNames } from "@/lib";
|
import { classNames } from "@/lib";
|
||||||
import { EventData } from "@/Zustand";
|
import { EventData } from "@/Zustand";
|
||||||
import {
|
import { Fragment } from "react";
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableColumn,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@heroui/react";
|
|
||||||
|
|
||||||
export default function AssignmentTable({
|
export default function AssignmentTable({
|
||||||
tasks,
|
tasks,
|
||||||
@@ -23,40 +16,27 @@ export default function AssignmentTable({
|
|||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<h4 id="assignmentTableHeader">Tasks</h4>
|
<h4 id="assignmentTableHeader">Tasks</h4>
|
||||||
<Table
|
<div className="grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2">
|
||||||
aria-labelledby="assignmentTableHeader"
|
{tasks.map((task) => (
|
||||||
hideHeader
|
<Fragment key={task.taskID}>
|
||||||
removeWrapper
|
<div
|
||||||
classNames={{
|
className={classNames(
|
||||||
td: "text-base",
|
classNames({
|
||||||
}}
|
|
||||||
shadow="none"
|
|
||||||
title="Tasks"
|
|
||||||
>
|
|
||||||
<TableHeader>
|
|
||||||
<TableColumn>Task</TableColumn>
|
|
||||||
<TableColumn>Volunteer</TableColumn>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody items={tasks}>
|
|
||||||
{(task) => (
|
|
||||||
<TableRow
|
|
||||||
key={task.taskID}
|
|
||||||
className={classNames({
|
|
||||||
"text-danger":
|
"text-danger":
|
||||||
task.userName === highlightUser ||
|
task.userName === highlightUser ||
|
||||||
task.taskName === highlightTask,
|
task.taskName === highlightTask,
|
||||||
})}
|
}),
|
||||||
|
"text-sm font-bold",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<TableCell className="font-bold">{task.taskName}</TableCell>
|
{task.taskName}
|
||||||
<TableCell>
|
</div>
|
||||||
{task.userName ?? (
|
{task.userName ?? (
|
||||||
<span className="italic text-highlight">missing</span>
|
<span className="text-sm italic text-highlight">missing</span>
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</Fragment>
|
||||||
</TableRow>
|
))}
|
||||||
)}
|
</div>
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ export default function AvailabilitySelector({
|
|||||||
event,
|
event,
|
||||||
className,
|
className,
|
||||||
startSelection,
|
startSelection,
|
||||||
|
onRefresh,
|
||||||
}: {
|
}: {
|
||||||
event: EventAvailability;
|
event: EventAvailability;
|
||||||
className?: string;
|
className?: string;
|
||||||
startSelection?: number;
|
startSelection?: number;
|
||||||
|
onRefresh?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const [value, setValue] = useState<Selection>(new Set([]));
|
const [value, setValue] = useState<Selection>(new Set([]));
|
||||||
|
|
||||||
@@ -38,12 +40,16 @@ export default function AvailabilitySelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function setAvailability(eventID: number, availabilityID: number) {
|
async function setAvailability(eventID: number, availabilityID: number) {
|
||||||
await apiCall(
|
const request = await apiCall(
|
||||||
"PUT",
|
"PUT",
|
||||||
"events/user/availability",
|
"events/user/availability",
|
||||||
{ eventID },
|
{ eventID },
|
||||||
availabilityID,
|
availabilityID,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (request.ok) {
|
||||||
|
onRefresh?.();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -59,7 +65,7 @@ export default function AvailabilitySelector({
|
|||||||
<div>
|
<div>
|
||||||
{availability.map((a) =>
|
{availability.map((a) =>
|
||||||
!!a.data ? (
|
!!a.data ? (
|
||||||
<AvailabilityChip key={a.key} availability={a.data} />
|
<AvailabilityChip key={a.key}>{a.data}</AvailabilityChip>
|
||||||
) : null,
|
) : null,
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -75,7 +81,7 @@ export default function AvailabilitySelector({
|
|||||||
key={availability.availabilityID}
|
key={availability.availabilityID}
|
||||||
textValue={availability.availabilityName}
|
textValue={availability.availabilityName}
|
||||||
>
|
>
|
||||||
<AvailabilityChip availability={availability} />
|
<AvailabilityChip>{availability}</AvailabilityChip>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
)}
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
|
|||||||
39
client/src/components/Event/AvailabilityTable.tsx
Normal file
39
client/src/components/Event/AvailabilityTable.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { useAsyncList } from "@react-stately/data";
|
||||||
|
import AvailabilityChip from "../AvailabilityChip";
|
||||||
|
import { EventAvailabilities } from "@/Zustand";
|
||||||
|
import { getAvailabilities } from "@/lib";
|
||||||
|
import { Fragment } from "react";
|
||||||
|
|
||||||
|
export default function AvailabilityTable({
|
||||||
|
className,
|
||||||
|
availabilities: eventAvailabilities,
|
||||||
|
}: {
|
||||||
|
className?: string;
|
||||||
|
availabilities: EventAvailabilities["availabilities"];
|
||||||
|
}) {
|
||||||
|
const availabilities = useAsyncList({
|
||||||
|
async load() {
|
||||||
|
return {
|
||||||
|
items: await getAvailabilities(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
<h4>Availabilities</h4>
|
||||||
|
<div className="grid grid-cols-[auto_1fr] items-center gap-2">
|
||||||
|
{Object.entries(eventAvailabilities).map(([a, users]) => (
|
||||||
|
<Fragment key={a}>
|
||||||
|
<AvailabilityChip className="mx-auto">
|
||||||
|
{availabilities.items.find(
|
||||||
|
(i) => i.availabilityID === parseInt(a),
|
||||||
|
)}
|
||||||
|
</AvailabilityChip>
|
||||||
|
<div className="text-sm italic">{users.join(", ")}</div>
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,9 +7,11 @@ import React from "react";
|
|||||||
|
|
||||||
export default function Event({
|
export default function Event({
|
||||||
event,
|
event,
|
||||||
|
hideDescription,
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
event: BaseEvent;
|
event: BaseEvent;
|
||||||
|
hideDescription?: boolean;
|
||||||
children?: React.ReactNode | [React.ReactNode, React.ReactNode];
|
children?: React.ReactNode | [React.ReactNode, React.ReactNode];
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
@@ -34,7 +36,7 @@ export default function Event({
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<Divider />
|
<Divider />
|
||||||
<CardBody>
|
<CardBody>
|
||||||
{event.description != "" ? (
|
{event.description != "" && !hideDescription ? (
|
||||||
<div>
|
<div>
|
||||||
<h4>Description</h4>
|
<h4>Description</h4>
|
||||||
<div className="ms-2 mt-2">{event.description}</div>
|
<div className="ms-2 mt-2">{event.description}</div>
|
||||||
|
|||||||
@@ -194,6 +194,8 @@ export async function getAvailabilities(): Promise<Availability[]> {
|
|||||||
|
|
||||||
state.patch({ availabilities });
|
state.patch({ availabilities });
|
||||||
|
|
||||||
|
console.debug(zustand.getState().availabilities);
|
||||||
|
|
||||||
return availabilities;
|
return availabilities;
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
Reference in New Issue
Block a user