Skip to content

Commit

Permalink
Add event extras system with management in dashboard (#680)
Browse files Browse the repository at this point in the history
  • Loading branch information
henrikskog authored Nov 3, 2023
1 parent 7eaa4cf commit b7aea43
Show file tree
Hide file tree
Showing 25 changed files with 664 additions and 47 deletions.
6 changes: 3 additions & 3 deletions apps/dashboard/src/app/(dashboard)/event/[id]/edit-card.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { type FC } from "react"
import { useCommitteeAllQuery } from "src/modules/committee/queries/use-committee-all-query"
import { useEventDetailsContext } from "./provider"
import { useEditEventMutation } from "../../../../modules/event/mutations/use-edit-event-mutation"
import { useEditEventWithCommitteesMutation } from "../../../../modules/event/mutations/use-edit-event-mutation-comittees"
import { useEventEditForm } from "../edit-form"

export const EventEditCard: FC = () => {
const { event, eventCommittees } = useEventDetailsContext()
const edit = useEditEventMutation()
const edit = useEditEventWithCommitteesMutation()
const { committees } = useCommitteeAllQuery()
const FormComponent = useEventEditForm({
label: "Oppdater arrangement",
Expand All @@ -16,7 +16,7 @@ export const EventEditCard: FC = () => {
edit.mutate({
id: data.id,
event,
committeeIds,
committees: committeeIds,
})
},
defaultValues: {
Expand Down
61 changes: 61 additions & 0 deletions apps/dashboard/src/app/(dashboard)/event/[id]/extras-page.tsx
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>
)
}
9 changes: 8 additions & 1 deletion apps/dashboard/src/app/(dashboard)/event/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { Icon } from "@iconify/react"
import { Box, CloseButton, Group, Tabs, Title } from "@mantine/core"
import { useRouter } from "next/navigation"
import { type FC } from "react"
import { EventAttendancePage } from "./attendance-page"
import { EventCompaniesPage } from "./companies-page"
import { EventEditCard } from "./edit-card"
import { ExtrasPage } from "./extras-page"
import { useEventDetailsContext } from "./provider"
import { EventAttendancePage } from "./attendance-page"

const EventDetailsCompanies: FC = () => <h1>Bedrifter</h1>

Expand Down Expand Up @@ -44,6 +45,12 @@ const SIDEBAR_LINKS = [
slug: "attendance",
component: EventAttendancePage,
},
{
icon: "tabler:calendar-event",
label: "Valg",
slug: "extras",
component: ExtrasPage,
},
]

export default function EventDetailsPage() {
Expand Down
4 changes: 4 additions & 0 deletions apps/dashboard/src/app/ModalProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import { type FC, type PropsWithChildren } from "react"
import { CreateCompanyModal } from "src/modules/company/modals/create-company-modal"
import { CreateEventModal } from "src/modules/event/modals/create-event-modal"
import { CreateJobListingModal } from "src/modules/job-listing/modals/create-job-listing-modal"
import { CreateEventExtrasModal } from "../modules/event/modals/create-event-extras-modal"
import { UpdateEventExtrasModal } from "../modules/event/modals/edit-event-extras-modal"

const modals = {
"event/create": CreateEventModal,
"jobListing/create": CreateJobListingModal,
"company/create": CreateCompanyModal,
"extras/create": CreateEventExtrasModal,
"extras/update": UpdateEventExtrasModal,
} as const

export const ModalProvider: FC<PropsWithChildren> = ({ children }) => (
Expand Down
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 apps/dashboard/src/components/molecules/extras-form/ExtrasForm.tsx
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 apps/dashboard/src/components/molecules/extras-form/templates.ts
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",
},
],
},
}
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,
},
})
Loading

1 comment on commit b7aea43

@vercel
Copy link

@vercel vercel bot commented on b7aea43 Nov 3, 2023

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

Please sign in to comment.