fix(announcements): improved sending flow
Build and Push Docker image / build-and-push (push) Successful in 6m10s Details

This commit is contained in:
Valentin Kolb 2024-06-17 20:22:49 +02:00
parent 7eb38a1ef7
commit 73afba37ac
5 changed files with 47 additions and 30 deletions

View File

@ -1,7 +1,6 @@
import {Button, Group, Modal, ModalProps, Text} from "@mantine/core"; import {Button, Group, Modal, ModalProps, Text} from "@mantine/core";
import {useDisclosure} from "@mantine/hooks"; import {useDisclosure} from "@mantine/hooks";
import {IconAlertTriangle} from "@tabler/icons-react"; import {IconAlertTriangle} from "@tabler/icons-react";
import {useState} from "react";
/** /**
@ -13,19 +12,17 @@ import {useState} from "react";
* @param onCancel - the function to call when the user cancels (optional) * @param onCancel - the function to call when the user cancels (optional)
* @param props - additional props for the modal * @param props - additional props for the modal
*/ */
export const useConfirmModal = <T, >( export const useConfirmModal = (
{title, description, onConfirm, onCancel, ...props}: { {title, description, onConfirm, onCancel, ...props}: {
title?: string, title?: string,
description: string, description: string,
onConfirm: (t?: T) => void, onConfirm: () => void,
onCancel?: () => void onCancel?: () => void
} & Omit<ModalProps, "opened" | "onClose"> } & Omit<ModalProps, "opened" | "onClose">
) => { ) => {
const [showConfirmModal, handler] = useDisclosure(false) const [showConfirmModal, handler] = useDisclosure(false)
const [data, setData] = useState<T | undefined>(undefined)
const ConfirmModal = () => { const ConfirmModal = () => {
return <> return <>
<Modal <Modal
@ -48,15 +45,13 @@ export const useConfirmModal = <T, >(
onClick={() => { onClick={() => {
handler.close() handler.close()
onCancel?.() onCancel?.()
setData(undefined)
}}> }}>
Abbrechen Abbrechen
</Button> </Button>
<Button <Button
onClick={() => { onClick={() => {
handler.close() handler.close()
onConfirm(data) onConfirm()
setData(undefined)
}} }}
size={"xs"} size={"xs"}
leftSection={<IconAlertTriangle/>} leftSection={<IconAlertTriangle/>}
@ -72,9 +67,8 @@ export const useConfirmModal = <T, >(
return { return {
showConfirmModal, showConfirmModal,
toggleConfirmModal: (t?: T) => { toggleConfirmModal: () => {
handler.toggle() handler.toggle()
setData(t)
}, },
ConfirmModal ConfirmModal
} }

View File

@ -4,6 +4,7 @@ import {FieldEntriesFilter, FieldEntryFilter} from "@/components/formUtil/FromIn
import {Button, Group, Popover, Switch, Text} from "@mantine/core"; import {Button, Group, Popover, Switch, Text} from "@mantine/core";
import {IconFilter, IconFilterOff, IconFilterPlus} from "@tabler/icons-react"; import {IconFilter, IconFilterOff, IconFilterPlus} from "@tabler/icons-react";
import {FilterField} from "@/components/formUtil/FormFilter/FilterField.tsx"; import {FilterField} from "@/components/formUtil/FormFilter/FilterField.tsx";
import {useEffect} from "react";
/** /**
* todo * todo
@ -60,7 +61,7 @@ const createDefaultFilter = (field: FormSchemaField): FieldEntryFilter => {
* @see assembleFilter * @see assembleFilter
* @see FormBuilder * @see FormBuilder
* *
* @param schema The schema to render * @param schema The schema to render. Make sure that the schema is memoized!
* @param label The label for the filter button * @param label The label for the filter button
* @param defaultValue The default value for the filter * @param defaultValue The default value for the filter
* @param onChange The function to call when the filter changes * @param onChange The function to call when the filter changes
@ -72,7 +73,6 @@ export default function FormFilter({schema, label, defaultValue, onChange}: {
onChange?: (filter: FieldEntriesFilter) => void, onChange?: (filter: FieldEntriesFilter) => void,
}) { }) {
const formValues = useForm({ const formValues = useForm({
initialValues: defaultValue ?? {} as FieldEntriesFilter, initialValues: defaultValue ?? {} as FieldEntriesFilter,
onValuesChange: (values) => { onValuesChange: (values) => {
@ -88,6 +88,21 @@ export default function FormFilter({schema, label, defaultValue, onChange}: {
} }
} }
useEffect(() => {
// this removes all filter values not present in schema, this is necessary because the schema might change
// and the filter values might not be valid anymore
const values = formValues.values
const newValues: FieldEntriesFilter = {}
Object.keys(values).forEach(key => {
if (schema.fields.find(f => f.id === key)) {
newValues[key] = values[key]
}
})
formValues.setValues(newValues)
console.log("Schema changed", values, newValues)
// eslint-disable-next-line
}, [schema])
return <> return <>
<Popover width={300} position="bottom" withArrow shadow="md"> <Popover width={300} position="bottom" withArrow shadow="md">
<Popover.Target> <Popover.Target>

View File

@ -58,7 +58,7 @@ export default function Announcements() {
</Center> </Center>
) : announcements.length === 0 ? <div className={classes.text}> ) : announcements.length === 0 ? <div className={classes.text}>
<Text ta={"center"} size={"xs"} c={"dimmed"}> <Text ta={"center"} size={"xs"} c={"dimmed"}>
"Noch keine Ankündigungen" Noch keine Ankündigungen
</Text> </Text>
</div> : null </div> : null
} }

View File

@ -62,6 +62,7 @@ export default function MessageEntriesModal({opened, event, onClose, query}: {
}, },
onSuccess: () => { onSuccess: () => {
showSuccessNotification("Nachrichten wurden versendet") showSuccessNotification("Nachrichten wurden versendet")
formValues.reset()
onClose() onClose()
} }
}) })

View File

@ -35,6 +35,7 @@ import EventEntries from "@/pages/events/e/:eventId/EventLists/Search/EventEntri
import {useEventRights} from "@/pages/events/util.ts"; import {useEventRights} from "@/pages/events/util.ts";
import DownloadDataModal from "@/pages/events/e/:eventId/EventLists/Search/DownloadDataModal.tsx"; import DownloadDataModal from "@/pages/events/e/:eventId/EventLists/Search/DownloadDataModal.tsx";
import MessageEntriesModal from "@/pages/events/e/:eventId/EventLists/Search/MessageEntriesModal.tsx"; import MessageEntriesModal from "@/pages/events/e/:eventId/EventLists/Search/MessageEntriesModal.tsx";
import {useMemo} from "react";
export default function ListSearch({event}: { event: EventModel }) { export default function ListSearch({event}: { event: EventModel }) {
@ -118,12 +119,32 @@ export default function ListSearch({event}: { event: EventModel }) {
initialPageParam: 1, initialPageParam: 1,
}) })
// this string informs the user about the current list selection // 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 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 entries = query.data?.pages.flatMap(p => p.items) ?? []
const entriesCount = query.data?.pages?.[0]?.totalItems ?? 0 const entriesCount = query.data?.pages?.[0]?.totalItems ?? 0
const questionSchema = useMemo(() => ({
fields: [
...event.defaultEntryQuestionSchema?.fields ?? [],
...formValues.values.selectedLists.map(l => ([
...l.entryQuestionSchema?.fields ?? [],
])).flat()
]
// eslint-disable-next-line react-hooks/exhaustive-deps
}), [event.id, JSON.stringify(formValues.values.selectedLists)])
const statusSchema = useMemo(() => ({
fields: [
...event.defaultEntryStatusSchema?.fields ?? [],
...formValues.values.selectedLists.map(l => ([
...l.entryStatusSchema?.fields ?? [],
])).flat()
]
// eslint-disable-next-line react-hooks/exhaustive-deps
}), [event.id, JSON.stringify(formValues.values.selectedLists)])
return <div className={"stack"}> return <div className={"stack"}>
<Title order={4} c={"blue"}>Listen Anmeldungen</Title> <Title order={4} c={"blue"}>Listen Anmeldungen</Title>
@ -163,28 +184,14 @@ export default function ListSearch({event}: { event: EventModel }) {
<Group> <Group>
<FormFilter <FormFilter
label={"Fragen"} label={"Fragen"}
schema={{ schema={questionSchema}
fields: [
...event.defaultEntryQuestionSchema?.fields ?? [],
...formValues.values.selectedLists.map(l => ([
...l.entryQuestionSchema?.fields ?? [],
])).flat()
]
}}
defaultValue={formValues.values.questionFilter} defaultValue={formValues.values.questionFilter}
onChange={(qf) => formValues.setFieldValue("questionFilter", qf)} onChange={(qf) => formValues.setFieldValue("questionFilter", qf)}
/> />
<FormFilter <FormFilter
label={"Status"} label={"Status"}
schema={{ schema={statusSchema}
fields: [
...event.defaultEntryStatusSchema?.fields ?? [],
...formValues.values.selectedLists.map(l => ([
...l.entryStatusSchema?.fields ?? [],
])).flat()
]
}}
defaultValue={formValues.values.statusFilter} defaultValue={formValues.values.statusFilter}
onChange={(sf) => formValues.setFieldValue("statusFilter", sf)} onChange={(sf) => formValues.setFieldValue("statusFilter", sf)}
/> />