From ea3e4573b3fe52c86ad237aa2181e8d7c7eb9de6 Mon Sep 17 00:00:00 2001 From: z1glr Date: Fri, 24 Jan 2025 12:35:05 +0000 Subject: [PATCH] added availability changing to events view --- backend/pkg/db/events/events.go | 38 +++++++++ backend/pkg/router/events.go | 11 +++ backend/pkg/router/router.go | 3 + client/src/Zustand.ts | 6 ++ client/src/app/Overview.tsx | 4 +- client/src/app/PendingEvents.tsx | 60 ++------------ client/src/app/events/page.tsx | 62 +++++++++++--- .../src/components/Event/AssignmentTable.tsx | 4 +- .../components/Event/AvailabilitySelector.tsx | 81 +++++++++++++++++++ client/src/components/Event/Event.tsx | 9 ++- 10 files changed, 207 insertions(+), 71 deletions(-) create mode 100644 client/src/components/Event/AvailabilitySelector.tsx diff --git a/backend/pkg/db/events/events.go b/backend/pkg/db/events/events.go index 0f4de3a..670bb5b 100644 --- a/backend/pkg/db/events/events.go +++ b/backend/pkg/db/events/events.go @@ -35,6 +35,11 @@ type EventWithAvailabilities struct { Availabilities availabilities.AvailabilityMap `json:"availabilities"` } +type EventWithAssignmentsUserAvailability struct { + EventWithAssignments + Availability *int `json:"availability" db:"availabilityID"` +} + type EventCreate struct { Date string `db:"date" json:"date" validate:"required,datetime=2006-01-02T15:04:05.999999999Z"` Description string `db:"description" json:"description"` @@ -54,6 +59,19 @@ func (e EventData) WithAssignments() (EventWithAssignments, error) { } } +func (e EventWithAssignments) WithUserAvailability(userName string) (EventWithAssignmentsUserAvailability, error) { + // get the availability of the user + event := EventWithAssignmentsUserAvailability{ + EventWithAssignments: e, + } + + if err := db.DB.Select(&event, "SELECT availabilityID FROM USER_AVAILABILITIES WHERE eventID = $1 AND userName = $2", e.EventID, userName); err != nil { + return EventWithAssignmentsUserAvailability{}, err + } else { + return event, nil + } +} + func (e EventData) WithAvailabilities() (EventWithAvailabilities, error) { // get the event with assignments if event, err := e.WithAssignments(); err != nil { @@ -217,6 +235,26 @@ func WithAvailabilities() ([]EventWithAvailabilities, error) { } } +func WithUserAvailability(userName string) ([]EventWithAssignmentsUserAvailability, error) { + var events []EventWithAssignmentsUserAvailability + + if err := db.DB.Select(&events, "SELECT EVENTS.eventID, EVENTS.description, EVENTS.date, USER_AVAILABILITIES.availabilityID FROM EVENTS LEFT JOIN USER_AVAILABILITIES ON EVENTS.eventID = USER_AVAILABILITIES.eventID AND USER_AVAILABILITIES.userName = $1", userName); err != nil { + return nil, err + } else { + // get the assignments for every event + for ii, event := range events { + if eventWithAssignments, err := event.EventWithAssignments.EventData.WithAssignments(); err != nil { + // remove the current event from the events + events = append(events[:ii], events[ii+1:]...) + } else { + events[ii].EventWithAssignments = eventWithAssignments + } + } + + return events, nil + } +} + func UserPending(userName string) ([]EventData, error) { var result []EventData diff --git a/backend/pkg/router/events.go b/backend/pkg/router/events.go index 906259f..5d34692 100644 --- a/backend/pkg/router/events.go +++ b/backend/pkg/router/events.go @@ -90,6 +90,17 @@ func (a *Handler) getEventsAvailabilities() { } } +func (a *Handler) getEventUserAssignmentAvailability() { + // retrieve the assignments + if events, err := events.WithUserAvailability(a.UserName); err != nil { + a.Status = fiber.StatusBadRequest + + logger.Log().Msgf("getting events with tasks and user-availability failed: %v", err) + } else { + a.Data = events + } +} + func (a *Handler) getEventsUserPending() { if events, err := events.UserPending(a.UserName); err != nil { a.Status = fiber.StatusInternalServerError diff --git a/backend/pkg/router/router.go b/backend/pkg/router/router.go index 6f15c33..e956044 100644 --- a/backend/pkg/router/router.go +++ b/backend/pkg/router/router.go @@ -83,6 +83,9 @@ func init() { // all events with the availabilities of the individual users "events/availabilities": (*Handler).getEventsAvailabilities, + // all events with the task-assignments and the availability of the current user + "events/user/assignmentAvailability": (*Handler).getEventUserAssignmentAvailability, + // events the user has to enter his availability for "events/user/pending": (*Handler).getEventsUserPending, diff --git a/client/src/Zustand.ts b/client/src/Zustand.ts index f456442..31da8fd 100644 --- a/client/src/Zustand.ts +++ b/client/src/Zustand.ts @@ -10,10 +10,16 @@ export interface BaseEvent { description: string; } +export type EventAvailability = BaseEvent & { + availability: number; +}; + export type EventData = BaseEvent & { tasks: TaskAssignment[]; }; +export type EventDataWithAvailability = EventData & EventAvailability; + export interface TaskAssignment { taskID: number; taskName: string; diff --git a/client/src/app/Overview.tsx b/client/src/app/Overview.tsx index 9647a10..bb431ee 100644 --- a/client/src/app/Overview.tsx +++ b/client/src/app/Overview.tsx @@ -1,5 +1,5 @@ import MyEvents from "./MyEvents"; -import PengingEvents from "./PendingEvents"; +import PendingEvents from "./PendingEvents"; export default function Overview() { return ( @@ -12,7 +12,7 @@ export default function Overview() {

Pending Events

- + ); diff --git a/client/src/app/PendingEvents.tsx b/client/src/app/PendingEvents.tsx index 504b40d..1979146 100644 --- a/client/src/app/PendingEvents.tsx +++ b/client/src/app/PendingEvents.tsx @@ -1,17 +1,12 @@ "use client"; -import AvailabilityChip from "@/components/AvailabilityChip"; +import AvailabilitySelector from "@/components/Event/AvailabilitySelector"; import Event from "@/components/Event/Event"; -import { apiCall, getAvailabilities } from "@/lib"; -import { BaseEvent } from "@/Zustand"; -import { Select, SelectItem } from "@heroui/react"; +import { apiCall } from "@/lib"; +import { EventAvailability } from "@/Zustand"; import { useAsyncList } from "@react-stately/data"; -type EventAvailability = BaseEvent & { - availability: number; -}; - -export default function PengingEvents() { +export default function PendingEvents() { // get the events the user hasn't yet inserted his availability for const events = useAsyncList({ async load() { @@ -32,56 +27,11 @@ export default function PengingEvents() { }, }); - // the individual, selectable availabilities - const availabilities = useAsyncList({ - async load() { - return { - items: (await getAvailabilities()).filter((a) => a.enabled), - }; - }, - }); - - async function setAvailability(eventID: number, availabilityID: number) { - await apiCall( - "PUT", - "events/user/availability", - { eventID }, - availabilityID, - ); - } - return (
{events.items.map((e) => ( - + ))}
diff --git a/client/src/app/events/page.tsx b/client/src/app/events/page.tsx index f7685c4..69b5bab 100644 --- a/client/src/app/events/page.tsx +++ b/client/src/app/events/page.tsx @@ -3,23 +3,31 @@ import AddEvent from "@/components/Event/AddEvent"; import AssignmentTable from "@/components/Event/AssignmentTable"; import Event from "@/components/Event/Event"; import { apiCall } from "@/lib"; -import zustand, { EventData } from "@/Zustand"; +import zustand, { EventDataWithAvailability } from "@/Zustand"; import { Add } from "@carbon/icons-react"; -import { Button } from "@heroui/react"; +import { Button, Tab, Tabs } from "@heroui/react"; import { useAsyncList } from "@react-stately/data"; import { useState } from "react"; +import AvailabilitySelector from "@/components/Event/AvailabilitySelector"; export default function Events() { const [showAddItemDialogue, setShowAddItemDialogue] = useState(false); - const admin = zustand((state) => state.user?.admin); + const [filter, setFilter] = useState(""); - const events = useAsyncList({ + const user = zustand((state) => state.user); + + const events = useAsyncList({ async load() { - const result = await apiCall("GET", "events/assignments"); + const result = await apiCall( + "GET", + "events/user/assignmentAvailability", + ); if (result.ok) { const data = await result.json(); + console.debug(data); + return { items: data, }; @@ -31,20 +39,52 @@ export default function Events() { }, }); + function showEvent(event: EventDataWithAvailability): boolean { + switch (filter) { + case "assigned": + return event.tasks.some((t) => { + return t.userName === user?.userName; + }); + + case "pending": + return event.availability === null; + + default: + return true; + } + } + return ( -
-

Upcoming Events

+
+

Upcoming Events

+ + + + + + +
- {events.items.map((ee, ii) => ( - + {events.items.filter(showEvent).map((e) => ( +
- + +
))}
- {admin ? ( + {user?.admin ? ( <>