diff --git a/config.ts b/config.ts index be50288..381ccc2 100644 --- a/config.ts +++ b/config.ts @@ -3,11 +3,11 @@ */ // POCKETBASE -export const PB_USER_COLLECTION = "ldap_users" +export const PB_USER_COLLECTION = "users" export const PB_BASE_URL = "https://backend.stuve-it.de" export const PB_STORAGE_KEY = "stuve-it-login-record" // general export const APP_NAME = "StuVe IT" -export const APP_VERSION = "0.7.0 (beta)" +export const APP_VERSION = "0.8.0 (beta)" export const APP_URL = "https://it.stuve.uni-ulm.de" \ No newline at end of file diff --git a/package.json b/package.json index 1f575e7..9f79f86 100644 --- a/package.json +++ b/package.json @@ -13,14 +13,14 @@ "@fontsource/fira-code": "^5.0.15", "@fontsource/overpass": "^5.0.15", "@hello-pangea/dnd": "^16.6.0", - "@mantine/code-highlight": "^7.8.0", - "@mantine/core": "^7.8.0", - "@mantine/dates": "^7.8.0", - "@mantine/form": "^7.8.0", - "@mantine/hooks": "^7.8.0", - "@mantine/modals": "^7.9.0", - "@mantine/notifications": "^7.8.1", - "@mantine/tiptap": "^7.8.0", + "@mantine/code-highlight": "^7.10.0", + "@mantine/core": "^7.10.0", + "@mantine/dates": "^7.10.0", + "@mantine/form": "^7.10.0", + "@mantine/hooks": "^7.10.0", + "@mantine/modals": "^7.10.0", + "@mantine/notifications": "^7.10.0", + "@mantine/tiptap": "^7.10.0", "@tabler/icons-react": "^3.2.0", "@tanstack/react-query": "^5.0.5", "@tanstack/react-query-devtools": "^5.31.0", diff --git a/src/components/formUtil/FormFilter/FilterField.tsx b/src/components/formUtil/FormFilter/FilterField.tsx new file mode 100644 index 0000000..eed924d --- /dev/null +++ b/src/components/formUtil/FormFilter/FilterField.tsx @@ -0,0 +1,170 @@ +import { + CheckboxFieldEntryFilter, + DateFieldEntryFilter, + DateRangeFieldEntryFilter, + EmailFieldEntryFilter, + FieldEntryFilter, + NumberFieldEntryFilter, + SelectFieldEntryFilter, + TextFieldEntryFilter +} from "@/components/formUtil/FromInput/types.ts"; +import {FormSchemaField} from "@/components/formUtil/formBuilder/types.ts"; +import {Autocomplete, Button, NumberInput, Popover, TextInput} from "@mantine/core"; +import {IconFilterEdit} from "@tabler/icons-react"; +import classes from "@/components/formUtil/FormFilter/index.module.css"; +import {DateTimePicker} from "@mantine/dates"; +import {CheckboxCard} from "@/components/input/CheckboxCard"; + +type FilterFieldProps = { + field: FormSchemaField + filter: T + setFilter: (value: T) => void, +} + +export const FilterField = ({field, filter, setFilter}: FilterFieldProps) => { + + return <> + + + + + + { + (filter.dataType === "text" || filter.dataType === "email") && + + } + { + filter.dataType === "select" && + + } + { + (filter.dataType === "date" || filter.dataType === "date-range") && + + } + { + filter.dataType === "number" && + + } + { + filter.dataType === "checkbox" && + + } + + + +} + +export const FilterDateField = ({ + filter, + setFilter + }: FilterFieldProps) => { + return
+ setFilter({...filter, min: v})} + /> + + setFilter({...filter, max: v})} + /> +
+} + +export const FilterNumberField = ({filter, setFilter}: FilterFieldProps) => { + return
+ setFilter({...filter, min: v})} + /> + + setFilter({...filter, max: v})} + /> +
+} + +export const FilterTextField = ({ + filter, + setFilter + }: FilterFieldProps) => { + return setFilter({...filter, value: e.currentTarget.value})} + /> +} + +export const FilterSelectField = ({ + field, + filter, + setFilter + }: FilterFieldProps) => { + return setFilter({...filter, value: s})} + /> + +} + +export const CheckboxFilterField = ({filter, setFilter}: FilterFieldProps) => { + + const checked = !!filter.value + + const indeterminate = filter.value === null + + return { + if (checked) { + setFilter({...filter, value: false}) + } else if (filter.value === false) { + setFilter({...filter, value: null}) + } else { + setFilter({...filter, value: true}) + } + }} + /> +} \ No newline at end of file diff --git a/src/components/formUtil/FormFilter/index.module.css b/src/components/formUtil/FormFilter/index.module.css new file mode 100644 index 0000000..0e8c72a --- /dev/null +++ b/src/components/formUtil/FormFilter/index.module.css @@ -0,0 +1,11 @@ +.filterButton { + max-width: 250px; +} + +.filterButtonLabel { + display: inline-block; + max-width: calc(100%); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} \ No newline at end of file diff --git a/src/components/formUtil/FormFilter/index.tsx b/src/components/formUtil/FormFilter/index.tsx new file mode 100644 index 0000000..6c77185 --- /dev/null +++ b/src/components/formUtil/FormFilter/index.tsx @@ -0,0 +1,134 @@ +import {FormSchema, FormSchemaField} from "../formBuilder/types.ts"; +import {useForm} from "@mantine/form"; +import {FieldEntriesFilter, FieldEntryFilter} from "@/components/formUtil/FromInput/types.ts"; +import {Button, Group, Popover, Switch, Text} from "@mantine/core"; +import {IconFilter, IconFilterOff, IconFilterPlus} from "@tabler/icons-react"; +import {FilterField} from "@/components/formUtil/FormFilter/FilterField.tsx"; + +/** + * todo + * + * @param field The field to create the default value for + */ +const createDefaultFilter = (field: FormSchemaField): FieldEntryFilter => { + switch (field.dataType) { + case "text": + return { + value: null, + dataType: field.dataType, + } + case "email": + return { + value: null, + dataType: field.dataType, + } + case "number": + return { + min: null, + max: null, + dataType: field.dataType, + } + case "checkbox": + return { + value: null, + dataType: field.dataType, + } + case "select": + return { + value: null, + dataType: field.dataType, + } + case "date": + return { + min: null, + max: null, + dataType: field.dataType, + } + case "date-range": + return { + min: null, + max: null, + dataType: field.dataType, + } + } + +} + +/** + * This renders a form based on a schema + * + * @see assembleFilter + * @see FormBuilder + * + * @param schema The schema to render + * @param label The label for the filter button + * @param defaultValue The default value for the filter + * @param onChange The function to call when the filter changes + */ +export default function FormFilter({schema, label, defaultValue, onChange}: { + schema: FormSchema, + label?: string, + defaultValue?: FieldEntriesFilter, + onChange?: (filter: FieldEntriesFilter) => void, +}) { + + + const formValues = useForm({ + initialValues: defaultValue ?? {} as FieldEntriesFilter, + onValuesChange: (values) => { + onChange?.(values) + } + }) + + const toggleField = (field: FormSchemaField) => { + if (formValues.values[field.id] !== undefined) { + formValues.setValues({[field.id]: undefined}) + } else { + formValues.setFieldValue(field.id, createDefaultFilter(field)) + } + } + + return <> + + + + + +
+ {schema.fields.map(field => ( +
+ + toggleField(field)} + size={"sm"} + onLabel={} + offLabel={} + /> + + {field.label} + + +
+ ))} +
+
+
+ + {schema + .fields + .filter(field => formValues.values[field.id] !== undefined) + .map(field => { + return ( + formValues.setFieldValue(field.id, value)} + /> + ) + })} + +} \ No newline at end of file diff --git a/src/components/formUtil/FormFilter/util.ts b/src/components/formUtil/FormFilter/util.ts new file mode 100644 index 0000000..940badd --- /dev/null +++ b/src/components/formUtil/FormFilter/util.ts @@ -0,0 +1,45 @@ +import {FieldEntriesFilter} from "@/components/formUtil/FromInput/types.ts"; + +/** + * This function takes a filter object and assembles it into a filter string compatible with pocketbase + * @param values The filter object + * @param prefix The prefix to add to the field keys (e.g. questionData) this is useful for nested fields + */ +export const assembleFilter = (values: FieldEntriesFilter, prefix?: string) => { + const filter: string[] = [] + + Object.keys(values).forEach(key => { + const field = values[key] + + if (field === undefined) return + + // add prefix to key if it exists + if (prefix) key = `${prefix}.${key}` + + switch (field.dataType) { + case "text": + case "email": + case "select": + if (field.value !== null && field.value !== '') filter.push(`${key}.value~'${field.value}'`) + break + case "number": + if (field.min !== null && field.min !== '') filter.push(`${key}.value>=${field.min}`) + if (field.max !== null && field.max !== '') filter.push(`${key}.value<=${field.max}`) + break + case "checkbox": + if (field.value) filter.push(`${key}.value=${field.value}`) + if (field.value === false) filter.push(`${key}.value!=true`) + break + case "date": + if (field.min !== null) filter.push(`${key}.value>='${field.min.toISOString()}'`) + if (field.max !== null) filter.push(`${key}.value<='${field.max.toISOString()}'`) + break + case "date-range": + if (field.min !== null) filter.push(`${key}.value.0>='${field.min.toISOString()}'`) + if (field.max !== null) filter.push(`${key}.value.1<='${field.max.toISOString()}'`) + break + } + }) + + return filter +} \ No newline at end of file diff --git a/src/components/formUtil/FromInput/formFieldComponents.tsx b/src/components/formUtil/FromInput/formFieldComponents.tsx index 9b9b3d7..1caeffe 100644 --- a/src/components/formUtil/FromInput/formFieldComponents.tsx +++ b/src/components/formUtil/FromInput/formFieldComponents.tsx @@ -135,10 +135,10 @@ const Wrapper = ({field, children}: { children: ReactNode }) => { return - {field.label} + {field.label} {field.description && <> - + Mehr anzeigen} diff --git a/src/components/formUtil/FromInput/index.tsx b/src/components/formUtil/FromInput/index.tsx index 15db370..5039493 100644 --- a/src/components/formUtil/FromInput/index.tsx +++ b/src/components/formUtil/FromInput/index.tsx @@ -1,5 +1,5 @@ import {FormSchema} from "../formBuilder/types.ts"; -import {Button, Group,} from "@mantine/core"; +import {ActionIcon, Button, Code, CopyButton, Group, Table, Text, Tooltip} from "@mantine/core"; import {useForm} from "@mantine/form"; import ShowDebug from "../../ShowDebug.tsx"; @@ -7,12 +7,16 @@ import {FieldEntries} from "@/components/formUtil/FromInput/types.ts"; import {createValidationFromSchema} from "@/components/formUtil/FromInput/validation.ts"; import { CheckboxField, - DateField, DateRangeField, + DateField, + DateRangeField, EmailField, FormTextareaField, FormTextField, - NumberField, SelectField + NumberField, + SelectField } from "@/components/formUtil/FromInput/formFieldComponents.tsx"; +import {transformData} from "@/components/formUtil/formTable"; +import {IconCheck, IconHash} from "@tabler/icons-react"; /** * This function creates default values based on a data type (e.g. "" for text, false for checkbox) @@ -24,7 +28,7 @@ import { * @param initialEntries The already existing entries */ const createDefaultValuesFromSchema = (schema: FormSchema, initialEntries?: FieldEntries): FieldEntries => { - const entries: FieldEntries = initialEntries ?? {} + const entries: FieldEntries = transformData(initialEntries ?? {}) schema.fields.forEach(field => { // if the field already exists, skip it @@ -166,9 +170,25 @@ export default function FormInput({schema, onAbort, onSubmit, disabled, initialD -
-                {JSON.stringify(formValues, null, 2)}
-            
+ ([ + + {({copied, copy}) => ( + + + {copied ? : } + + + )} + , + + {field.label} + , + {JSON.stringify(formValues.values[field.id]?.value)} + ])), + head: ["Field", "Value"] + }}/> } \ No newline at end of file diff --git a/src/components/formUtil/FromInput/types.ts b/src/components/formUtil/FromInput/types.ts index 87f92b9..57613dc 100644 --- a/src/components/formUtil/FromInput/types.ts +++ b/src/components/formUtil/FromInput/types.ts @@ -61,8 +61,8 @@ export type EmailFieldEntry = { } export type NumberFieldEntryFilter = { - min: number | null - max: number | null + min: number | string | null + max: number | string | null dataType: "number" } diff --git a/src/components/formUtil/formTable/RenderCell.tsx b/src/components/formUtil/formTable/RenderCell.tsx index e53e3eb..65737a9 100644 --- a/src/components/formUtil/formTable/RenderCell.tsx +++ b/src/components/formUtil/formTable/RenderCell.tsx @@ -35,7 +35,7 @@ const DateTimeCell = ({date}: { date: Date | string }) => { const DateRangeCell = ({dateRange}: { dateRange: [Date | null, Date | null] }) => { - const [start, end] = dateRange + const [start, end] = dateRange.map(date => date === null ? null : new Date(date)) if (start === null || end === null) { return diff --git a/src/components/formUtil/util.ts b/src/components/formUtil/util.ts index 0ab1747..019c9af 100644 --- a/src/components/formUtil/util.ts +++ b/src/components/formUtil/util.ts @@ -1,5 +1,7 @@ import {FieldDataType, FormSchemaField} from "./formBuilder/types.ts"; -import {nanoid} from "nanoid"; +import {customAlphabet} from "nanoid"; + +const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRTSUVWXYZ', 6) export const humanReadableField = (fieldType: FieldDataType): string => { switch (fieldType) { diff --git a/src/components/input/CheckboxCard/index.module.css b/src/components/input/CheckboxCard/index.module.css index 1917b20..12bb6fc 100644 --- a/src/components/input/CheckboxCard/index.module.css +++ b/src/components/input/CheckboxCard/index.module.css @@ -11,7 +11,7 @@ background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8)); - &[aria-error="true"] { + &[data-error="true"] { border-color: var(--mantine-color-error); } } diff --git a/src/components/input/CheckboxCard/index.tsx b/src/components/input/CheckboxCard/index.tsx index 3b8cd38..f1b6661 100644 --- a/src/components/input/CheckboxCard/index.tsx +++ b/src/components/input/CheckboxCard/index.tsx @@ -7,11 +7,12 @@ export type CheckboxCardProps = InputWrapperProps & CheckboxProps export function CheckboxCard(props: CheckboxCardProps) { return ( -
+
- {props.label} + {props.label} {props.description && - } + + + } {props.error && {props.error}}
diff --git a/src/components/input/RecordSearchInput.tsx b/src/components/input/RecordSearchInput.tsx index b02bb67..efa509c 100644 --- a/src/components/input/RecordSearchInput.tsx +++ b/src/components/input/RecordSearchInput.tsx @@ -1,7 +1,7 @@ import {RecordModel} from "pocketbase"; import {UseMutationResult} from "@tanstack/react-query"; import {CheckIcon, Combobox, Group, Pill, PillsInput, PillsInputProps, Stack, Text, useCombobox} from "@mantine/core"; -import {useState} from "react"; +import {useEffect, useState} from "react"; /* * GenericRecordInputProps is a generic type that describes the props that are common to all Record Input Wrappers @@ -57,6 +57,11 @@ export default function RecordSearchInput(props: { props.setSelectedRecords(props.selectedRecords.filter((record) => record.id !== val)) } + useEffect(() => { + props.recordSearchMutation.mutate('') + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const searchResults = (props.recordSearchMutation.data || []) .map((recordView) => ( } + + { + refreshUser() + }} + > + + + + { refetchInterval: oneMinuteInMs }) + const refreshUserQuery = useQuery({ + queryKey: ["refreshUser"], + queryFn: async () => { + try { + const {record, token} = await pb.collection(PB_USER_COLLECTION).authRefresh({ + expand: "memberOf" + }) + pb.authStore.save(token, record) + return record + } catch (e) { + pb.authStore.clear() + return null + } + }, + refetchInterval: oneMinuteInMs + }) + const [user, setUser] = useState(pb.authStore.model) pb.authStore.onChange((_, userRecord) => { setUser(userRecord) }) - const refreshUser = useCallback(async () => { - await pb.collection(PB_USER_COLLECTION).authRefresh({ - expand: "memberOf" - }).catch(() => { - pb.authStore.clear() - }) - }, [pb]) - const ldapLogin = useCallback(async (usernameOrCN: string, password: string) => { await pb.send("/api/ldap/login", { method: "POST", @@ -126,9 +134,7 @@ const PocketData = () => { pb.collection(idOrName).unsubscribe(topic) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, deps ? deps : []); - - useInterval(refreshUser, oneMinuteInMs) + }, deps ? deps : []) return { ldapLogin, @@ -136,7 +142,7 @@ const PocketData = () => { logout, user: user as UserModal | null, pb, - refreshUser, + refreshUser: refreshUserQuery.refetch, useSubscription, apiIsHealthy: apiIsHealthyQuery.data ?? false } diff --git a/src/models/EventTypes.ts b/src/models/EventTypes.ts index ea690a9..bd92884 100644 --- a/src/models/EventTypes.ts +++ b/src/models/EventTypes.ts @@ -39,6 +39,8 @@ export type EventListModel = { event: string entryQuestionSchema: FormSchema | null entryStatusSchema: FormSchema | null; + ignoreDefaultEntryQuestionScheme: boolean | null; + ignoreDefaultEntryStatusSchema: boolean | null; expand?: { event: EventModel; } @@ -49,7 +51,7 @@ export type EventListSlotModel = { eventList: string; startDate: string; endDate: string; - maxEntries: number | null; + maxEntries: number; description: string | null; expand?: { eventList: EventListModel; @@ -59,7 +61,7 @@ export type EventListSlotModel = { export type EventListSlotsWithEntriesCountModel = EventListSlotModel & { entriesCount: number } & Pick - & Pick + & Pick export type EventListSlotEntryModel = { entryQuestionData: FieldEntries; @@ -91,4 +93,4 @@ export type EventListSlotEntriesWithUserModel = } } & Pick - & Pick \ No newline at end of file + & Pick \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EditEventRouter.tsx b/src/pages/events/e/:eventId/EditEventRouter.tsx index 0503108..5119a34 100644 --- a/src/pages/events/e/:eventId/EditEventRouter.tsx +++ b/src/pages/events/e/:eventId/EditEventRouter.tsx @@ -7,6 +7,7 @@ import { Alert, Anchor, Breadcrumbs, + Code, createPolymorphicComponent, Grid, Group, @@ -41,6 +42,8 @@ import {useEventRights} from "@/pages/events/util.ts"; import EventFavourites from "./EventComponents/EventFavourites.tsx"; import {forwardRef} from "react"; import {APP_URL} from "../../../../../config.ts"; +import ShowDebug from "@/components/ShowDebug.tsx"; +import {pprintDateTime} from "@/lib/datetime.ts"; type NavIconProps = { @@ -148,6 +151,13 @@ export default function EditEventRouter() {
+ + Event Id - {event.id} +
+ created - {pprintDateTime(event.created)} +
+ updated - {pprintDateTime(event.updated)} +
diff --git a/src/pages/events/e/:eventId/EventComponents/EventSettings/EventSettingsRouter.tsx b/src/pages/events/e/:eventId/EventComponents/EventSettings/EventSettingsRouter.tsx index 6909a4c..f54b4b2 100644 --- a/src/pages/events/e/:eventId/EventComponents/EventSettings/EventSettingsRouter.tsx +++ b/src/pages/events/e/:eventId/EventComponents/EventSettings/EventSettingsRouter.tsx @@ -90,7 +90,7 @@ export const EventSettingsMenu = ({event, target}: { event: EventModel, target: { - nav.map(({label, children,}, index, array) =>
+ nav.map(({label, children,}, index) =>
{label} {children.map(({title, icon, to}) => ( @@ -101,7 +101,6 @@ export const EventSettingsMenu = ({event, target}: { event: EventModel, target: )} ))} - {index !== array.length - 1 && }
) } diff --git a/src/pages/events/e/:eventId/EventLists/:listId/EventListRouter.tsx b/src/pages/events/e/:eventId/EventLists/:listId/EventListRouter.tsx index 3648167..a690adb 100644 --- a/src/pages/events/e/:eventId/EventLists/:listId/EventListRouter.tsx +++ b/src/pages/events/e/:eventId/EventLists/:listId/EventListRouter.tsx @@ -1,5 +1,5 @@ import {useQuery} from "@tanstack/react-query"; -import {Alert, Breadcrumbs, Button, Group, LoadingOverlay, Title} from "@mantine/core"; +import {Alert, Breadcrumbs, Button, Code, Group, LoadingOverlay, Title} from "@mantine/core"; import { IconCheckupList, IconClockCog, @@ -13,13 +13,15 @@ import {Link, Navigate, NavLink, Route, Routes, useParams} from "react-router-do import InnerHtml from "@/components/InnerHtml"; import {EventModel} from "@/models/EventTypes.ts"; import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx"; -import SlotsTable from "./EventListSlotsTable"; -import EventListSettings from "@/pages/events/e/:eventId/EventLists/:listId/EventListSettings/EventListSettings.tsx"; -import EventListEntryQuestionSettings - from "@/pages/events/e/:eventId/EventLists/:listId/EventListSettings/EventListEntryQuestionSettings.tsx"; -import EventListEntryStatusSettings - from "@/pages/events/e/:eventId/EventLists/:listId/EventListSettings/EventListEntryStatusSettings.tsx"; +import ListSlots from "./ListSlots"; import TextWithIcon from "@/components/layout/TextWithIcon"; +import ListSettings from "@/pages/events/e/:eventId/EventLists/:listId/ListSettings/ListSettings.tsx"; +import ListEntryQuestionSettings + from "@/pages/events/e/:eventId/EventLists/:listId/ListSettings/ListEntryQuestionSettings.tsx"; +import ListEntryStatusSettings + from "@/pages/events/e/:eventId/EventLists/:listId/ListSettings/ListEntryStatusSettings.tsx"; +import ShowDebug from "@/components/ShowDebug.tsx"; +import {pprintDateTime} from "@/lib/datetime.ts"; export default function EventListRouter({event}: { event: EventModel }) { @@ -57,6 +59,14 @@ export default function EventListRouter({event}: { event: EventModel }) { + + List Id - {list.id} +
+ created - {pprintDateTime(list.created)} +
+ updated - {pprintDateTime(list.updated)} +
+ : }> Liste ist für Anmeldungen {list.open ? "geöffnet" : "geschlossen"} @@ -90,7 +100,7 @@ export default function EventListRouter({event}: { event: EventModel }) { {[ { icon: , - to: `/events/e/${event.id}/lists/overview/${list.id}/entries`, + to: `/events/e/${event.id}/lists/overview/${list.id}/slots`, title: "Zeitslots" }, { @@ -123,11 +133,11 @@ export default function EventListRouter({event}: { event: EventModel }) { - }/> - }/> - }/> - }/> - }/> + }/> + }/> + }/> + }/> + }/>
} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntriesTable.module.css b/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntriesTable.module.css deleted file mode 100644 index d302bb5..0000000 --- a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntriesTable.module.css +++ /dev/null @@ -1,13 +0,0 @@ -.bottomRow { - margin-left: var(--mantine-spacing-lg); - display: flex; - justify-content: space-between; - align-items: center; - gap: var(--gap); -} - -.table { - display: flex; - flex-direction: column; - gap: calc(var(--gap) / 2); -} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntriesTable.tsx b/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntriesTable.tsx deleted file mode 100644 index f413fba..0000000 --- a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntriesTable.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import {EventListModel, EventListSlotsWithEntriesCountModel, EventModel} from "@/models/EventTypes.ts"; -import {useQuery} from "@tanstack/react-query"; -import {useState} from "react"; -import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx"; -import classes from "./EventListSlotEntriesTable.module.css"; -import {Loader, Pagination, Stack, Text, ThemeIcon} from "@mantine/core"; -import {transformData} from "@/components/formUtil/formTable"; -import {EventListSlotEntryRow} from "./EventListSlotEntryRow.tsx"; -import {IconDatabaseOff} from "@tabler/icons-react"; - -export const EventListSlotEntriesTable = ({slot, list, event, refetch, visible}: - { - visible: boolean, - refetch: () => void, - slot: EventListSlotsWithEntriesCountModel, - list: EventListModel, - event: EventModel - }) => { - const {pb} = usePB() - - const [page, setPage] = useState(1) - - const query = useQuery({ - queryKey: ["event", event.id, "list", list.id, "slot", slot.id, "entriesWithUser", page], - queryFn: async () => { - - const res = await pb.collection("eventListSlotEntriesWithUser").getList(page, 50, { - filter: `eventListsSlot='${slot.id}'`, - expand: "user" - }) - - return { - ...res, - items: res.items.map(item => ({ - ...item, - questionData: transformData(item.questionData ?? {}), - statusData: transformData(item.statusData ?? {}), - })) - } - }, - enabled: visible - }) - - if (query.isLoading) { - return - - Anmeldungen werden geladen - - } - - if (query.data?.totalItems === 0) { - return - - - - Keine Anmeldungen vorhanden - - } - - if (query.isError) return - - return
- { - query.data?.items.map(entry => ( - { - query.refetch() - refetch() - }} - /> - )) - } - -
- - {query.data?.totalItems} Anmeldungen - - -
-
-} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntryRow.tsx b/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntryRow.tsx deleted file mode 100644 index 68106bc..0000000 --- a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntryRow.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { - EventListModel, - EventListSlotEntriesWithUserModel, - EventListSlotModel, - EventModel -} from "@/models/EventTypes.ts"; -import {useDisclosure} from "@mantine/hooks"; -import classes from "./EventListSlotEntryRow.module.css"; -import {ActionIcon, Box, Collapse, Group, SimpleGrid, Tooltip} from "@mantine/core"; -import {IconCheckupList, IconForms, IconUserMinus, IconUserPlus} from "@tabler/icons-react"; -import {renderEntries} from "@/components/formUtil/formTable"; -import RenderCell from "@/components/formUtil/formTable/RenderCell.tsx"; -import EditSlotEntryMenu from "@/pages/events/e/:eventId/EventLists/components/EditSlotEntryMenu.tsx"; -import {RenderUserName} from "@/components/users/modals/util.tsx"; - - -export const EventListSlotEntryDetails = ({entry}: { - entry: EventListSlotEntriesWithUserModel -}) => { - const questionSchemaFields = [ - ...entry.entryQuestionSchema?.fields ?? [], - ...entry.defaultEntryQuestionSchema?.fields ?? [], - ] - - const statusSchemaFields = [ - ...entry.entryStatusSchema?.fields ?? [], - ...entry.defaultEntryStatusSchema?.fields ?? [], - ] - - return <> - - {renderEntries(entry.entryQuestionData ?? {}, questionSchemaFields).map(({label, value}) => { - return
-
- -
- -
- {label} -
- - - -
- })} - - {renderEntries(entry.entryStatusData ?? {}, statusSchemaFields).map(({label, value}) => { - return
-
- -
- -
- {label} -
- - - -
- })} -
- -} - -/** - * Renders a row with all entries for a slot - * @param entry - entry to render - * @param slot - slot the entry belongs to - * @param list - list the entry belongs to - * @param event - event the entry belongs to - * @param refetch - refetch function for the entries - * @constructor - */ -export const EventListSlotEntryRow = ({entry, refetch}: { - entry: EventListSlotEntriesWithUserModel, - slot: EventListSlotModel, - list: EventListModel, - event: EventModel, - refetch: () => void -}) => { - const [expanded, expandedHandler] = useDisclosure(false) - return
-
- - - {expanded ? : } - - - -
- - - - -
- - - - -
-} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/:listId/EventListSettings/EventListEntryQuestionSettings.tsx b/src/pages/events/e/:eventId/EventLists/:listId/ListSettings/ListEntryQuestionSettings.tsx similarity index 62% rename from src/pages/events/e/:eventId/EventLists/:listId/EventListSettings/EventListEntryQuestionSettings.tsx rename to src/pages/events/e/:eventId/EventLists/:listId/ListSettings/ListEntryQuestionSettings.tsx index 316d105..a6a1ab6 100644 --- a/src/pages/events/e/:eventId/EventLists/:listId/EventListSettings/EventListEntryQuestionSettings.tsx +++ b/src/pages/events/e/:eventId/EventLists/:listId/ListSettings/ListEntryQuestionSettings.tsx @@ -8,18 +8,27 @@ import {FormSchema} from "@/components/formUtil/formBuilder/types.ts"; import FormBuilder from "@/components/formUtil/formBuilder"; import FormFieldsPreview from "@/components/formUtil/formBuilder/FormFieldsPreview.tsx"; import {IconAlertTriangle} from "@tabler/icons-react"; +import {useForm} from "@mantine/form"; +import {CheckboxCard} from "@/components/input/CheckboxCard"; -export default function EventListEntryQuestionSettings({list, event}: { list: EventListModel, event: EventModel }) { +export default function ListEntryQuestionSettings({list, event}: { list: EventListModel, event: EventModel }) { const {pb} = usePB() + const formValues = useForm({ + initialValues: { + ignoreDefaultEntryQuestionScheme: list.ignoreDefaultEntryQuestionScheme || false, + } + }) + const editMutation = useMutation({ mutationFn: async (schema: FormSchema) => { return await pb.collection("eventLists").update(list.id, { - entryQuestionSchema: schema + entryQuestionSchema: schema, + ...formValues.values }) }, onSuccess: () => { - showSuccessNotification("Fragen gespeichert") + showSuccessNotification("Fragen und Liste gespeichert") return queryClient.invalidateQueries({queryKey: ["event", event.id, "list", list.id]}) } }) @@ -29,7 +38,7 @@ export default function EventListEntryQuestionSettings({list, event}: { list: Ev - {event.defaultEntryQuestionSchema && ( + {(!list.ignoreDefaultEntryQuestionScheme && event.defaultEntryQuestionSchema) && ( Folgende Fragen sind standardmäßig für dieses Event vorgesehen @@ -39,6 +48,13 @@ export default function EventListEntryQuestionSettings({list, event}: { list: Ev )} + + }> Wenn du Felder entfernst, werden alle Daten, die für diese Felder gespeichert wurden nicht mehr angezeigt. @@ -48,7 +64,7 @@ export default function EventListEntryQuestionSettings({list, event}: { list: Ev defaultValue={list.entryQuestionSchema || {fields: []}} onSubmit={(schema) => editMutation.mutate(schema)} withPreview - additionalSchemaToPreview={event.defaultEntryQuestionSchema || {fields: []}} + additionalSchemaToPreview={!list.ignoreDefaultEntryQuestionScheme && event.defaultEntryQuestionSchema || {fields: []}} />
} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/:listId/EventListSettings/EventListEntryStatusSettings.tsx b/src/pages/events/e/:eventId/EventLists/:listId/ListSettings/ListEntryStatusSettings.tsx similarity index 55% rename from src/pages/events/e/:eventId/EventLists/:listId/EventListSettings/EventListEntryStatusSettings.tsx rename to src/pages/events/e/:eventId/EventLists/:listId/ListSettings/ListEntryStatusSettings.tsx index 31e9f36..f33e316 100644 --- a/src/pages/events/e/:eventId/EventLists/:listId/EventListSettings/EventListEntryStatusSettings.tsx +++ b/src/pages/events/e/:eventId/EventLists/:listId/ListSettings/ListEntryStatusSettings.tsx @@ -8,18 +8,27 @@ import {FormSchema} from "@/components/formUtil/formBuilder/types.ts"; import FormBuilder from "@/components/formUtil/formBuilder"; import FormFieldsPreview from "@/components/formUtil/formBuilder/FormFieldsPreview.tsx"; import {IconAlertTriangle} from "@tabler/icons-react"; +import {useForm} from "@mantine/form"; +import {CheckboxCard} from "@/components/input/CheckboxCard"; -export default function EventListEntryStatusSettings({list, event}: { list: EventListModel, event: EventModel }) { +export default function ListEntryStatusSettings({list, event}: { list: EventListModel, event: EventModel }) { const {pb} = usePB() - const editMutation = useMutation({ + const formValues = useForm({ + initialValues: { + ignoreDefaultEntryStatusSchema: list.ignoreDefaultEntryStatusSchema || false, + } + }) + + const editStatusMutation = useMutation({ mutationFn: async (schema: FormSchema) => { - return await pb.collection("eventLists").update(list.id, { - entryStatusSchema: schema + await pb.collection("eventLists").update(list.id, { + entryStatusSchema: schema, + ...formValues.values }) }, onSuccess: () => { - showSuccessNotification("Status Felder gespeichert") + showSuccessNotification("Status Felder und Liste gespeichert") return queryClient.invalidateQueries({queryKey: ["event", event.id, "list", list.id]}) } }) @@ -27,9 +36,9 @@ export default function EventListEntryStatusSettings({list, event}: { list: Even return
Eintrags-Status der Liste - + - {event.defaultEntryStatusSchema && ( + {(!list.ignoreDefaultEntryStatusSchema && event.defaultEntryStatusSchema) && ( Folgende Status-Felder sind standardmäßig für dieses Event vorgesehen @@ -39,6 +48,13 @@ export default function EventListEntryStatusSettings({list, event}: { list: Even )} + + }> Wenn du Felder entfernst, werden alle Daten, die für diese Felder gespeichert wurden nicht mehr angezeigt. @@ -46,9 +62,9 @@ export default function EventListEntryStatusSettings({list, event}: { list: Even editMutation.mutate(schema)} + onSubmit={(schema) => editStatusMutation.mutate(schema)} withPreview - additionalSchemaToPreview={event.defaultEntryStatusSchema || {fields: []}} + additionalSchemaToPreview={!list.ignoreDefaultEntryStatusSchema && event.defaultEntryStatusSchema || {fields: []}} />
} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/:listId/EventListSettings/EventListSettings.tsx b/src/pages/events/e/:eventId/EventLists/:listId/ListSettings/ListSettings.tsx similarity index 94% rename from src/pages/events/e/:eventId/EventLists/:listId/EventListSettings/EventListSettings.tsx rename to src/pages/events/e/:eventId/EventLists/:listId/ListSettings/ListSettings.tsx index 09fb785..5bd6fe4 100644 --- a/src/pages/events/e/:eventId/EventLists/:listId/EventListSettings/EventListSettings.tsx +++ b/src/pages/events/e/:eventId/EventLists/:listId/ListSettings/ListSettings.tsx @@ -11,7 +11,7 @@ import {useNavigate} from "react-router-dom"; import {useConfirmModal} from "@/components/ConfirmModal.tsx"; import {CheckboxCard} from "@/components/input/CheckboxCard"; -export default function EventListSettings({list, event}: { list: EventListModel, event: EventModel }) { +export default function ListSettings({list, event}: { list: EventListModel, event: EventModel }) { const {pb} = usePB() const navigate = useNavigate() @@ -22,7 +22,7 @@ export default function EventListSettings({list, event}: { list: EventListModel, allowOverlappingEntries: list.allowOverlappingEntries, onlyStuVeAccounts: list.onlyStuVeAccounts, favorite: list.favorite, - description: list.description || "" + description: list.description || "", } }) @@ -99,7 +99,7 @@ export default function EventListSettings({list, event}: { list: EventListModel, :nth-child(2) { + & > :nth-child(1) { width: 50%; font-size: var(--mantine-font-size-sm); } - & > :nth-child(3) { + & > :nth-child(2) { flex: 1; } } \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotRow.tsx b/src/pages/events/e/:eventId/EventLists/:listId/ListSlots/ListSlotRow.tsx similarity index 60% rename from src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotRow.tsx rename to src/pages/events/e/:eventId/EventLists/:listId/ListSlots/ListSlotRow.tsx index 54cf101..255a51f 100644 --- a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotRow.tsx +++ b/src/pages/events/e/:eventId/EventLists/:listId/ListSlots/ListSlotRow.tsx @@ -1,27 +1,24 @@ -import {EventListModel, EventListSlotsWithEntriesCountModel, EventModel} from "@/models/EventTypes.ts"; +import {EventListModel, EventListSlotsWithEntriesCountModel} from "@/models/EventTypes.ts"; import {useDisclosure} from "@mantine/hooks"; -import classes from "./EventListSlotRow.module.css"; -import {ActionIcon, Alert, Code, Collapse, Group, Modal, ThemeIcon} from "@mantine/core"; -import {IconAdjustments, IconAdjustmentsOff, IconInfoCircle, IconUsersMinus, IconUsersPlus} from "@tabler/icons-react"; -import {RenderDateRange} from "../../components/RenderDateRange.tsx"; +import classes from "./ListSlotRow.module.css"; +import {ActionIcon, Alert, Code, Group, Modal} from "@mantine/core"; +import {IconAdjustments, IconAdjustmentsOff, IconInfoCircle} from "@tabler/icons-react"; +import {RenderDateRange} from "@/pages/events/e/:eventId/EventLists/EventListComponents/RenderDateRange.tsx"; import {modals} from "@mantine/modals"; import InnerHtml from "@/components/InnerHtml"; -import {EventListSlotEntriesTable} from "./EventListSlotEntriesTable.tsx"; -import UpsertEventListSlot from "@/pages/events/e/:eventId/EventLists/components/UpsertEventListSlot.tsx"; -import EventListSlotProgress from "@/pages/events/e/:eventId/EventLists/components/EventListSlotProgress.tsx"; import ShowDebug from "@/components/ShowDebug.tsx"; +import UpsertSlot from "@/pages/events/e/:eventId/EventLists/EventListComponents/UpsertSlot.tsx"; +import SlotProgress from "@/pages/events/e/:eventId/EventLists/EventListComponents/SlotProgress.tsx"; +import {pprintDateTime} from "@/lib/datetime.ts"; -export const EventListSlotRow = ({slot, list, event, refetch}: { +export const ListSlotRow = ({slot, list, refetch}: { slot: EventListSlotsWithEntriesCountModel, list: EventListModel, - event: EventModel, refetch: () => void }) => { const [showEditModal, showEditModalHandler] = useDisclosure(false) - const [expanded, expandedHandler] = useDisclosure(false) - return (
- { + { showEditModalHandler.close() refetch() }} onAbort={showEditModalHandler.close}/> -
- - { - expanded ? : - } - +
+ - + - Listen-ID {list.id} + Slot Id - {slot.id}
- Slot-ID {slot.id} + created - {pprintDateTime(slot.created)} +
+ updated - {pprintDateTime(slot.updated)}
{ slot.description ? : @@ -85,10 +77,6 @@ export const EventListSlotRow = ({slot, list, event, refetch}: {
- - - -
) } \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/index.module.css b/src/pages/events/e/:eventId/EventLists/:listId/ListSlots/index.module.css similarity index 100% rename from src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/index.module.css rename to src/pages/events/e/:eventId/EventLists/:listId/ListSlots/index.module.css diff --git a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/index.tsx b/src/pages/events/e/:eventId/EventLists/:listId/ListSlots/index.tsx similarity index 81% rename from src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/index.tsx rename to src/pages/events/e/:eventId/EventLists/:listId/ListSlots/index.tsx index b62d09e..c14d8bf 100644 --- a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/index.tsx +++ b/src/pages/events/e/:eventId/EventLists/:listId/ListSlots/index.tsx @@ -1,4 +1,4 @@ -import {EventListModel, EventModel} from "@/models/EventTypes.ts"; +import {EventListModel} from "@/models/EventTypes.ts"; import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx"; import {useQuery} from "@tanstack/react-query"; import {Box, Button, Center, Pagination, Title} from "@mantine/core"; @@ -6,8 +6,8 @@ import {IconClockPlus} from "@tabler/icons-react"; import {useState} from "react"; import {useDisclosure} from "@mantine/hooks"; import classes from './index.module.css' -import {EventListSlotRow} from "./EventListSlotRow.tsx"; -import UpsertEventListSlot from "@/pages/events/e/:eventId/EventLists/components/UpsertEventListSlot.tsx"; +import {ListSlotRow} from "./ListSlotRow.tsx"; +import UpsertSlot from "@/pages/events/e/:eventId/EventLists/EventListComponents/UpsertSlot.tsx"; /** * Renders a table with all slots for a list @@ -16,7 +16,7 @@ import UpsertEventListSlot from "@/pages/events/e/:eventId/EventLists/components * @param event - event the list belongs to * @constructor */ -export default function SlotsTable({list, event}: { event: EventModel, list: EventListModel }) { +export default function ListSlots({list}: { list: EventListModel }) { const {pb} = usePB() @@ -39,8 +39,8 @@ export default function SlotsTable({list, event}: { event: EventModel, list: Eve
{query.data?.items.map(slot => ( - ))} @@ -53,7 +53,7 @@ export default function SlotsTable({list, event}: { event: EventModel, list: Eve Neuen Slot hinzufügen - { + { showNewSlotFormHandler.close() query.refetch() }} onAbort={showNewSlotFormHandler.close}/> diff --git a/src/pages/events/e/:eventId/EventLists/components/EditSlotEntryMenu.tsx b/src/pages/events/e/:eventId/EventLists/EventListComponents/EditSlotEntryMenu.tsx similarity index 61% rename from src/pages/events/e/:eventId/EventLists/components/EditSlotEntryMenu.tsx rename to src/pages/events/e/:eventId/EventLists/EventListComponents/EditSlotEntryMenu.tsx index 946bda1..4a98524 100644 --- a/src/pages/events/e/:eventId/EventLists/components/EditSlotEntryMenu.tsx +++ b/src/pages/events/e/:eventId/EventLists/EventListComponents/EditSlotEntryMenu.tsx @@ -4,19 +4,18 @@ import {showSuccessNotification} from "@/components/util.tsx"; import {useConfirmModal} from "@/components/ConfirmModal.tsx"; import {usePB} from "@/lib/pocketbase.tsx"; import {useDisclosure} from "@mantine/hooks"; -import { - UpdateEventListSlotEntryStatusModal -} from "@/pages/events/e/:eventId/EventLists/components/UpdateEventListSlotEntryStatusModal.tsx"; -import { - MoveEventListSlotEntryModal -} from "@/pages/events/e/:eventId/EventLists/components/MoveEventListSlotEntryModal.tsx"; -import { - UpdateEventListSlotEntryFormModal -} from "@/pages/events/e/:eventId/EventLists/components/UpdateEventListSlotEntryFormModal.tsx"; -import {ActionIcon, Button, Menu} from "@mantine/core"; -import {IconArrowsMove, IconCheckupList, IconForms, IconSettings, IconTrash} from "@tabler/icons-react"; +import {ActionIcon, HoverCard, Menu} from "@mantine/core"; +import {IconArrowsMove, IconCheckupList, IconSend, IconSettings, IconTrash} from "@tabler/icons-react"; import {getUserName} from "@/components/users/modals/util.tsx"; +import { + UpdateEntryStatusModal +} from "@/pages/events/e/:eventId/EventLists/EventListComponents/UpdateEntryStatusModal.tsx"; +import {MoveEntryModal} from "@/pages/events/e/:eventId/EventLists/EventListComponents/MoveEntryModal.tsx"; +import EntryStatusSpoiler from "@/pages/events/e/:eventId/EventLists/EventListComponents/EntryStatusSpoiler.tsx"; +import {getListSchemas} from "@/pages/events/util.ts"; + + export default function EditSlotEntryMenu({entry, refetch}: { refetch: () => void, entry: EventListSlotEntriesWithUserModel @@ -28,8 +27,6 @@ export default function EditSlotEntryMenu({entry, refetch}: { const [showMoveEntryModal, showMoveEntryModalHandler] = useDisclosure(false) - const [showEditFormModal, showEditFormModalHandler] = useDisclosure(false) - const deleteEntryMutation = useMutation({ mutationFn: async () => { await pb.collection("eventListSlotEntries").delete(entry.id) @@ -46,62 +43,67 @@ export default function EditSlotEntryMenu({entry, refetch}: { onConfirm: () => deleteEntryMutation.mutate() }) + const {statusSchema} = getListSchemas(entry) + + const noStatusField = statusSchema.fields.length === 0 + return <> - - - - - + + + + + + + + + + + } + disabled + > + Person benachrichtigen + } onClick={showMoveEntryModalHandler.toggle} > Eintrag verschieben - } - onClick={showEditFormModalHandler.toggle} - > - Formular - } onClick={toggleConfirmModal} diff --git a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntryRow.module.css b/src/pages/events/e/:eventId/EventLists/EventListComponents/EntryQuestionAndStatusData.module.css similarity index 58% rename from src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntryRow.module.css rename to src/pages/events/e/:eventId/EventLists/EventListComponents/EntryQuestionAndStatusData.module.css index 9611c15..cb26ab6 100644 --- a/src/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntryRow.module.css +++ b/src/pages/events/e/:eventId/EventLists/EventListComponents/EntryQuestionAndStatusData.module.css @@ -1,25 +1,3 @@ -.entryContainer { - margin-left: var(--mantine-spacing-lg); - display: flex; - flex-direction: column; -} - -.entryRow { - display: flex; - justify-content: space-between; - align-items: center; - gap: var(--gap); - width: 100%; - - & > * { - font-size: var(--mantine-font-size-sm); - } - - & > :nth-child(2) { - flex: 1; - } -} - .dataGrid { padding: var(--gap) 0; } @@ -43,4 +21,7 @@ color: var(--mantine-color-dimmed); border-bottom: var(--border); margin-bottom: var(--mantine-spacing-xs); + word-break: break-all; + overflow-wrap: break-word; + hyphens: auto; } \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/EventListComponents/EntryQuestionAndStatusData.tsx b/src/pages/events/e/:eventId/EventLists/EventListComponents/EntryQuestionAndStatusData.tsx new file mode 100644 index 0000000..f4aaebf --- /dev/null +++ b/src/pages/events/e/:eventId/EventLists/EventListComponents/EntryQuestionAndStatusData.tsx @@ -0,0 +1,53 @@ +import {EventListSlotEntriesWithUserModel} from "@/models/EventTypes.ts"; +import {getListSchemas} from "@/pages/events/util.ts"; +import {Box, SimpleGrid} from "@mantine/core"; +import classes from "./EntryQuestionAndStatusData.module.css"; +import {renderEntries} from "@/components/formUtil/formTable"; +import {IconCheckupList, IconForms} from "@tabler/icons-react"; +import RenderCell from "@/components/formUtil/formTable/RenderCell.tsx"; + +/** + * This component renders the details of an event list slot entry + * It displays the question and status data of the entry + * @param entry - the entry to display + */ +export const EntryQuestionAndStatusData = ({entry}: { + entry: EventListSlotEntriesWithUserModel +}) => { + + const {questionSchema, statusSchema} = getListSchemas(entry) + + return <> + + {renderEntries(entry.entryQuestionData ?? {}, questionSchema.fields).map(({label, value}) => { + return
+
+ +
+ +
+ {label} +
+ + + +
+ })} + + {renderEntries(entry.entryStatusData ?? {}, statusSchema.fields).map(({label, value}) => { + return
+
+ +
+ +
+ {label} +
+ + + +
+ })} +
+ +} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/EventListComponents/EntryStatusSpoiler.module.css b/src/pages/events/e/:eventId/EventLists/EventListComponents/EntryStatusSpoiler.module.css new file mode 100644 index 0000000..72f10e5 --- /dev/null +++ b/src/pages/events/e/:eventId/EventLists/EventListComponents/EntryStatusSpoiler.module.css @@ -0,0 +1,30 @@ +.statusCell { + display: flex; + align-items: center; + flex-direction: row; + justify-content: space-between; + flex-wrap: nowrap; + gap: var(--gap); + padding: 10px; + + &:not(:last-of-type) { + border-bottom: 1px solid var(--border-color); + } +} + +.statusTitle { + width: 40%; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.statusValue { + width: 60%; + + overflow: hidden; + display: flex; + align-items: center; + justify-content: start; +} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/EventListComponents/EntryStatusSpoiler.tsx b/src/pages/events/e/:eventId/EventLists/EventListComponents/EntryStatusSpoiler.tsx new file mode 100644 index 0000000..0479ca6 --- /dev/null +++ b/src/pages/events/e/:eventId/EventLists/EventListComponents/EntryStatusSpoiler.tsx @@ -0,0 +1,23 @@ +import {EventListSlotEntriesWithUserModel} from "@/models/EventTypes.ts"; +import {getListSchemas} from "@/pages/events/util.ts"; +import {renderEntries} from "@/components/formUtil/formTable"; +import classes from "./EntryStatusSpoiler.module.css"; +import RenderCell from "@/components/formUtil/formTable/RenderCell.tsx"; + +export default function EntryStatusSpoiler({entry}: { entry: EventListSlotEntriesWithUserModel }) { + + const {statusSchema} = getListSchemas(entry) + + return
+ {renderEntries(entry.entryStatusData ?? {}, statusSchema.fields).map(({label, value}) => { + return
+
+ {label} +
+
+ +
+
+ })} +
+} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/components/MoveEventListSlotEntryModal.tsx b/src/pages/events/e/:eventId/EventLists/EventListComponents/MoveEntryModal.tsx similarity index 73% rename from src/pages/events/e/:eventId/EventLists/components/MoveEventListSlotEntryModal.tsx rename to src/pages/events/e/:eventId/EventLists/EventListComponents/MoveEntryModal.tsx index ba91b00..3619823 100644 --- a/src/pages/events/e/:eventId/EventLists/components/MoveEventListSlotEntryModal.tsx +++ b/src/pages/events/e/:eventId/EventLists/EventListComponents/MoveEntryModal.tsx @@ -2,12 +2,15 @@ import {EventListSlotEntriesWithUserModel} from "@/models/EventTypes.ts"; import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx"; import {useMutation, useQuery} from "@tanstack/react-query"; import {showSuccessNotification} from "@/components/util.tsx"; -import {Alert, Button, Group, Modal, Select} from "@mantine/core"; +import {Alert, Button, Group, Modal, Select, SelectProps, Text} from "@mantine/core"; import {useEffect, useState} from "react"; import {pprintDateTime} from "@/lib/datetime.ts"; import {getUserName, RenderUserName} from "@/components/users/modals/util.tsx"; +import {CheckboxCard} from "@/components/input/CheckboxCard"; +import {IconCheck} from "@tabler/icons-react"; +import SlotProgress from "@/pages/events/e/:eventId/EventLists/EventListComponents/SlotProgress.tsx"; -export const MoveEventListSlotEntryModal = ({opened, close, refetch, entry}: { +export const MoveEntryModal = ({opened, close, refetch, entry}: { opened: boolean, close: () => void, refetch: () => void, @@ -20,6 +23,8 @@ export const MoveEventListSlotEntryModal = ({opened, close, refetch, entry}: { const [selectedList, setSelectedList] = useState(entry.eventList) + const [showFullSlots, setShowFullSlots] = useState(false) + useEffect(() => { setSelectedSlot(null) }, [selectedList]); @@ -52,17 +57,34 @@ export const MoveEventListSlotEntryModal = ({opened, close, refetch, entry}: { }) const slotsQuery = useQuery({ - queryKey: ["eventListSlots", entry.eventList, "notFull", selectedList], + queryKey: ["eventListSlots", entry.eventList, "notFull", selectedList, showFullSlots], queryFn: async () => ( await pb.collection("eventListSlotsWithEntriesCount").getFullList({ filter: `eventList='${selectedList}' - &&(maxEntries=0 || entriesCount < maxEntries)`, + ${showFullSlots ? "" : "&&(maxEntries=0 || entriesCount < maxEntries)"} `, sort: "startDate" }) ), enabled: selectedList !== null && opened }) + const renderSelectOption: SelectProps['renderOption'] = ({option, checked}) => { + + const slot = slotsQuery.data?.find(slot => slot.id === option.value) + + if (!slot) return <>Not Found + + return ( + + + {option.label} + + {} + {checked && } + + ) + } + return setSelectedSlot(value)} /> + setShowFullSlots(!showFullSlots)} + /> + { selectedList !== entry.eventList && ( diff --git a/src/pages/events/e/:eventId/EventLists/components/RenderDateRange.tsx b/src/pages/events/e/:eventId/EventLists/EventListComponents/RenderDateRange.tsx similarity index 100% rename from src/pages/events/e/:eventId/EventLists/components/RenderDateRange.tsx rename to src/pages/events/e/:eventId/EventLists/EventListComponents/RenderDateRange.tsx diff --git a/src/pages/events/e/:eventId/EventLists/components/ListSlotProgress.module.css b/src/pages/events/e/:eventId/EventLists/EventListComponents/SlotProgress.module.css similarity index 100% rename from src/pages/events/e/:eventId/EventLists/components/ListSlotProgress.module.css rename to src/pages/events/e/:eventId/EventLists/EventListComponents/SlotProgress.module.css diff --git a/src/pages/events/e/:eventId/EventLists/components/EventListSlotProgress.tsx b/src/pages/events/e/:eventId/EventLists/EventListComponents/SlotProgress.tsx similarity index 67% rename from src/pages/events/e/:eventId/EventLists/components/EventListSlotProgress.tsx rename to src/pages/events/e/:eventId/EventLists/EventListComponents/SlotProgress.tsx index aed6560..ba770b7 100644 --- a/src/pages/events/e/:eventId/EventLists/components/EventListSlotProgress.tsx +++ b/src/pages/events/e/:eventId/EventLists/EventListComponents/SlotProgress.tsx @@ -1,17 +1,41 @@ import {EventListSlotsWithEntriesCountModel} from "@/models/EventTypes.ts"; import {Badge, Group, Progress, Text, ThemeIcon, Tooltip} from "@mantine/core"; import {IconX} from "@tabler/icons-react"; -import classes from "./ListSlotProgress.module.css" +import classes from "./SlotProgress.module.css" + /** * Displays a progress status for the number of entries in a slot. * If the slot is unlimited or full, the message will reflect that. * @param slot The slot to display the progress for */ -export default function EventListSlotProgress({slot}: { slot: EventListSlotsWithEntriesCountModel }) { +export default function SlotProgress({slot, compact}: { + slot: EventListSlotsWithEntriesCountModel, + compact?: boolean +}) { + + const slotIsUnlimited = slot.maxEntries === 0 + const slotFull = !slotIsUnlimited && slot.entriesCount >= slot.maxEntries + const freeSlots = slotIsUnlimited ? 0 : slot.maxEntries - slot.entriesCount + const occupiedSlots = slot.entriesCount + + if (compact) { + return <> + {slotIsUnlimited ? ( + {slot.entriesCount} Anmeldungen (unbegrenzte Plätze)) : ( + slotFull ? ( + Voll - ({slot.maxEntries} Plätze) + ) : ( + {freeSlots} Plätze frei + ({slot.maxEntries} Plätze insgesamt) + ) + )} + + } + // if there are no max entries, the slot is unlimited - if (slot.maxEntries === 0 || slot.maxEntries === null) { + if (slotIsUnlimited) { return ( - Alle Plätze {slot.entriesCount}/{slot.maxEntries} belegt + Alle + Plätze {slot.entriesCount}/{slot.maxEntries} belegt ) } - const freeSlots = slot.maxEntries - slot.entriesCount - const occupiedSlots = slot.entriesCount // if the slot is not full and has a max entry count return <> diff --git a/src/pages/events/e/:eventId/EventLists/components/UpdateEventListSlotEntryFormModal.tsx b/src/pages/events/e/:eventId/EventLists/EventListComponents/UpdateEntryFormModal.tsx similarity index 84% rename from src/pages/events/e/:eventId/EventLists/components/UpdateEventListSlotEntryFormModal.tsx rename to src/pages/events/e/:eventId/EventLists/EventListComponents/UpdateEntryFormModal.tsx index 7685630..8176230 100644 --- a/src/pages/events/e/:eventId/EventLists/components/UpdateEventListSlotEntryFormModal.tsx +++ b/src/pages/events/e/:eventId/EventLists/EventListComponents/UpdateEntryFormModal.tsx @@ -6,8 +6,9 @@ import {showSuccessNotification} from "@/components/util.tsx"; import {Modal} from "@mantine/core"; import FormInput from "@/components/formUtil/FromInput"; import {getUserName, RenderUserName} from "@/components/users/modals/util.tsx"; +import {getListSchemas} from "@/pages/events/util.ts"; -export const UpdateEventListSlotEntryFormModal = ({opened, close, refetch, entry}: { +export const UpdateEntryFormModal = ({opened, close, refetch, entry}: { opened: boolean, close: () => void, refetch: () => void, @@ -30,10 +31,7 @@ export const UpdateEventListSlotEntryFormModal = ({opened, close, refetch, entry } }) - const questionSchemaFields = [ - ...entry.entryQuestionSchema?.fields ?? [], - ...entry.defaultEntryQuestionSchema?.fields ?? [], - ] + const {questionSchema} = getListSchemas(entry) return void, refetch: () => void, @@ -18,11 +19,6 @@ export const UpdateEventListSlotEntryStatusModal = ({opened, close, refetch, ent const {pb} = usePB() - const statusSchemaFields = [ - ...entry.entryStatusSchema?.fields ?? [], - ...entry.defaultEntryStatusSchema?.fields ?? [], - ] - const mutation = useMutation({ mutationFn: async (values: FieldEntries) => { return await pb.collection("eventListSlotEntries").update(entry.id, { @@ -36,6 +32,8 @@ export const UpdateEventListSlotEntryStatusModal = ({opened, close, refetch, ent } }) + const {statusSchema} = getListSchemas(entry) + return + {(entry.listDescription || entry.slotDescription) && ( + <> +
+ {entry.listDescription && } +
+ {entry.slotDescription && } + + )}
- {(entry.listDescription || entry.slotDescription) && ( - - - {entry.listDescription && ( - - )} - - {entry.slotDescription && ( - - )} - - - )} - void, diff --git a/src/pages/events/e/:eventId/EventLists/EventListSearch/EventListSearchResult.module.css b/src/pages/events/e/:eventId/EventLists/EventListSearch/EventListSearchResult.module.css deleted file mode 100644 index 577a018..0000000 --- a/src/pages/events/e/:eventId/EventLists/EventListSearch/EventListSearchResult.module.css +++ /dev/null @@ -1,27 +0,0 @@ -.container { - background-color: var(--mantine-color-body); - border: var(--border); - border-radius: var(--border-radius); - padding: var(--mantine-spacing-xs); -} - -.row { - display: flex; - justify-content: start; - align-items: center; - gap: var(--gap); - - font-size: var(--mantine-font-size-sm); - - & > :nth-child(2) { - width: 20%; - } - - & > :nth-child(3) { - width: 20%; - } - - & > :nth-child(4) { - flex: 1; - } -} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/EventListSearch/EventListSearchResult.tsx b/src/pages/events/e/:eventId/EventLists/EventListSearch/EventListSearchResult.tsx deleted file mode 100644 index 3058215..0000000 --- a/src/pages/events/e/:eventId/EventLists/EventListSearch/EventListSearchResult.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import {EventListSlotEntriesWithUserModel} from "@/models/EventTypes.ts"; -import classes from "./EventListSearchResult.module.css"; -import {ActionIcon, Collapse, ThemeIcon, Tooltip} from "@mantine/core"; -import {IconChevronDown, IconChevronRight, IconList, IconUser} from "@tabler/icons-react"; -import TextWithIcon from "@/components/layout/TextWithIcon"; -import { - EventListSlotEntryDetails -} from "@/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntryRow.tsx"; - -import {useDisclosure} from "@mantine/hooks"; -import {RenderDateRange} from "@/pages/events/e/:eventId/EventLists/components/RenderDateRange.tsx"; -import EditSlotEntryMenu from "@/pages/events/e/:eventId/EventLists/components/EditSlotEntryMenu.tsx"; -import {RenderUserName} from "@/components/users/modals/util.tsx"; - -export default function EventListSearchResult({entry, refetch}: { - entry: EventListSlotEntriesWithUserModel, - refetch: () => void -}) { - - const [expanded, expandedHandler] = useDisclosure(false) - - return
- -
- - - {expanded ? : } - - - - - - - }> - - - - - - - }> - {entry.listName} - - - - - -
- - - - -
-} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/EventListSearch/index.tsx b/src/pages/events/e/:eventId/EventLists/EventListSearch/index.tsx deleted file mode 100644 index 8f3bbe1..0000000 --- a/src/pages/events/e/:eventId/EventLists/EventListSearch/index.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import {EventListModel, EventModel} from "@/models/EventTypes.ts"; -import { - ActionIcon, - Autocomplete, - Center, - Collapse, - Group, - Loader, - Pagination, - Text, - Title, - Tooltip -} from "@mantine/core"; -import {IconFilter, IconFilterOff, IconUserSearch} from "@tabler/icons-react"; -import {useQuery} from "@tanstack/react-query"; -import {useDebouncedValue, useDisclosure} from "@mantine/hooks"; -import {usePB} from "@/lib/pocketbase.tsx"; -import {useState} from "react"; -import {onlyUnique} from "@/lib/util.ts"; -import EventListSearchResult from "@/pages/events/e/:eventId/EventLists/EventListSearch/EventListSearchResult.tsx"; -import ListSelect from "@/pages/events/e/:eventId/EventLists/ListSelect.tsx"; - - -export default function ListSearch({event}: { event: EventModel }) { - - const {pb} = usePB() - - const [showFilter, showFilterHandler] = useDisclosure(true) - - const [searchQueryString, setSearchQueryString] = useState('') - const [selectedLists, setSelectedLists] = useState([]) - - const [debouncedSearchQueryString] = useDebouncedValue(searchQueryString, 200) - - const [page, setPage] = useState(1) - - const searchQuery = useQuery({ - queryKey: ["eventListSearch", {event: event.id}, {debouncedSearchQueryString}, {page}, {selectedLists}], - queryFn: async () => { - - const filter: string[] = [`event='${event.id}'`] - - if (debouncedSearchQueryString) { - filter.push(`user.username ~ '${debouncedSearchQueryString.trim().replace(" ", ".")}'`) - } - - if (selectedLists.length > 0) { - filter.push(`(${selectedLists.map(l => `eventList='${l.id}'`).join(" || ")})`) - } - - return await pb.collection("eventListSlotEntriesWithUser").getList(1, 50, { - filter: filter.join(" && "), - expand: "user" - }) - } - }) - - return
- Listen Durchsuchen - - } - rightSection={searchQuery.isLoading ? : ( - - - {showFilter ? : } - - - )} - data={ - (searchQuery - .data - ?.items - .map(e => e.expand?.user.username) - .filter(u => u !== undefined) - .filter(onlyUnique) ?? []) as string[] - } - value={searchQueryString} onChange={setSearchQueryString} - /> - - - - - - - - {searchQueryString ? `Suche nach Person '${searchQueryString}'` : "Alle Anmeldungen für dieses Event"} - - - - {searchQuery.data?.totalItems ?? 0} {searchQueryString ? "Ergebnisse" : "Anmeldungen"} - - - - - { - /* - todo: more filter -
- - Filter - - Formularfelder Auswahl - Statusfelder Auswahl -
- */ - } - - { - searchQuery.data?.items.map((entry, index) => ( - searchQuery.refetch()} - /> - )) - } - -
- -
-
-} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/EventListsRouter.tsx b/src/pages/events/e/:eventId/EventLists/EventListsRouter.tsx index a414639..8815263 100644 --- a/src/pages/events/e/:eventId/EventLists/EventListsRouter.tsx +++ b/src/pages/events/e/:eventId/EventLists/EventListsRouter.tsx @@ -5,12 +5,12 @@ import EventListRouter from "./:listId/EventListRouter.tsx"; import {IconCheckupList, IconForms, IconLink, IconList, IconUserSearch} from "@tabler/icons-react"; import {ReactNode} from "react"; import {Menu} from "@mantine/core"; -import EventListSearch from "./EventListSearch/index.tsx"; -import EventListShare from "@/pages/events/e/:eventId/EventLists/EventListShare.tsx"; -import EditEventDefaultEntryStatusSchema - from "@/pages/events/e/:eventId/EventLists/EventListSettings/EditEventDefaultEntryStatusSchema.tsx"; -import EditEventDefaultEntryQuestionSchema - from "@/pages/events/e/:eventId/EventLists/EventListSettings/EditEventDefaultEntryQuestionSchema.tsx"; +import EventListSearch from "@/pages/events/e/:eventId/EventLists/Search/index.tsx"; +import ShareEventLists from "@/pages/events/e/:eventId/EventLists/ShareEventLists.tsx"; +import EditDefaultEntryStatusSchema + from "@/pages/events/e/:eventId/EventLists/GeneralListSettings/EditDefaultEntryStatusSchema.tsx"; +import EditDefaultEntryQuestionSchema + from "@/pages/events/e/:eventId/EventLists/GeneralListSettings/EditDefaultEntryQuestionSchema.tsx"; const nav = [ @@ -18,17 +18,17 @@ const nav = [ label: "Listenaktionen", children: [ { - title: "Übersicht", + title: "Listen und Slots", icon: , to: "overview" }, { - title: "Durchsuchen", + title: "Anmeldungen", icon: , to: "search" }, { - title: "Teilen", + title: "Listen Teilen", icon: , to: "share" } @@ -69,7 +69,7 @@ export const EventListsMenu = ({event, target}: { event: EventModel, target: Rea { - nav.map(({label, children,}, index, array) =>
+ nav.map(({label, children,}, index) =>
{label} {children.map(({title, icon, to}) => ( @@ -80,7 +80,6 @@ export const EventListsMenu = ({event, target}: { event: EventModel, target: Rea )} ))} - {index !== array.length - 1 && }
) } @@ -95,10 +94,10 @@ export default function EventListsRouter({event}: { event: EventModel }) { }/> }/> - }/> + }/> - }/> - }/> + }/> + }/> }/> }/> diff --git a/src/pages/events/e/:eventId/EventLists/EventListSettings/EditEventDefaultEntryQuestionSchema.tsx b/src/pages/events/e/:eventId/EventLists/GeneralListSettings/EditDefaultEntryQuestionSchema.tsx similarity index 97% rename from src/pages/events/e/:eventId/EventLists/EventListSettings/EditEventDefaultEntryQuestionSchema.tsx rename to src/pages/events/e/:eventId/EventLists/GeneralListSettings/EditDefaultEntryQuestionSchema.tsx index e1d8205..a8c10bc 100644 --- a/src/pages/events/e/:eventId/EventLists/EventListSettings/EditEventDefaultEntryQuestionSchema.tsx +++ b/src/pages/events/e/:eventId/EventLists/GeneralListSettings/EditDefaultEntryQuestionSchema.tsx @@ -14,7 +14,7 @@ import {IconAlertTriangle, IconInfoCircle} from "@tabler/icons-react"; * This component allows the user to edit the default questions for all eventLists of an event. * @param event The event to edit the default questions for. */ -export default function EditEventDefaultEntryQuestionSchema({event}: { event: EventModel }) { +export default function EditDefaultEntryQuestionSchema({event}: { event: EventModel }) { const {pb} = usePB() diff --git a/src/pages/events/e/:eventId/EventLists/EventListSettings/EditEventDefaultEntryStatusSchema.tsx b/src/pages/events/e/:eventId/EventLists/GeneralListSettings/EditDefaultEntryStatusSchema.tsx similarity index 96% rename from src/pages/events/e/:eventId/EventLists/EventListSettings/EditEventDefaultEntryStatusSchema.tsx rename to src/pages/events/e/:eventId/EventLists/GeneralListSettings/EditDefaultEntryStatusSchema.tsx index a5f125d..cab5885 100644 --- a/src/pages/events/e/:eventId/EventLists/EventListSettings/EditEventDefaultEntryStatusSchema.tsx +++ b/src/pages/events/e/:eventId/EventLists/GeneralListSettings/EditDefaultEntryStatusSchema.tsx @@ -14,7 +14,7 @@ import {IconAlertTriangle} from "@tabler/icons-react"; * This component allows the user to edit the default entry status fields for all eventLists of an event. * @param event The event to edit the default entry status fields for. */ -export default function EditEventDefaultEntryStatusSchema({event}: { event: EventModel }) { +export default function EditDefaultEntryStatusSchema({event}: { event: EventModel }) { const {pb} = usePB() diff --git a/src/pages/events/e/:eventId/EventLists/ListSelect.tsx b/src/pages/events/e/:eventId/EventLists/ListSelect.tsx index c8d261f..7b609e8 100644 --- a/src/pages/events/e/:eventId/EventLists/ListSelect.tsx +++ b/src/pages/events/e/:eventId/EventLists/ListSelect.tsx @@ -19,7 +19,6 @@ export default function ListSelect(props: GenericRecordSearchInputProps { - const filter: string[] = [] if (search) { diff --git a/src/pages/events/e/:eventId/EventLists/Search/EventEntries.module.css b/src/pages/events/e/:eventId/EventLists/Search/EventEntries.module.css new file mode 100644 index 0000000..5d472c5 --- /dev/null +++ b/src/pages/events/e/:eventId/EventLists/Search/EventEntries.module.css @@ -0,0 +1,49 @@ +.mainGrid { + display: grid; + grid-template-columns: auto auto auto 0.5fr; + gap: var(--gap); +} + +.subgrid { + display: grid; + grid-template-columns: subgrid; + grid-column: span 4; + + background-color: var(--mantine-color-body); + border: var(--border); + border-radius: var(--border-radius); + padding: var(--mantine-spacing-sm); + font-size: var(--mantine-font-size-sm); + + @media (max-width: $mantine-breakpoint-sm) { + font-size: var(--mantine-font-size-sm); + display: flex; + flex-direction: column; + gap: var(--gap); + } +} + +.child { + display: flex; + justify-content: start; + align-items: center; + text-align: start; + gap: var(--gap); + + word-wrap: break-word; /* Ensures text wraps within the cell */ + overflow-wrap: break-word; /* Ensures text wraps within the cell */ + hyphens: auto; + @media (max-width: $mantine-breakpoint-sm) { + } +} + +.alignEnd { + @media (min-width: $mantine-breakpoint-sm) { + justify-content: end; + } +} + +.detailsContainer { + grid-column: span 4; /* Spans all columns of the main grid */ +} + diff --git a/src/pages/events/e/:eventId/EventLists/Search/EventEntries.tsx b/src/pages/events/e/:eventId/EventLists/Search/EventEntries.tsx new file mode 100644 index 0000000..116f046 --- /dev/null +++ b/src/pages/events/e/:eventId/EventLists/Search/EventEntries.tsx @@ -0,0 +1,91 @@ +import {EventListSlotEntriesWithUserModel} from "@/models/EventTypes.ts"; +import classes from "./EventEntries.module.css"; +import {ActionIcon, Code, Collapse, ThemeIcon, Tooltip} from "@mantine/core"; +import {IconEye, IconEyeOff, IconList, IconUser} from "@tabler/icons-react"; +import TextWithIcon from "@/components/layout/TextWithIcon"; + + +import {useDisclosure} from "@mantine/hooks"; +import { + EntryQuestionAndStatusData +} from "@/pages/events/e/:eventId/EventLists/EventListComponents/EntryQuestionAndStatusData.tsx"; +import ShowDebug from "@/components/ShowDebug.tsx"; +import {pprintDateTime} from "@/lib/datetime.ts"; +import {RenderUserName} from "@/components/users/modals/util.tsx"; +import {RenderDateRange} from "@/pages/events/e/:eventId/EventLists/EventListComponents/RenderDateRange.tsx"; +import EditSlotEntryMenu from "@/pages/events/e/:eventId/EventLists/EventListComponents/EditSlotEntryMenu.tsx"; +import {Link} from "react-router-dom"; + +function EventEntry({entry, refetch}: { + entry: EventListSlotEntriesWithUserModel, + refetch: () => void +}) { + + const [expanded, expandedHandler] = useDisclosure(false) + + return <> +
+
+ + + + }> + + +
+ + + + + + }> + {entry.listName} + + + +
+ +
+ +
+ + + {expanded ? : } + + + +
+
+ + + + Entry Id - {entry.id} +
+ created - {pprintDateTime(entry.created)} +
+ updated - {pprintDateTime(entry.updated)} +
+
+ Entry User Id - {entry.user} +
+ Entry Event Id - {entry.event} +
+ Entry List Id - {entry.eventList} +
+ Entry Slot Id - {entry.eventListsSlot} +
+ +
+ +} + +export default function EventEntries({entries, refetch}: { + entries: EventListSlotEntriesWithUserModel[], + refetch: () => void +}) { + return
+ {entries.map(entry => )} +
+} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/Search/index.module.css b/src/pages/events/e/:eventId/EventLists/Search/index.module.css new file mode 100644 index 0000000..f1b4c32 --- /dev/null +++ b/src/pages/events/e/:eventId/EventLists/Search/index.module.css @@ -0,0 +1,4 @@ +.resultsContainer { + display: grid; + gap: var(--gap); +} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/Search/index.tsx b/src/pages/events/e/:eventId/EventLists/Search/index.tsx new file mode 100644 index 0000000..7c18aef --- /dev/null +++ b/src/pages/events/e/:eventId/EventLists/Search/index.tsx @@ -0,0 +1,226 @@ +import {EventListModel, EventModel} from "@/models/EventTypes.ts"; +import { + ActionIcon, + Autocomplete, + Button, + Center, + Collapse, + Group, + Loader, + Popover, + Text, + Title, + Tooltip +} from "@mantine/core"; +import {IconArrowDown, IconFilter, IconFilterEdit, IconFilterOff, IconSend, IconUserSearch} from "@tabler/icons-react"; +import {useInfiniteQuery} from "@tanstack/react-query"; +import {useDebouncedValue, useDisclosure} from "@mantine/hooks"; +import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx"; +import {onlyUnique} from "@/lib/util.ts"; +import ListSelect from "@/pages/events/e/:eventId/EventLists/ListSelect.tsx"; +import FormFilter from "@/components/formUtil/FormFilter"; +import {useForm} from "@mantine/form"; +import {FieldEntriesFilter} from "@/components/formUtil/FromInput/types.ts"; +import {assembleFilter} from "@/components/formUtil/FormFilter/util.ts"; +import {DateTimePicker} from "@mantine/dates"; +import EventEntries from "@/pages/events/e/:eventId/EventLists/Search/EventEntries.tsx"; + +export default function ListSearch({event}: { event: EventModel }) { + + const {pb} = usePB() + + const [showFilter, showFilterHandler] = useDisclosure(true) + + const formValues = useForm({ + initialValues: { + searchQueryString: '', + selectedLists: [] as EventListModel[], + questionFilter: {} as FieldEntriesFilter, + statusFilter: {} as FieldEntriesFilter, + createdByMin: null as Date | null, + createdByMax: null as Date | null, + } + }) + + const [debouncedFormValues] = useDebouncedValue(formValues.values, 200) + + /** + * Assemble the filter string for the search query + * @param filterValues The filter values (debounced form values) + */ + const filterString = (filterValues: typeof debouncedFormValues) => { + // this array will hold all specified filter conditions + const filter: string[] = [`event='${event.id}'`] + + // filter for user by name + filterValues.searchQueryString && filter.push(`user.username ~ '${filterValues.searchQueryString.trim().replace(" ", ".")}'`) + + // filter for lists + filterValues.selectedLists.length > 0 && filter.push(`(${filterValues.selectedLists.map(l => `eventList='${l.id}'`).join(" || ")})`) + + // filter for questions + const questionFilter = assembleFilter(filterValues.questionFilter, "entryQuestionData") + questionFilter.length !== 0 && filter.push(`(${questionFilter.join(" && ")})`) + + // filter for status + const statusFilter = assembleFilter(filterValues.statusFilter, "entryStatusData") + statusFilter.length !== 0 && filter.push(`(${statusFilter.join(" && ")})`) + + // filter for created date + filterValues.createdByMin && filter.push(`created>='${filterValues.createdByMin.toISOString()}'`) + filterValues.createdByMax && filter.push(`created<='${filterValues.createdByMax.toISOString()}'`) + + // join all filter conditions with '&&' to create the final filter string + return filter.join(" && ") + } + + const query = useInfiniteQuery({ + queryKey: ["eventListSearch", {event: event.id}, {filter: filterString(debouncedFormValues)}], + queryFn: async ({pageParam}) => { + return await pb.collection("eventListSlotEntriesWithUser").getList(pageParam, 50, { + filter: filterString(debouncedFormValues), + expand: "user" + }) + }, + getNextPageParam: (lastPage) => + lastPage.page >= lastPage.totalPages ? undefined : lastPage.page + 1, + initialPageParam: 1, + }) + + // this string informs the user about the current list selection + const listInfoString = `${formValues.values.selectedLists.length > 0 ? ` in den Listen ${formValues.values.selectedLists.map(l => `'${l.name}'`).join(", ")}` : ""}` + + const entries = query.data?.pages.flatMap(p => p.items) ?? [] + const entriesCount = query.data?.pages?.[0]?.totalItems ?? 0 + + return
+ Listen Anmeldungen + + } + rightSection={query.isPending ? : ( + + + {showFilter ? : } + + + )} + data={ + (entries + .map(e => e.expand?.user.username) + .filter(u => u !== undefined) + .filter(onlyUnique) ?? []) as string[] + } + value={formValues.values.searchQueryString} + onChange={(v) => formValues.setFieldValue("searchQueryString", v)} + /> + + +
+ formValues.setFieldValue("selectedLists", ls)} + placeholder={"Anmeldungen nur für bestimmte Listen anzeigen"} + /> + + + ([ + ...l.entryQuestionSchema?.fields ?? [], + ])).flat() + ] + }} + defaultValue={formValues.values.questionFilter} + onChange={(qf) => formValues.setFieldValue("questionFilter", qf)} + /> + + ([ + ...l.entryStatusSchema?.fields ?? [], + ])).flat() + ] + }} + defaultValue={formValues.values.statusFilter} + onChange={(sf) => formValues.setFieldValue("statusFilter", sf)} + /> + + + + + + +
+ formValues.setFieldValue("createdByMin", v)} + /> + + formValues.setFieldValue("createdByMax", v)} + /> +
+
+
+ + +
+
+
+ + + + {formValues.values.searchQueryString ? + `Suche nach Person '${formValues.values.searchQueryString}'${listInfoString}` : + `Alle Anmeldungen für dieses Event${listInfoString}`} + + + + {entriesCount} Ergebnisse + + + + + + query.refetch()}/> + + {query.hasNextPage && ( +
+ +
+ )} +
+} \ No newline at end of file diff --git a/src/pages/events/e/:eventId/EventLists/EventListShare.tsx b/src/pages/events/e/:eventId/EventLists/ShareEventLists.tsx similarity index 97% rename from src/pages/events/e/:eventId/EventLists/EventListShare.tsx rename to src/pages/events/e/:eventId/EventLists/ShareEventLists.tsx index 2d82d04..83f3f25 100644 --- a/src/pages/events/e/:eventId/EventLists/EventListShare.tsx +++ b/src/pages/events/e/:eventId/EventLists/ShareEventLists.tsx @@ -8,7 +8,7 @@ import {APP_URL} from "../../../../../../config.ts"; import {Link} from "react-router-dom"; -export default function EventListShare({event}: { event: EventModel }) { +export default function ShareEventLists({event}: { event: EventModel }) { const [selectedLists, setSelectedLists] = useState([]) diff --git a/src/pages/events/entries/UserEntryRow.tsx b/src/pages/events/entries/UserEntryRow.tsx index 45af8ec..abe6b5f 100644 --- a/src/pages/events/entries/UserEntryRow.tsx +++ b/src/pages/events/entries/UserEntryRow.tsx @@ -2,22 +2,23 @@ import {EventListSlotEntriesWithUserModel} from "@/models/EventTypes.ts"; import {ActionIcon, Collapse, Group, Text, ThemeIcon, Tooltip} from "@mantine/core"; import {IconChevronDown, IconChevronRight, IconConfetti, IconForms, IconList, IconTrash} from "@tabler/icons-react"; import TextWithIcon from "@/components/layout/TextWithIcon"; -import { - EventListSlotEntryDetails -} from "@/pages/events/e/:eventId/EventLists/:listId/EventListSlotsTable/EventListSlotEntryRow.tsx"; + import {useDisclosure} from "@mantine/hooks"; -import {RenderDateRange} from "@/pages/events/e/:eventId/EventLists/components/RenderDateRange.tsx"; import classes from "./UserEntryRow.module.css" import {humanDeltaFromNow} from "@/lib/datetime.ts"; import {useMutation} from "@tanstack/react-query"; import {showSuccessNotification} from "@/components/util.tsx"; import {useConfirmModal} from "@/components/ConfirmModal.tsx"; import {usePB} from "@/lib/pocketbase.tsx"; -import { - UpdateEventListSlotEntryFormModal -} from "@/pages/events/e/:eventId/EventLists/components/UpdateEventListSlotEntryFormModal.tsx"; + import {getUserName} from "@/components/users/modals/util.tsx"; +import {UpdateEntryFormModal} from "@/pages/events/e/:eventId/EventLists/EventListComponents/UpdateEntryFormModal.tsx"; +import {RenderDateRange} from "@/pages/events/e/:eventId/EventLists/EventListComponents/RenderDateRange.tsx"; +import { + EntryQuestionAndStatusData +} from "@/pages/events/e/:eventId/EventLists/EventListComponents/EntryQuestionAndStatusData.tsx"; + export default function UserEntryRow({entry, refetch}: { entry: EventListSlotEntriesWithUserModel, @@ -51,7 +52,7 @@ export default function UserEntryRow({entry, refetch}: { - - +
} \ No newline at end of file diff --git a/src/pages/events/s/EventListSlotView.tsx b/src/pages/events/s/EventListSlotView.tsx index 49c0213..1024f83 100644 --- a/src/pages/events/s/EventListSlotView.tsx +++ b/src/pages/events/s/EventListSlotView.tsx @@ -1,7 +1,5 @@ import {EventListModel, EventListSlotsWithEntriesCountModel} from "@/models/EventTypes.ts"; import classes from "./EventListSlotView.module.css"; -import {RenderDateRange} from "@/pages/events/e/:eventId/EventLists/components/RenderDateRange.tsx"; -import EventListSlotProgress from "@/pages/events/e/:eventId/EventLists/components/EventListSlotProgress.tsx"; import {useDisclosure} from "@mantine/hooks"; import {Alert, Collapse, ThemeIcon, Tooltip, UnstyledButton} from "@mantine/core"; import {IconChevronDown, IconChevronRight, IconInfoCircle} from "@tabler/icons-react"; @@ -12,25 +10,22 @@ import {showSuccessNotification} from "@/components/util.tsx"; import InnerHtml from "@/components/InnerHtml"; import FormInput from "@/components/formUtil/FromInput"; import {useNavigate} from "react-router-dom"; +import {getListSchemas} from "@/pages/events/util.ts"; +import {RenderDateRange} from "@/pages/events/e/:eventId/EventLists/EventListComponents/RenderDateRange.tsx"; +import SlotProgress from "@/pages/events/e/:eventId/EventLists/EventListComponents/SlotProgress.tsx"; export default function EventListSlotView({slot, list, refetch}: { list: EventListModel, slot: EventListSlotsWithEntriesCountModel, refetch: () => void }) { - const [expanded, expandedHandler] = useDisclosure(false) const slotIsFull = slot.maxEntries === 0 || slot.maxEntries === null ? false : slot.entriesCount >= slot.maxEntries const slotIsInPast = new Date(slot.endDate) < new Date() - const schema = { - fields: [ - ...slot.entryQuestionSchema?.fields ?? [], - ...slot.defaultEntryQuestionSchema?.fields ?? [], - ] - } + const {questionSchema} = getListSchemas(slot) const {pb, user} = usePB() @@ -62,9 +57,9 @@ export default function EventListSlotView({slot, list, refetch}: {
- + - +
@@ -97,7 +92,7 @@ export default function EventListSlotView({slot, list, refetch}: { : } diff --git a/src/pages/events/util.ts b/src/pages/events/util.ts index 91b3bc0..e587ea5 100644 --- a/src/pages/events/util.ts +++ b/src/pages/events/util.ts @@ -1,4 +1,8 @@ -import {EventModel} from "@/models/EventTypes.ts"; +import { + EventListSlotEntriesWithUserModel, + EventListSlotsWithEntriesCountModel, + EventModel +} from "@/models/EventTypes.ts"; import {usePB} from "@/lib/pocketbase.tsx"; import {useSettings} from "@/lib/settings.ts"; import {LdapGroupModel} from "@/models/AuthTypes.ts"; @@ -20,4 +24,28 @@ export const useEventRights = (event?: EventModel) => { canEditEvent: isEventAdmin || isStex, canEditEventList: false } +} + +/** + * Returns the schemas for the entry question and status + * + * @param slot The slot to get the schemas for + */ +export const getListSchemas = (slot: EventListSlotEntriesWithUserModel | EventListSlotsWithEntriesCountModel) => { + + + return { + questionSchema: { + fields: [ + ...slot.entryQuestionSchema?.fields ?? [], + ...(!slot.ignoreDefaultEntryQuestionScheme ? slot.defaultEntryQuestionSchema?.fields ?? [] : []), + ] + }, + statusSchema: { + fields: [ + ...slot.entryStatusSchema?.fields ?? [], + ...(!slot.ignoreDefaultEntryStatusSchema ? slot.defaultEntryStatusSchema?.fields ?? [] : []), + ] + } + } } \ No newline at end of file diff --git a/src/pages/test/DebugPage.tsx b/src/pages/test/DebugPage.tsx index 0aeeaa4..7a98906 100644 --- a/src/pages/test/DebugPage.tsx +++ b/src/pages/test/DebugPage.tsx @@ -1,10 +1,12 @@ import {useShowDebug} from "@/components/ShowDebug.tsx"; -import {ActionIcon, Alert, Group, LoadingOverlay, Text, TextInput, Title} from "@mantine/core"; +import {ActionIcon, Alert, Group, LoadingOverlay, Pagination, Text, TextInput, Title} from "@mantine/core"; import {useForm} from "@mantine/form"; import {useQuery} from "@tanstack/react-query"; import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx"; import {CodeHighlight} from "@mantine/code-highlight"; import {IconRefresh} from "@tabler/icons-react"; +import {RecordListOptions} from "pocketbase"; +import {useState} from "react"; export default function DebugPage() { @@ -13,6 +15,8 @@ export default function DebugPage() { const {pb} = usePB() + const [page, setPage] = useState(1) + const formValues = useForm({ initialValues: { collectionName: "", @@ -25,11 +29,16 @@ export default function DebugPage() { const debugQuery = useQuery({ queryKey: ["debug", formValues.values.collectionName, formValues.values.filter, formValues.values.sort, formValues.values.expand], queryFn: async () => { - return await pb.collection(formValues.values.collectionName).getList(1, 10, { - filter: formValues.values.filter, - sort: formValues.values.sort, - expand: formValues.values.expand - }) + + const options: RecordListOptions = {} + + if (formValues.values.filter) options["filter"] = formValues.values.filter + + if (formValues.values.sort) options["sort"] = formValues.values.sort + + if (formValues.values.expand) options["expand"] = formValues.values.expand + + return await pb.collection(formValues.values.collectionName).getList(page, 10, options) }, enabled: formValues.values.collectionName !== "" }) @@ -79,9 +88,7 @@ export default function DebugPage() { {debugQuery.data.totalItems} Ergebniss(e) - - {debugQuery.data.page}/{debugQuery.data.totalPages} Seiten - + debugQuery.refetch()} @@ -96,6 +103,7 @@ export default function DebugPage() { +
}
diff --git a/src/style/global.css b/src/style/global.css index c6eadab..0c1317b 100644 --- a/src/style/global.css +++ b/src/style/global.css @@ -14,7 +14,7 @@ } .tabler-icon { - stroke-width: 1.5; + stroke-width: 1.2; } .scrollable { @@ -145,6 +145,14 @@ a:hover, a:active, a:visited { align-items: center; } +.wrapWords { + overflow: hidden; + white-space: pre-wrap; + word-wrap: break-word; /* Ensures text wraps within the cell */ + overflow-wrap: break-word; /* Ensures text wraps within the cell */ + hyphens: auto; +} + .monospace { font-family: var(--mantine-font-family-monospace); } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index f01d26d..f8da1fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -461,68 +461,68 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@mantine/code-highlight@^7.8.0": - version "7.8.0" - resolved "https://registry.yarnpkg.com/@mantine/code-highlight/-/code-highlight-7.8.0.tgz#de91ef30224ae95d40c4a1b3ecf68f8c89772d50" - integrity sha512-AeuOG5TuKPOv2ifHrvwlOfNCaFpDdSwXCKq336oFBUm3Jq3B3pK+pVn3ZnX+6ccMQHwsHf/Lyv2muWHG54IEDA== +"@mantine/code-highlight@^7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@mantine/code-highlight/-/code-highlight-7.10.0.tgz#189c0c109168a1b2aeab0069a6376b6538e827db" + integrity sha512-7dep/kbqdDihCz3RYM5/q28I8M6aLgcZl0iQQN04zP12ZDgqWJ5NiyiNUodZCF4H34X/7XtQdz+cxVesJffXqw== dependencies: - clsx "2.1.0" + clsx "^2.1.1" highlight.js "^11.9.0" -"@mantine/core@^7.8.0": - version "7.8.0" - resolved "https://registry.yarnpkg.com/@mantine/core/-/core-7.8.0.tgz#b4bbd82ea2f1a25f5fb3d11ae5583cf80ecd8383" - integrity sha512-19RKuNdJ/s8pZjy2w2rvTsl4ybi/XM6vf+Kc0WY7kpLFCvdG+/UxNi1MuJF8t2Zs0QSFeb/H5yZQNe0XPbegHw== +"@mantine/core@^7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@mantine/core/-/core-7.10.0.tgz#bfaafc92cf2346e5a6cbb49289f577ce3f7c05f7" + integrity sha512-hNqhdn/+4x8+FDWzR5fu1eMgnG1Mw4fZHw4WjIYjKrSv0NeKHY263RiesZz8RwcUQ8r7LlD95/2tUOMnKVTV5Q== dependencies: "@floating-ui/react" "^0.26.9" - clsx "2.1.0" + clsx "^2.1.1" react-number-format "^5.3.1" react-remove-scroll "^2.5.7" react-textarea-autosize "8.5.3" type-fest "^4.12.0" -"@mantine/dates@^7.8.0": - version "7.8.0" - resolved "https://registry.yarnpkg.com/@mantine/dates/-/dates-7.8.0.tgz#a8785030000487158e1bd23655ea26245bbf299a" - integrity sha512-9jjiYMwP3jQOpOLKkjhp9uf2BGhtEbOnOzyAlpLOS0CJJlYtB0tO6dJ3JaogrOZ/Yfee7ZUBgouCG5EkR4/qtQ== +"@mantine/dates@^7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@mantine/dates/-/dates-7.10.0.tgz#0c2a02883d5fb4a36b40a578b26ef5a697c333e5" + integrity sha512-LBBh1U/RzxFQKGA6sSYxbCwYEMoM5lNIhwofY6g8zOTAZuRQqo5FIWItmB9I9ltT+M2o75SADeP6ZBLi4ec8ZA== dependencies: - clsx "2.1.0" + clsx "^2.1.1" -"@mantine/form@^7.8.0": - version "7.8.0" - resolved "https://registry.yarnpkg.com/@mantine/form/-/form-7.8.0.tgz#3f16b2e0124c65286892ed50181d192ae03d988b" - integrity sha512-Qn3/69zGt/p3wyMwGz2V0+FbmvqC2/PvXaeyO0a4CnwhROeE7ObyCKXDcBmgapOSBHr/7wFvMeTDMaTMfe3DXw== +"@mantine/form@^7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@mantine/form/-/form-7.10.0.tgz#3e8e3fb836948becb13b89412c74016b50bac3d3" + integrity sha512-ChAtqdQCAZrnH6iiCivumyMuMsev+tFWIgsCCgAmbP2sOyMtjbNtypKrcwBwI/PzAH9N4jSJlsmJsnRdXNeEkQ== dependencies: fast-deep-equal "^3.1.3" klona "^2.0.6" -"@mantine/hooks@^7.8.0": - version "7.8.0" - resolved "https://registry.yarnpkg.com/@mantine/hooks/-/hooks-7.8.0.tgz#fc32e07746689459c4b049dc581d1dbda5545686" - integrity sha512-+70fkgjhVJeJ+nJqnburIM3UAsfvxat1Low9HMPobLbv64FIdB4Nzu5ct3qojNQ58r5sK01tg5UoFIJYslaVrg== +"@mantine/hooks@^7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@mantine/hooks/-/hooks-7.10.0.tgz#10a259e204a8af29df6aeeb24090c1e2c6debca0" + integrity sha512-fnalwYS2WQEFS4wmhmAetDZ/VdJPLNeUXPX9t+S21o3p/dRTX1xhU2mS7yWaQUKM0hPD1TcujqXGlP2M2g/A9A== -"@mantine/modals@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@mantine/modals/-/modals-7.9.0.tgz#aedfb29de1630fc0498aebf5d3c17acfae14321b" - integrity sha512-shPoUmuAj6587DbGjc3AWpGRnxDvHftw7Ps7Xqcoy7ScxQ8+HpynBk9j7ojyXMolovK4hThW4oZdysPfpjQyaA== +"@mantine/modals@^7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@mantine/modals/-/modals-7.10.0.tgz#c08789491bfbfb1d432818e0fc4b2eac71fd480e" + integrity sha512-UVtmRpTBWDqcJjdv97IUYLduYcZBrqteyDwnspHT453iFZlvCglHUXYR+LvN5ExE+kxUe2IUXL/pEaIRTjwtKQ== -"@mantine/notifications@^7.8.1": - version "7.8.1" - resolved "https://registry.yarnpkg.com/@mantine/notifications/-/notifications-7.8.1.tgz#ce2a9e97753a4f18a5e7b1822c3be42bda43fd85" - integrity sha512-HpjZs45EmBYYmJj72+hbwLVxbO7nbZrkpjcrYc40/VIbcDjjJBXDZrAX4VLu7ZIjkbeoxxPgxp245XheC68ikA== +"@mantine/notifications@^7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@mantine/notifications/-/notifications-7.10.0.tgz#aa638b8bb6c3d6bfb34d518a49ef8a8b6ab499e4" + integrity sha512-3a0mmM9Kr3nPP+8VHsIuly507nda6ciu2aB/xSxb7gFIKHw3GqSu77pxXa+5l4Y6AQKKvP9360K4KjH6+rOBWw== dependencies: - "@mantine/store" "7.8.1" + "@mantine/store" "7.10.0" react-transition-group "4.4.5" -"@mantine/store@7.8.1": - version "7.8.1" - resolved "https://registry.yarnpkg.com/@mantine/store/-/store-7.8.1.tgz#75edadd8b42491466f681f1d23de528826b7cdff" - integrity sha512-lX1l8zPd7LazenTKqmI/E17BssRLpf/c8aKYiwV1ZgNONWc+XlWr+6MW3GLKB5uM/DS1Zg0bAYAaRUtKGP1MIQ== +"@mantine/store@7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@mantine/store/-/store-7.10.0.tgz#68368c6ca5b75cfb331220e06a3235be753df055" + integrity sha512-B6AyUX0cA97/hI9v0att7eJJnQTcUG7zBlTdWhOsptBV5UoDNrzdv3DDWIFxrA8h+nhNKGBh6Dif5HWh1+QLeA== -"@mantine/tiptap@^7.8.0": - version "7.8.0" - resolved "https://registry.yarnpkg.com/@mantine/tiptap/-/tiptap-7.8.0.tgz#390eb44659bf01d898335f6063c3d6231bb8e756" - integrity sha512-0O766+DZ8/L2VX//7GKUCfOiMyC7lJXfLmMNqQdth2JtlGoDzWDAF7hhQos0hBor+rT7ci3gPLVMv39l2U0CQw== +"@mantine/tiptap@^7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@mantine/tiptap/-/tiptap-7.10.0.tgz#77926f0a2d81c05e3f4084e65786778d5227fa56" + integrity sha512-C8wURpoh1dduWPgGgyknVc+E9/gDZVOMIyPxZXNx/r74/OoaE8tu8tgF/T21t8DtCQ4jter0PEGZFDB9hIXuag== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1396,11 +1396,6 @@ character-reference-invalid@^2.0.0: optionalDependencies: fsevents "~2.3.2" -clsx@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb" - integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg== - clsx@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"