-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add event extras system with management in dashboard (#680)
- Loading branch information
1 parent
7eaa4cf
commit b7aea43
Showing
25 changed files
with
664 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
apps/dashboard/src/app/(dashboard)/event/[id]/extras-page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { Icon } from "@iconify/react" | ||
import { ActionIcon, Box, Button, Paper, Title } from "@mantine/core" | ||
import { type FC } from "react" | ||
import { useEventDetailsContext } from "./provider" | ||
import { useCreateEventExtrasModal } from "../../../../modules/event/modals/create-event-extras-modal" | ||
import { useEditEventExtrasModal } from "../../../../modules/event/modals/edit-event-extras-modal" | ||
import { useEditEventMutation } from "../../../../modules/event/mutations/use-edit-event-mutation" | ||
|
||
export const ExtrasPage: FC = () => { | ||
const { event } = useEventDetailsContext() | ||
|
||
const openCreate = useCreateEventExtrasModal({ | ||
event, | ||
}) | ||
|
||
const openEdit = useEditEventExtrasModal({ | ||
event, | ||
}) | ||
|
||
const edit = useEditEventMutation() | ||
|
||
const deleteAlternative = (id: string) => { | ||
const newChoices = event.extras?.filter((alt) => alt.id !== id) | ||
edit.mutate({ | ||
id: event.id, | ||
event: { | ||
...event, | ||
extras: newChoices ?? [], | ||
}, | ||
}) | ||
} | ||
|
||
return ( | ||
<Box> | ||
<Title order={3}>Valg</Title> | ||
{!event.extras?.length && <p>Ingen valg er lagt til</p>} | ||
<Box> | ||
{event.extras?.map((extra) => ( | ||
<Paper key={extra.id} withBorder p={"md"} mt={"md"}> | ||
<ActionIcon variant="outline" onClick={() => openEdit(extra)} mr="md"> | ||
<Icon icon="tabler:edit" /> | ||
</ActionIcon> | ||
<ActionIcon variant="outline" onClick={() => deleteAlternative(extra.id)} color="red"> | ||
<Icon icon="tabler:trash" /> | ||
</ActionIcon> | ||
<h3>{extra.name}</h3> | ||
{extra.choices.map((choice) => ( | ||
<div key={choice.id}> | ||
<p>{choice.name}</p> | ||
</div> | ||
))} | ||
</Paper> | ||
))} | ||
</Box> | ||
|
||
<Button mt="md" onClick={openCreate}> | ||
Legg til nytt valg | ||
</Button> | ||
</Box> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
apps/dashboard/src/components/molecules/ActionSelect/ActionSelect.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { Button, type ButtonProps, Combobox, type ComboboxProps, useCombobox } from "@mantine/core" | ||
import { type FC } from "react" | ||
|
||
interface ActionSelectProps extends ComboboxProps { | ||
data: { value: string; label: string }[] | ||
onChange?(value: string): void | ||
buttonProps?: ButtonProps | ||
} | ||
|
||
export const ActionSelect: FC<ActionSelectProps> = ({ data, onChange, buttonProps, ...comboBoxProps }) => { | ||
const combobox = useCombobox({ | ||
onDropdownClose: () => combobox.resetSelectedOption(), | ||
}) | ||
|
||
const options = data.map((item) => ( | ||
<Combobox.Option value={item.value} key={item.value}> | ||
{item.label} | ||
</Combobox.Option> | ||
)) | ||
|
||
return ( | ||
<Combobox | ||
{...comboBoxProps} | ||
store={combobox} | ||
position="bottom-start" | ||
onOptionSubmit={(val) => { | ||
combobox.closeDropdown() | ||
if (onChange) { | ||
onChange(val) | ||
} | ||
}} | ||
> | ||
<Combobox.Target> | ||
<Button onClick={() => combobox.toggleDropdown()} color="green" {...buttonProps}> | ||
Bruk mal | ||
</Button> | ||
</Combobox.Target> | ||
|
||
<Combobox.Dropdown> | ||
<Combobox.Options>{options}</Combobox.Options> | ||
</Combobox.Dropdown> | ||
</Combobox> | ||
) | ||
} |
118 changes: 118 additions & 0 deletions
118
apps/dashboard/src/components/molecules/extras-form/ExtrasForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import { Box, Button, Flex, InputLabel, Text, TextInput } from "@mantine/core" | ||
import { type FC } from "react" | ||
import { useFieldArray, useForm } from "react-hook-form" | ||
import { Icon } from "@iconify/react" | ||
import { zodResolver } from "@hookform/resolvers/zod" | ||
import { z } from "zod" | ||
import { templates } from "./templates" | ||
import { ActionSelect } from "../../../components/molecules/ActionSelect/ActionSelect" | ||
|
||
type TemplateKey = keyof typeof templates | ||
|
||
const FormValuesSchema = z.object({ | ||
question: z.string(), | ||
alternatives: z.array(z.object({ value: z.string().min(1, "Dette feltet er påkrevd") })), | ||
}) | ||
|
||
export type ExtrasFormValues = z.infer<typeof FormValuesSchema> | ||
|
||
interface Props { | ||
onSubmit(data: ExtrasFormValues): void | ||
defaultAlternatives: ExtrasFormValues | ||
} | ||
|
||
const templateChoices: { value: TemplateKey; label: TemplateKey }[] = Object.keys(templates).map((key) => ({ | ||
value: key, | ||
label: key, | ||
})) | ||
|
||
export const ExtrasForm: FC<Props> = ({ onSubmit, defaultAlternatives }) => { | ||
const { | ||
register, | ||
control, | ||
handleSubmit, | ||
setValue, | ||
formState: { errors }, | ||
} = useForm<ExtrasFormValues>({ | ||
defaultValues: defaultAlternatives, | ||
mode: "onSubmit", | ||
resolver: zodResolver(FormValuesSchema), | ||
}) | ||
|
||
const { fields, append, remove } = useFieldArray({ | ||
name: "alternatives", | ||
control, | ||
}) | ||
|
||
return ( | ||
<Box> | ||
<ActionSelect | ||
buttonProps={{ | ||
w: "100%", | ||
}} | ||
data={templateChoices} | ||
onChange={(value) => { | ||
const template = templates[value] | ||
setValue("question", template.question) | ||
setValue("alternatives", template.alternatives) | ||
}} | ||
/> | ||
<Box mt="xl"> | ||
<form onSubmit={handleSubmit(onSubmit)}> | ||
<Box> | ||
<InputLabel>Spørsmål</InputLabel> | ||
<TextInput {...register("question")} title="Spørsmål" placeholder="Hvilken mat vil du ha?" /> | ||
</Box> | ||
<Box mt="md"> | ||
<InputLabel>Svaralternativer</InputLabel> | ||
{fields.map((field, index) => ( | ||
<Box key={field.id}> | ||
<Flex key={field.id} mt={index ? "sm" : undefined}> | ||
<TextInput | ||
placeholder="Pizza" | ||
{...register(`alternatives.${index}.value` as const, { | ||
required: true, | ||
})} | ||
style={{ | ||
width: "100%", | ||
}} | ||
/> | ||
<Button type="button" onClick={() => remove(index)} color="red" ml="sm" variant="light"> | ||
<Icon icon="tabler:trash" /> | ||
</Button> | ||
</Flex> | ||
{errors.alternatives?.[index]?.value && ( | ||
<Text size="xs" c="red"> | ||
{errors.alternatives[index]?.value?.message ?? "Ukjent feil"} | ||
</Text> | ||
)} | ||
</Box> | ||
))} | ||
</Box> | ||
<Button | ||
type="button" | ||
variant="light" | ||
onClick={() => | ||
append({ | ||
value: "", | ||
}) | ||
} | ||
mt="sm" | ||
display="block" | ||
style={{ | ||
margin: "5px auto", | ||
}} | ||
> | ||
<Icon icon="tabler:plus" /> | ||
</Button> | ||
|
||
<Box mt="lg"> | ||
<Button type="submit" mr="sm"> | ||
Bekreft | ||
</Button> | ||
</Box> | ||
</form> | ||
</Box> | ||
</Box> | ||
) | ||
} |
15 changes: 15 additions & 0 deletions
15
apps/dashboard/src/components/molecules/extras-form/templates.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { type ExtrasFormValues } from "./ExtrasForm" | ||
|
||
export const templates: Record<string, ExtrasFormValues> = { | ||
"Pizza / sushi": { | ||
question: "Hvilken mat vil du ha?", | ||
alternatives: [ | ||
{ | ||
value: "Pizza", | ||
}, | ||
{ | ||
value: "Sushi", | ||
}, | ||
], | ||
}, | ||
} |
52 changes: 52 additions & 0 deletions
52
apps/dashboard/src/modules/event/modals/create-event-extras-modal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { type Event } from "@dotkomonline/types" | ||
import { type ContextModalProps, modals } from "@mantine/modals" | ||
import { type FC } from "react" | ||
import { useEditEventMutation } from "../mutations/use-edit-event-mutation" | ||
import { ExtrasForm, type ExtrasFormValues } from "../../../components/molecules/extras-form/ExtrasForm" | ||
|
||
export const CreateEventExtrasModal: FC<ContextModalProps<{ event: Event }>> = ({ context, id, innerProps }) => { | ||
const editEvent = useEditEventMutation() | ||
const allExtras = innerProps.event.extras || [] | ||
|
||
const defaultAlternatives: ExtrasFormValues = { | ||
question: "", | ||
alternatives: [{ value: "" }], | ||
} | ||
|
||
const onSubmit = (data: ExtrasFormValues) => { | ||
const newExtras = [ | ||
...allExtras, | ||
{ | ||
id: `${allExtras.length - 1}`, | ||
name: data.question, | ||
choices: data.alternatives.map((alternative, i) => ({ | ||
id: `${i}`, | ||
name: alternative.value, | ||
})), | ||
}, | ||
] | ||
|
||
editEvent.mutate({ | ||
id: innerProps.event.id, | ||
event: { | ||
...innerProps.event, | ||
extras: newExtras, | ||
}, | ||
}) | ||
|
||
context.closeModal(id) | ||
} | ||
|
||
return <ExtrasForm onSubmit={onSubmit} defaultAlternatives={defaultAlternatives} /> | ||
} | ||
|
||
export const useCreateEventExtrasModal = | ||
({ event }: { event: Event }) => | ||
() => | ||
modals.openContextModal({ | ||
modal: "extras/create", | ||
title: "Legg til nytt deltakervalg", | ||
innerProps: { | ||
event, | ||
}, | ||
}) |
Oops, something went wrong.
b7aea43
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
rif – ./apps/rif
rif-two.vercel.app
rif-git-main-dotkom.vercel.app
dev.interesse.online.ntnu.no
rif-dotkom.vercel.app