fix(app): added legal pages and made formUtil description HTML
Build and Push Docker image / build-and-push (push) Successful in 1m43s Details

This commit is contained in:
Valentin Kolb 2024-05-13 18:41:56 +02:00
parent 5d2eba55b0
commit 1afa7596d0
27 changed files with 324 additions and 215 deletions

View File

@ -4,8 +4,7 @@ import NotFound from "./pages/not-found/index.page.tsx";
import Layout from "@/components/layout";
import QRCodeGenerator from "./pages/util/qr/index.page.tsx";
import EventsRouter from "./pages/events/EventsRouter.tsx";
import PrivacyPolicy from "./pages/privacy-policy.page.tsx";
import TermsAndConditions from "./pages/terms-and-conditions.page.tsx";
import LegalPage from "@/pages/LegalPage.tsx";
const router = createBrowserRouter([
{
@ -17,16 +16,8 @@ const router = createBrowserRouter([
element: <HomePage/>
},
{
path: "privacy-policy",
element: <PrivacyPolicy/>
},
{
path: "imprint",
element: <PrivacyPolicy/>
},
{
path: "terms-and-conditions",
element: <TermsAndConditions/>
path: "legal/:page",
element: <LegalPage/>
},
{
path: "events/*",

View File

@ -36,7 +36,7 @@ export default function LoginModal() {
username: "",
password: "",
authMethod: "ldap" as "ldap" | "guest",
privacy: false
terms: false
}
})
@ -94,23 +94,23 @@ export default function LoginModal() {
required
label={
<Text>
Ich akzeptiere die <Anchor
Ich habe die <Anchor
component={Link}
target={"_blank"}
to={"/privacy-policy"}
to={"/legal/terms-and-conditions"}
>
Datenschutzerklärung
</Anchor>.
AGB der StuVe
</Anchor> gelesen und nehme sie zur Kenntnis
</Text>
}
{...formValues.getInputProps("privacy", {type: "checkbox"})}
{...formValues.getInputProps("terms", {type: "checkbox"})}
/>
<PocketBaseErrorAlert error={loginMutation.error}/>
<Button
loading={loginMutation.isPending}
disabled={formValues.values.username === "" || formValues.values.password === "" || !formValues.values.privacy}
disabled={formValues.values.username === "" || formValues.values.password === "" || !formValues.values.terms}
type={"submit"}
>
Einloggen

View File

@ -1,11 +1,12 @@
import {useRegister} from "@/components/auth/modals/hooks.ts";
import {isEmail, useForm} from "@mantine/form";
import {Alert, Button, Collapse, Group, Modal, PasswordInput, TextInput} from "@mantine/core";
import {Alert, Anchor, Button, Checkbox, Collapse, Group, Modal, PasswordInput, Text, TextInput} from "@mantine/core";
import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx";
import {IconAt, IconKey, IconUser, IconUserPlus, IconX} from "@tabler/icons-react";
import PasswordStrengthMeter, {getPasswordStrength} from "@/components/input/PasswordStrengthMeter.tsx";
import {useMutation} from "@tanstack/react-query";
import {showSuccessNotification} from "@/components/util.tsx";
import {Link} from "react-router-dom";
export default function RegisterModal() {
const {value, handler} = useRegister()
@ -20,12 +21,16 @@ export default function RegisterModal() {
passwordConfirm: "",
sn: "",
givenName: "",
terms: false,
privacy: false
},
validate: {
email: isEmail("Ungültige E-Mail Adresse"),
username: (val) => val.length < 3 ? "Der Anmeldename muss mindestens 3 Zeichen lang sein" : null,
password: (val) => getPasswordStrength(val) !== 100 ? "Das Passwort ist zu schwach" : null,
passwordConfirm: (val, values) => val !== values.password ? "Die Passwörter stimmen nicht überein" : null
passwordConfirm: (val, values) => val !== values.password ? "Die Passwörter stimmen nicht überein" : null,
terms: (val) => !val ? "Du musst die AGB akzeptieren" : null,
privacy: (val) => !val ? "Du musst die Datenschutzerklärung akzeptieren" : null
}
})
@ -106,6 +111,38 @@ export default function RegisterModal() {
</Collapse>
</Collapse>
<Checkbox
required
label={
<Text>
Ich habe die <Anchor
component={Link}
target={"_blank"}
to={"/legal/privacy-policy"}
>
Datenschutzerklärung
</Anchor> gelesen und akzeptiere sie.
</Text>
}
{...formValues.getInputProps("privacy", {type: "checkbox"})}
/>
<Checkbox
required
label={
<Text>
Ich habe die <Anchor
component={Link}
target={"_blank"}
to={"/legal/terms-and-conditions"}
>
AGB der StuVe
</Anchor> gelesen und nehme sie zur Kenntnis
</Text>
}
{...formValues.getInputProps("terms", {type: "checkbox"})}
/>
<Group>
<Button
leftSection={<IconX/>}

View File

@ -0,0 +1,164 @@
import {DatePickerInput, DatePickerInputProps, DateTimePicker, DateTimePickerProps} from "@mantine/dates";
import {
Input,
MultiSelect,
MultiSelectProps,
NumberInput,
NumberInputProps,
Select,
SelectProps,
Spoiler,
Stack,
Text,
Textarea,
TextareaProps,
TextInput,
TextInputProps
} from "@mantine/core";
import {IconAt} from "@tabler/icons-react";
import {
CheckboxFieldSchema,
DateFieldSchema,
DateRangeFieldSchema,
EmailFieldSchema,
FormSchemaField,
NumberFieldSchema,
SelectFieldSchema,
TextFieldSchema
} from "../formBuilder/types.ts";
import {CheckboxCard, CheckboxCardProps} from "@/components/input/CheckboxCard";
import InnerHtml from "@/components/InnerHtml";
import {ReactNode} from "react";
export const FormTextField = ({field, ...props}: { field: TextFieldSchema } & TextInputProps) => {
if (field.multiline) {
console.error("for multiline text fields use FormTextareaField instead of FormTextField")
return null
}
return <Wrapper field={field}>
<TextInput
required={field.required}
placeholder={field.placeholder ?? "Text eingeben"}
{...props}
/>
</Wrapper>
}
export const FormTextareaField = ({field, ...props}: { field: TextFieldSchema } & TextareaProps) => {
if (!field.multiline) {
console.error("for single line text fields use FormTextField instead of FormTextareaField")
return null
}
return <Wrapper field={field}>
<Textarea
resize={"vertical"}
required={field.required}
placeholder={field.placeholder ?? "Text eingeben"}
{...props}
/>
</Wrapper>
}
export const EmailField = ({field, ...props}: { field: EmailFieldSchema } & TextInputProps) => {
return <Wrapper field={field}>
<TextInput
leftSection={<IconAt/>}
required={field.required}
placeholder={field.placeholder ?? "Email eingeben"}
type={"email"}
{...props}
/>
</Wrapper>
}
export const NumberField = ({field, ...props}: { field: NumberFieldSchema } & NumberInputProps) => {
return <Wrapper field={field}>
<NumberInput
required={field.required}
placeholder={field.placeholder ?? "Zahl eingeben"}
{...props}
/>
</Wrapper>
}
export const DateField = ({field, ...props}: { field: DateFieldSchema } & DateTimePickerProps) => {
return <Wrapper field={field}>
<DateTimePicker
clearable
required={field.required}
placeholder={field.placeholder ?? "Datum auswählen"}
{...props}
/>
</Wrapper>
}
export const DateRangeField = ({field, ...props}: { field: DateRangeFieldSchema } & DatePickerInputProps<"range">) => {
return <Wrapper field={field}>
{
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
<DatePickerInput
type="range"
clearable
required={field.required}
placeholder={field.placeholder ?? "Zeitraum auswählen"}
{...props}
/>
}
</Wrapper>
}
export const SelectField = ({field, ...props}: {
field: SelectFieldSchema
} & SelectProps & MultiSelectProps) => {
const Component = field.multiple ? MultiSelect : Select
return <>
<Wrapper field={field}>
<Component
clearable
required={field.required}
placeholder={field.placeholder ?? "Auswählen"}
{...props}
data={field.options}
/>
</Wrapper>
</>
}
const Wrapper = ({field, children}: {
field: FormSchemaField,
children: ReactNode
}) => {
return <Stack gap={5}>
<Input.Label required>{field.label}</Input.Label>
{field.description && <>
<Input.Description>
<Spoiler
maxHeight={40}
showLabel={<Text span size={"xs"}>Mehr anzeigen</Text>}
hideLabel={<Text span size={"xs"}>Weniger anzeigen</Text>}
>
<InnerHtml html={field.description}/>
</Spoiler>
</Input.Description>
</>}
{children}
</Stack>
}
export const CheckboxField = ({field, ...props}: { field: CheckboxFieldSchema } & CheckboxCardProps) => {
return <CheckboxCard
label={field.label}
description={field.description}
required={field.required}
{...props}
/>
}

View File

@ -1,19 +1,18 @@
import {FormSchema} from "../formBuilder/types.ts";
import {Button, Group,} from "@mantine/core";
import {useForm} from "@mantine/form";
import {FieldEntries} from "./types.ts";
import ShowDebug from "../../ShowDebug.tsx";
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
} from "./formFieldComponents.tsx";
import {createValidationFromSchema} from "./validation.ts";
import ShowDebug from "../../ShowDebug.tsx";
NumberField, SelectField
} from "@/components/formUtil/FromInput/formFieldComponents.tsx";
/**
* This function creates default values based on a data type (e.g. "" for text, false for checkbox)

View File

@ -6,8 +6,8 @@ import {
NumberFieldSchema,
TextFieldSchema
} from "../formBuilder/types.ts"
import {FieldEntry} from "./types.ts";
import {pprintDateTime} from "@/lib/datetime.ts";
import {FieldEntry} from "@/components/formUtil/FromInput/types.ts";
const textValidator = (field: TextFieldSchema, value: string) => {

View File

@ -9,6 +9,7 @@ import {
Fieldset,
Group,
NumberInput,
Spoiler,
Stack,
Text,
TextInput,
@ -33,6 +34,8 @@ import {useDisclosure} from "@mantine/hooks";
import classes from "./editField.module.css"
import {Draggable} from "@hello-pangea/dnd";
import {humanReadableField} from "../util.ts";
import TextEditor from "@/components/input/Editor";
import InnerHtml from "@/components/InnerHtml";
type EditFieldProps = {
index: number;
@ -132,8 +135,8 @@ const SelectFieldSettings = ({field, formValues, index}: Omit<EditFieldProps, "f
placeholder={"Option"}
rightSection={
<ActionIcon
disabled={field.meta?.required}
color={"red"}
variant={"transparent"}
aria-label={"delete option"}
@ -153,6 +156,7 @@ const SelectFieldSettings = ({field, formValues, index}: Omit<EditFieldProps, "f
<Group>
<Button
disabled={field.meta?.required}
variant={"light"} size="xs"
leftSection={<IconPlus/>}
onClick={() => {
@ -297,12 +301,24 @@ export default function EditField({field, formValues, index}: EditFieldProps) {
</Badge>
</Tooltip>
<Group>
<TextInput
label={"Beschreibung"}
{...formValues.getInputProps(`fields.${index}.description`)}
{
field.meta?.required ? <>
<Fieldset legend={"Beschreibung"}>
<Spoiler hideLabel={"Beschreibung ausblenden"}
showLabel={"Beschreibung anzeigen"}>
<InnerHtml html={field.description ?? ""}/>
</Spoiler>
</Fieldset>
</> : <TextEditor
placeholder={"Beschreibung ..."}
value={formValues.values.fields[index].description ?? ""}
onChange={(value) => {
formValues.setFieldValue(`fields.${index}.description`, value)
}}
/>
}
<Group>
<TextInput
label={"Platzhalter"}
{...formValues.getInputProps(`fields.${index}.placeholder`)}

View File

@ -6,7 +6,7 @@ import EditField from "./editField.tsx";
import {IconDatabaseOff, IconEye, IconSquarePlus2} from "@tabler/icons-react";
import FormTypeIcon from "../FormTypeIcon.tsx";
import {useDisclosure} from "@mantine/hooks";
import FormInput from "../fromInput";
import FormInput from "../FromInput";
import {DragDropContext, Droppable} from "@hello-pangea/dnd";
import {useEffect} from "react";

View File

@ -1,4 +1,4 @@
import {FieldEntry} from "../fromInput/types.ts";
import {FieldEntry} from "@/components/formUtil/FromInput/types.ts";
import {ReactNode} from "react";
import {Anchor, Badge, Code, Group, List, Spoiler, ThemeIcon, Tooltip} from "@mantine/core";
import {

View File

@ -1,4 +1,4 @@
import {FieldEntries, FieldEntry} from "../fromInput/types.ts"
import {FieldEntries, FieldEntry} from "@/components/formUtil/FromInput/types.ts"
import {objectMap} from "@/lib/util.ts";
import {FormSchemaField} from "../formBuilder/types.ts";

View File

@ -1,5 +1,5 @@
import {FieldDataType} from "../formBuilder/types.ts";
import {FieldEntryValue} from "../fromInput/types.ts";
import {FieldEntryValue} from "@/components/formUtil/FromInput/types.ts";
import dayjs from "dayjs";
/**

View File

@ -1,118 +0,0 @@
import {DatePickerInput, DatePickerInputProps, DateTimePicker, DateTimePickerProps} from "@mantine/dates";
import {
MultiSelect,
MultiSelectProps,
NumberInput,
NumberInputProps,
Select,
SelectProps,
Textarea,
TextareaProps,
TextInput,
TextInputProps
} from "@mantine/core";
import {IconAt} from "@tabler/icons-react";
import {
CheckboxFieldSchema,
DateFieldSchema,
DateRangeFieldSchema,
EmailFieldSchema,
FormSchemaField,
NumberFieldSchema,
SelectFieldSchema,
TextFieldSchema
} from "../formBuilder/types.ts";
import {CheckboxCard, CheckboxCardProps} from "@/components/input/CheckboxCard";
/**
* This function unpacks a field into a set of props that can be used in the input components
* @param field
*/
const unpackField = (field: FormSchemaField) => {
return {
label: field.label || undefined,
placeholder: field.placeholder || undefined,
description: field.description || undefined,
required: field.required || false,
}
}
export const FormTextField = ({field, ...props}: { field: TextFieldSchema } & TextInputProps) => {
if (field.multiline) {
console.error("for multiline text fields use FormTextareaField instead of FormTextField")
return null
}
return <TextInput
{...unpackField(field)}
{...props}
/>
}
export const FormTextareaField = ({field, ...props}: { field: TextFieldSchema } & TextareaProps) => {
if (!field.multiline) {
console.error("for single line text fields use FormTextField instead of FormTextareaField")
return null
}
return <Textarea
resize={"vertical"}
{...unpackField(field)}
{...props}/>
}
export const EmailField = ({field, ...props}: { field: EmailFieldSchema } & TextInputProps) => {
return <TextInput
leftSection={<IconAt/>}
{...unpackField(field)}
type={"email"}
{...props}
/>
}
export const NumberField = ({field, ...props}: { field: NumberFieldSchema } & NumberInputProps) => {
return <NumberInput
{...unpackField(field)}
{...props}
/>
}
export const DateField = ({field, ...props}: { field: DateFieldSchema } & DateTimePickerProps) => {
return <DateTimePicker
clearable
{...unpackField(field)}
{...props}
/>
}
export const DateRangeField = ({field, ...props}: { field: DateRangeFieldSchema } & DatePickerInputProps<"range">) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return <DatePickerInput
type="range"
clearable
{...unpackField(field)}
{...props}
/>
}
export const SelectField = ({field, ...props}: { field: SelectFieldSchema } & SelectProps & MultiSelectProps) => {
const Component = field.multiple ? MultiSelect : Select
return <Component
clearable
{...unpackField(field)}
{...props}
data={field.options}
/>
}
export const CheckboxField = ({field, ...props}: { field: CheckboxFieldSchema } & CheckboxCardProps) => {
return <CheckboxCard
{...unpackField(field)}
{...props}
/>
/*
return <Checkbox
{...unpackField(field)}
{...props}
/>
*/
}

View File

@ -9,6 +9,11 @@
padding: var(--mantine-spacing-md);
/*noinspection CssInvalidFunction*/
background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8));
&[aria-error="true"] {
border-color: var(--mantine-color-error);
}
}
.textContainer {

View File

@ -1,12 +1,13 @@
import {Checkbox, CheckboxProps, Input, InputWrapperProps} from '@mantine/core';
import classes from './index.module.css';
import InnerHtml from "@/components/InnerHtml";
export type CheckboxCardProps = InputWrapperProps & CheckboxProps
export function CheckboxCard(props: CheckboxCardProps) {
return (
<div className={classes.container}>
<div className={classes.container} aria-error={!!props.error}>
<Checkbox
checked={props.checked}
onChange={props.onChange}
@ -19,7 +20,8 @@ export function CheckboxCard(props: CheckboxCardProps) {
<div className={classes.textContainer}>
<Input.Label required={props.required}>{props.label}</Input.Label>
{props.description && <Input.Description>{props.description}</Input.Description>}
{props.description &&
<Input.Description><InnerHtml html={props.description.toString() ?? ""}/></Input.Description>}
{props.error && <Input.Error>{props.error}</Input.Error>}
</div>
</div>

View File

@ -2,7 +2,7 @@ import {BubbleMenu, Editor, useEditor} from "@tiptap/react";
import {StarterKit} from "@tiptap/starter-kit";
import {Underline} from "@tiptap/extension-underline";
import Placeholder from '@tiptap/extension-placeholder';
import {RichTextEditor, RichTextEditorContent} from "@mantine/tiptap";
import {Link, RichTextEditor, RichTextEditorContent} from "@mantine/tiptap";
import classes from './index.module.css';
import {Box, Input, InputWrapperProps, Loader} from "@mantine/core";
import {useEffect} from "react";
@ -14,7 +14,6 @@ const Bubble = ({editor}: { editor: Editor }) => (
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold/>
<RichTextEditor.Italic/>
<RichTextEditor.Link/>
</RichTextEditor.ControlsGroup>
</BubbleMenu>
)
@ -45,6 +44,11 @@ const Toolbar = ({fullToolbar}: { fullToolbar: boolean, editor: Editor }) => (
<RichTextEditor.BulletList/>
<RichTextEditor.OrderedList/>
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Link/>
<RichTextEditor.Unlink/>
</RichTextEditor.ControlsGroup>
</>
:
<>
@ -54,6 +58,10 @@ const Toolbar = ({fullToolbar}: { fullToolbar: boolean, editor: Editor }) => (
<RichTextEditor.Underline/>
<RichTextEditor.Code/>
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Link/>
<RichTextEditor.Unlink/>
</RichTextEditor.ControlsGroup>
</>
}
@ -79,6 +87,7 @@ export default function TextEditor({
maxHeight,
hideToolbar,
noBorder,
disabled,
...props
}: {
value: string;
@ -88,14 +97,17 @@ export default function TextEditor({
maxHeight?: number | string;
hideToolbar?: boolean;
noBorder?: boolean;
disabled?: boolean;
} & Omit<InputWrapperProps, "onChange">) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Placeholder.configure({placeholder})
Link,
Placeholder.configure({placeholder: placeholder})
],
editable: !disabled,
content: value,
onUpdate: ({editor}) => {
const cleanHtml = sanitizeHtml(editor.getHTML())

View File

@ -50,14 +50,12 @@ export default function Footer() {
<div className={classes.links}>
<h5 className={classes.hideMobile}>Rechtliches</h5>
<Link to={"/terms-and-conditions"}>AGB der StuVe</Link>
<Link to={"/legal/terms-and-conditions"}>AGB der StuVe</Link>
<Link to={"/privacy-policy"}>Datenschutzerklärung</Link>
<Link to={"/imprint"}>Impressum</Link>
<Link to={"/legal/privacy-policy"}>Datenschutzerklärung</Link>
<Link to={"/legal/imprint"}>Impressum</Link>
</div>
</div>
<Divider/>

View File

@ -9,6 +9,7 @@ type Setting = {
}
type Settings = {
imprint: Setting
privacyPolicy: Setting
agb: Setting
stexGroupId: Setting

View File

@ -1,6 +1,6 @@
import {GuestUserModel, LdapUserModel} from "./AuthTypes.ts";
import {RecordModel} from "pocketbase";
import {FieldEntries} from "@/components/formUtil/fromInput/types.ts";
import {FieldEntries} from "@/components/formUtil/FromInput/types.ts";
import {FormSchema} from "@/components/formUtil/formBuilder/types.ts";
export type EventModel = {

View File

@ -16,11 +16,20 @@ export type SettingsModel = {
public?: boolean
} & RecordModel
export type LegalSettingsModal = {
name: string,
updated: string,
legalText: string
} & RecordModel
export interface TypedPocketBase extends PocketBase {
collection(idOrName: string): RecordService<RecordModel>
collection(idOrName: 'settings'): RecordService<SettingsModel>
collection(idOrName: 'legalSettings'): RecordService<LegalSettingsModal>
collection(idOrName: 'users'): RecordService<UserModal>
collection(idOrName: 'guest_users'): RecordService<GuestUserModel>

33
src/pages/LegalPage.tsx Normal file
View File

@ -0,0 +1,33 @@
import {LoadingOverlay} from "@mantine/core";
import InnerHtml from "@/components/InnerHtml";
import {pprintDate} from "@/lib/datetime.ts";
import {useParams} from "react-router-dom";
import {useQuery} from "@tanstack/react-query";
import {usePB} from "@/lib/pocketbase.tsx";
import NotFound from "@/pages/not-found/index.page.tsx";
export default function LegalPage() {
const {page} = useParams() as { page: string }
const {pb} = usePB()
const query = useQuery({
queryKey: ["legalSettings", page],
queryFn: async () => await pb.collection("legalSettings").getFirstListItem(`name='${page}'`),
enabled: !!page
})
if (query.isPending) return <LoadingOverlay visible={true}/>
if (query.isError || !query.data) return <NotFound/>
return <>
<div className={"section"}>
Zuletzt Bearbeitet: {pprintDate(query.data.updated)}
</div>
<div className={"section"}>
<InnerHtml html={query.data.legalText}/>
</div>
</>
}

View File

@ -34,7 +34,7 @@ export default function EventAGB({event}: { event: EventModel }) {
<Button
mt={"sm"}
variant={"light"}
component={Link} to={"/terms-and-conditions"} target={"_blank"}
component={Link} to={"/legal/terms-and-conditions"} target={"_blank"}
>
AGB der StuVe
</Button>

View File

@ -1,10 +1,10 @@
import {EventListSlotEntriesWithUserModel} from "@/models/EventTypes.ts";
import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx";
import {useMutation} from "@tanstack/react-query";
import {FieldEntries} from "@/components/formUtil/fromInput/types.ts";
import {FieldEntries} from "@/components/formUtil/FromInput/types.ts";
import {showSuccessNotification} from "@/components/util.tsx";
import {Modal} from "@mantine/core";
import FormInput from "@/components/formUtil/fromInput";
import FormInput from "@/components/formUtil/FromInput";
export const UpdateEventListSlotEntryFormModal = ({opened, close, refetch, entry}: {
opened: boolean,

View File

@ -1,12 +1,12 @@
import {EventListSlotEntriesWithUserModel} from "@/models/EventTypes.ts";
import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx";
import {useMutation} from "@tanstack/react-query";
import {FieldEntries} from "@/components/formUtil/fromInput/types.ts";
import {FieldEntries} from "@/components/formUtil/FromInput/types.ts";
import {showSuccessNotification} from "@/components/util.tsx";
import {Alert, Modal} from "@mantine/core";
import FormInput from "@/components/formUtil/fromInput";
import InnerHtml from "@/components/InnerHtml";
import {RenderDateRange} from "./RenderDateRange.tsx";
import FormInput from "@/components/formUtil/FromInput";
export const UpdateEventListSlotEntryStatusModal = ({opened, close, refetch, entry}: {
opened: boolean,

View File

@ -7,10 +7,10 @@ import {Alert, Collapse, ThemeIcon, Tooltip, UnstyledButton} from "@mantine/core
import {IconChevronDown, IconChevronRight, IconInfoCircle} from "@tabler/icons-react";
import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx";
import {useMutation} from "@tanstack/react-query";
import {FieldEntries} from "@/components/formUtil/fromInput/types.ts";
import {FieldEntries} from "@/components/formUtil/FromInput/types.ts";
import {showSuccessNotification} from "@/components/util.tsx";
import InnerHtml from "@/components/InnerHtml";
import FormInput from "@/components/formUtil/fromInput";
import FormInput from "@/components/formUtil/FromInput";
import {useUser} from "@/lib/user.ts";
export default function EventListSlotView({slot, refetch}: {

View File

@ -1,20 +0,0 @@
import {useSettings} from "@/lib/settings.ts";
import {LoadingOverlay} from "@mantine/core";
import InnerHtml from "@/components/InnerHtml";
import {pprintDate} from "@/lib/datetime.ts";
export default function PrivacyPolicy() {
const settings = useSettings()
if (!settings) return <LoadingOverlay visible={true}/>
return <>
<div className={"section"}>
Zuletzt Bearbeitet: {pprintDate(settings.privacyPolicy.updated)}
</div>
<div className={"section"}>
<InnerHtml html={settings.privacyPolicy.value}/>
</div>
</>
}

View File

@ -1,20 +0,0 @@
import {useSettings} from "@/lib/settings.ts";
import {LoadingOverlay} from "@mantine/core";
import InnerHtml from "@/components/InnerHtml";
import {pprintDate} from "@/lib/datetime.ts";
export default function TermsAndConditions() {
const settings = useSettings()
if (!settings) return <LoadingOverlay visible={true}/>
return <>
<div className={"section"}>
Zuletzt Bearbeitet: {pprintDate(settings.agb.updated)}
</div>
<div className={"section"}>
<InnerHtml html={settings.agb.value}/>
</div>
</>
}