Skip to content

Commit

Permalink
Merge pull request #150 from codeanker/feature/create-anmeldung
Browse files Browse the repository at this point in the history
Anmeldungen unabhängig von Meldeschlüssen erstellen
  • Loading branch information
danielswiatek authored Apr 29, 2024
2 parents 5678b4a + 21a1318 commit c235ec3
Show file tree
Hide file tree
Showing 14 changed files with 260 additions and 107 deletions.
8 changes: 5 additions & 3 deletions api/src/services/anmeldung/anmeldung.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { anmeldungPublicCreateProcedure } from './anmeldungPublicCreate'
import { anmeldungTeilnehmerStornoProcedure } from './anmeldungTeilnehmerStorno'
import { anmeldungVerwaltungAblehnenProcedure } from './anmeldungVerwaltungAblehnen'
import { anmeldungVerwaltungAnnehmenProcedure } from './anmeldungVerwaltungAnnehmen'
import { anmeldungVerwaltungCreateProcedure } from './anmeldungVerwaltungCreate'
import { anmeldungVerwaltungGetProcedure } from './anmeldungVerwaltungGet'
import { anmeldungVerwaltungCountProcedure, anmeldungVerwaltungListProcedure } from './anmeldungVerwaltungList'
import { anmeldungVerwaltungPatchProcedure } from './anmeldungVerwaltungPatch'
Expand All @@ -21,12 +22,13 @@ export const anmeldungRouter = mergeRouters(
anmeldungVerwaltungAnnehmenProcedure.router,
anmeldungVerwaltungStornoProcedure.router,
anmeldungVerwaltungListProcedure.router,
anmeldungGliederungdListProcedure.router,
anmeldungGliederungGetProcedure.router,
anmeldungVerwaltungCountProcedure.router,
anmeldungGliederungCountProcedure.router,
anmeldungVerwaltungCreateProcedure.router,
anmeldungVerwaltungGetProcedure.router,
anmeldungVerwaltungPatchProcedure.router,
anmeldungGliederungdListProcedure.router,
anmeldungGliederungCountProcedure.router,
anmeldungGliederungGetProcedure.router,
anmeldungGliederungPatchProcedure.router
// Add Routes here - do not delete this line
)
160 changes: 82 additions & 78 deletions api/src/services/anmeldung/anmeldungPublicCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,98 +21,102 @@ export const inputSchema = z.strictObject({
customFieldValues: defineCustomFieldValues(),
})

export const anmeldungPublicCreateProcedure = defineProcedure({
key: 'publicCreate',
method: 'mutation',
protection: { type: 'public' },
inputSchema: inputSchema,
async handler(options) {
const unterveranstaltung = await prisma.unterveranstaltung.findUniqueOrThrow({
where: {
id: options.input.data.unterveranstaltungId,
},
select: {
id: true,
meldeschluss: true,
veranstaltung: {
select: {
id: true,
name: true,
hostname: {
select: {
hostname: true,
},
export async function handle(input: z.infer<typeof inputSchema>, isPublic: boolean) {
const unterveranstaltung = await prisma.unterveranstaltung.findUniqueOrThrow({
where: {
id: input.data.unterveranstaltungId,
},
select: {
id: true,
meldeschluss: true,
veranstaltung: {
select: {
id: true,
name: true,
hostname: {
select: {
hostname: true,
},
},
},
},
})
},
})

if (dayjs().isAfter(unterveranstaltung.meldeschluss)) {
throw new TRPCError({
code: 'FORBIDDEN',
message: 'Meldeschluss erreicht',
})
}
if (isPublic && dayjs().isAfter(unterveranstaltung.meldeschluss)) {
throw new TRPCError({
code: 'FORBIDDEN',
message: 'Meldeschluss erreicht',
})
}

const personData = await getPersonCreateData(options.input.data)
const personData = await getPersonCreateData(input.data)

const person = await prisma.person.create({
data: {
...personData,
anmeldungen: {
create: {
unterveranstaltungId: unterveranstaltung.id,
mahlzeiten: options.input.data.mahlzeitenIds
? {
connect: options.input.data.mahlzeitenIds.map((id) => ({
id,
})),
}
: undefined,
uebernachtungsTage: options.input.data.uebernachtungsTage,
tshirtBestellt: options.input.data.tshirtBestellt,
comment: options.input.data.comment,
createdAt: new Date(),
customFieldValues: {
createMany: customFieldValuesCreateMany(options.input.customFieldValues),
},
const person = await prisma.person.create({
data: {
...personData,
anmeldungen: {
create: {
unterveranstaltungId: unterveranstaltung.id,
mahlzeiten: input.data.mahlzeitenIds
? {
connect: input.data.mahlzeitenIds.map((id) => ({
id,
})),
}
: undefined,
uebernachtungsTage: input.data.uebernachtungsTage,
tshirtBestellt: input.data.tshirtBestellt,
comment: input.data.comment,
createdAt: new Date(),
customFieldValues: {
createMany: customFieldValuesCreateMany(input.customFieldValues),
},
},
},
select: {
id: true,
anmeldungen: {
take: 1,
orderBy: {
createdAt: 'desc',
},
},
select: {
id: true,
anmeldungen: {
take: 1,
orderBy: {
createdAt: 'desc',
},
},
})
},
})

await Promise.all([
logActivity({
type: 'CREATE',
description: 'person created via public registration',
subjectType: 'person',
subjectId: person.id,
}),
logActivity({
type: 'CREATE',
description: 'new public registration',
subjectType: 'anmeldung',
subjectId: person.anmeldungen[0].id,
}),
])
await Promise.all([
logActivity({
type: 'CREATE',
description: 'person created via public registration',
subjectType: 'person',
subjectId: person.id,
}),
logActivity({
type: 'CREATE',
description: 'new public registration',
subjectType: 'anmeldung',
subjectId: person.anmeldungen[0].id,
}),
])

await sendMail({
to: options.input.data.email,
subject: `${unterveranstaltung?.veranstaltung?.hostname?.hostname} Anmeldung erfolgreich`,
categories: ['anmeldung', 'create'],
html: `Vielen Dank für deine Anmeldung zur Veranstaltung ${unterveranstaltung.veranstaltung.name} .`,
})
await sendMail({
to: input.data.email,
subject: `${unterveranstaltung?.veranstaltung?.hostname?.hostname} Anmeldung erfolgreich`,
categories: ['anmeldung', 'create'],
html: `Vielen Dank für deine Anmeldung zur Veranstaltung ${unterveranstaltung.veranstaltung.name} .`,
})

return person
return person
}

export const anmeldungPublicCreateProcedure = defineProcedure({
key: 'publicCreate',
method: 'mutation',
protection: { type: 'public' },
inputSchema: inputSchema,
async handler(options) {
await handle(options.input, true)
},
})
13 changes: 13 additions & 0 deletions api/src/services/anmeldung/anmeldungVerwaltungCreate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineProcedure } from '../../types/defineProcedure'

import { handle, inputSchema } from './anmeldungPublicCreate'

export const anmeldungVerwaltungCreateProcedure = defineProcedure({
key: 'verwaltungCreate',
method: 'mutation',
protection: { type: 'restrictToRoleIds', roleIds: ['ADMIN', 'GLIEDERUNG_ADMIN'] },
inputSchema: inputSchema,
async handler(options) {
await handle(options.input, false)
},
})
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export const unterveranstaltungVerwaltungGetProcedure = defineProcedure({
teilnahmegebuehr: true,
meldebeginn: true,
meldeschluss: true,
gliederung: {
select: {
id: true,
name: true,
},
},
veranstaltung: {
select: {
id: true,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'

import authenticationGuard from './authenticationGuard'

import routesPublicAnmeldung from '@/views/Anmeldung/routes'
import routesDevelopment from '@/views/Development/routes'
import routesAuth from '@/views/Login/routes'
import { routesPublic } from '@/views/Public/routes'
import routesPublicAnmeldung from '@/views/PublicAnmeldung/routes'
import routesRegistrierung from '@/views/Registrierung/routes'
import { routesUnterveranstaltung } from '@/views/Unterveranstaltung/routes'
import routesVeranstaltungen from '@/views/Veranstaltungen/routes'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ChevronLeftIcon, FaceFrownIcon } from '@heroicons/vue/24/outline'
import { useAsyncState } from '@vueuse/core'
import { computed, ref, watch } from 'vue'
import { computed, ref, watch, withDefaults, defineProps } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { apiClient } from '@/api'
Expand All @@ -15,22 +15,53 @@ import Loading from '@/components/UIComponents/Loading.vue'
import { type NahrungsmittelIntoleranz } from '@codeanker/api'
import { dayjs } from '@codeanker/helpers'
const props = withDefaults(
defineProps<{
isPublic: boolean
unterveranstaltungId?: number
/**
* Gibt an, ob der Meldeschluss ignoriert werden soll.
*/
ignoreClosingDate?: boolean
}>(),
{
isPublic: true,
ignoreClosingDate: false,
}
)
const router = useRouter()
const route = useRoute()
const unterveranstaltungId = computed(() => parseInt(route.params.ausschreibungId as string))
const unterveranstaltungId = computed(() => {
if (props.unterveranstaltungId !== undefined) {
return props.unterveranstaltungId
}
if (props.isPublic) {
return parseInt(route.params.ausschreibungId as string)
} else {
return parseInt(route.params.ausschreibungId as string)
}
})
const { state: unterveranstaltung, isLoading } = useAsyncState(async () => {
return apiClient.unterveranstaltung.publicGet.query({ id: unterveranstaltungId.value })
if (props.isPublic) {
return apiClient.unterveranstaltung.publicGet.query({ id: unterveranstaltungId.value })
}
return apiClient.unterveranstaltung.verwaltungGet.query({ id: unterveranstaltungId.value })
}, undefined)
const isClosed = computed(() => dayjs().isAfter(unterveranstaltung.value?.meldeschluss))
watch(isClosed, (value) => {
if (value) {
router.back()
}
})
if (!props.ignoreClosingDate && props.isPublic) {
watch(isClosed, (value) => {
if (value) {
router.back()
}
})
}
const { state: customFields } = useAsyncState(async () => {
if (!unterveranstaltung) {
Expand Down Expand Up @@ -60,9 +91,11 @@ const {
})
.map((entry) => entry[0] as NahrungsmittelIntoleranz)
await apiClient.anmeldung.publicCreate.mutate({
const endpoint = props.isPublic ? apiClient.anmeldung.publicCreate : apiClient.anmeldung.verwaltungCreate
await endpoint.mutate({
data: {
unterveranstaltungId: Number(route.params.ausschreibungId),
unterveranstaltungId: props.unterveranstaltungId ?? Number(route.params.ausschreibungId),
gliederungId: Number(unterveranstaltung.value?.gliederung.id),
firstname: anmeldung.stammdaten.firstname,
Expand Down Expand Up @@ -90,7 +123,12 @@ const {
value: entry[1],
})),
})
router.push('/ausschreibung/' + route.params.ausschreibungId + '/anmeldung/result')
if (props.isPublic) {
router.push('/ausschreibung/' + route.params.ausschreibungId + '/anmeldung/result')
} else {
router.back()
}
},
undefined,
{
Expand Down Expand Up @@ -123,30 +161,41 @@ const {
</div>
</Drawer>
<div class="lg:py-10 lg:px-20 flex flex-col h-full grow">
<div
class="lg:py-10 flex flex-col h-full grow"
:class="{ 'lg:px-20': props.isPublic }"
>
<div
v-if="unterveranstaltung && !isLoading"
class="grow"
>
<!-- Header -->
<PublicHeader :gliederung="unterveranstaltung.gliederung" />
<PublicHeader
v-if="props.isPublic"
:gliederung="unterveranstaltung.gliederung"
/>
<Button
v-if="props.isPublic"
class="mb-10 flex flex-row items-center"
color="secondary"
@click="router.back()"
>
<ChevronLeftIcon class="h-5 mr-2" />
<span>Zurück zur Ausschreibung</span>
</Button>
<div class="text-3xl font-medium">Anmeldung</div>
<div class="mb-5">{{ unterveranstaltung?.veranstaltung.name }}</div>
<template v-if="props.isPublic">
<div class="text-3xl font-medium">Anmeldung</div>
<div class="mb-5">{{ unterveranstaltung?.veranstaltung.name }}</div>
</template>
<!-- Form -->
<FormPersonGeneral
:is-loading="isLoadingCreate"
:error="errorCreate as Error"
submit-text="Anmelden"
is-public-anmeldung
:is-public-anmeldung="props.isPublic"
:show-tshirt="unterveranstaltung?.veranstaltung?.hostname?.hostname !== 'landes.digital'"
@submit="(value) => createAnmeldung(undefined, value)"
@show-terms="showBedingungen = true"
Expand Down Expand Up @@ -174,7 +223,7 @@ const {
class="my-5"
/>
</FormPersonGeneral>
<PublicFooter />
<PublicFooter v-if="props.isPublic" />
</div>
<div
v-else
Expand Down
Loading

0 comments on commit c235ec3

Please sign in to comment.