From f5667b01087613ddcf8c25297b1ad433a4ef6c16 Mon Sep 17 00:00:00 2001 From: Axel Rindle Date: Sat, 10 May 2025 21:18:50 +0200 Subject: [PATCH] add veranstaltung settings --- .../migration.sql | 2 + apps/api/prisma/schema/Veranstaltung.prisma | 1 + apps/api/src/client.ts | 2 + .../anmeldung/anmeldungPublicCreate.ts | 6 +- .../veranstaltung/veranstaltung.router.ts | 7 +- .../veranstaltung/veranstaltung.schema.ts | 31 +++++++ .../veranstaltung/veranstaltungSettingsGet.ts | 24 ++++++ .../veranstaltungSettingsPatch.ts | 23 ++++++ .../FormVeranstaltungSettings.vue | 80 +++++++++++++++++++ .../Veranstaltungen/VeranstaltungDetail.vue | 14 ++++ 10 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 apps/api/prisma/migrations/20250510181946_veranstaltung_settings/migration.sql create mode 100644 apps/api/src/services/veranstaltung/veranstaltung.schema.ts create mode 100644 apps/api/src/services/veranstaltung/veranstaltungSettingsGet.ts create mode 100644 apps/api/src/services/veranstaltung/veranstaltungSettingsPatch.ts create mode 100644 apps/frontend/src/components/forms/veranstaltung/FormVeranstaltungSettings.vue diff --git a/apps/api/prisma/migrations/20250510181946_veranstaltung_settings/migration.sql b/apps/api/prisma/migrations/20250510181946_veranstaltung_settings/migration.sql new file mode 100644 index 00000000..425d3474 --- /dev/null +++ b/apps/api/prisma/migrations/20250510181946_veranstaltung_settings/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Veranstaltung" ADD COLUMN "settings" JSONB NOT NULL DEFAULT '{}'; diff --git a/apps/api/prisma/schema/Veranstaltung.prisma b/apps/api/prisma/schema/Veranstaltung.prisma index 6df0d30e..384ba204 100644 --- a/apps/api/prisma/schema/Veranstaltung.prisma +++ b/apps/api/prisma/schema/Veranstaltung.prisma @@ -19,4 +19,5 @@ model Veranstaltung { hostnameId Int? hostname Hostname? @relation(fields: [hostnameId], references: [id]) customFields CustomField[] + settings Json @default("{}") } diff --git a/apps/api/src/client.ts b/apps/api/src/client.ts index 35646dcf..2b57afdf 100644 --- a/apps/api/src/client.ts +++ b/apps/api/src/client.ts @@ -13,3 +13,5 @@ export type RouterOutput = inferRouterOutputs export * from './types/enums/index.js' export * from './types/enums/mappings/index.js' + +export type { VeranstaltungSettings } from './services/veranstaltung/veranstaltung.schema.js' diff --git a/apps/api/src/services/anmeldung/anmeldungPublicCreate.ts b/apps/api/src/services/anmeldung/anmeldungPublicCreate.ts index d3aebf77..24052cbd 100644 --- a/apps/api/src/services/anmeldung/anmeldungPublicCreate.ts +++ b/apps/api/src/services/anmeldung/anmeldungPublicCreate.ts @@ -10,6 +10,7 @@ import logActivity from '../../util/activity.js' import { sendMail } from '../../util/mail.js' import { getPersonCreateData, personSchema } from '../person/schema/person.schema.js' import { updateMeiliPerson } from '../../meilisearch/person.js' +import { veranstaltungSettingsGet } from '../veranstaltung/veranstaltung.schema.js' export const inputSchema = z.strictObject({ token: z.string().optional(), @@ -46,6 +47,7 @@ export const anmeldungPublicCreateProcedure = definePublicMutateProcedure({ }, }, maxTeilnehmende: true, + settings: true, unterveranstaltungen: { select: { _count: { @@ -141,7 +143,9 @@ export const anmeldungPublicCreateProcedure = definePublicMutateProcedure({ gliederung: person.gliederung, }) - const accessToken = randomUUID() + const accessToken = veranstaltungSettingsGet(unterveranstaltung.veranstaltung.settings, 'enablePhotoUpload') + ? randomUUID() + : null const assignmentCode = ctx.authenticated ? null : randomUUID() const anmeldung = await prisma.anmeldung.create({ data: { diff --git a/apps/api/src/services/veranstaltung/veranstaltung.router.ts b/apps/api/src/services/veranstaltung/veranstaltung.router.ts index 536a6c8e..4d7eebab 100644 --- a/apps/api/src/services/veranstaltung/veranstaltung.router.ts +++ b/apps/api/src/services/veranstaltung/veranstaltung.router.ts @@ -2,6 +2,8 @@ import { mergeRouters } from '../../trpc.js' import { veranstaltungGliederungListProcedure } from './veranstaltungGliederungList.js' +import { veranstaltungSettingsGetProcedure } from './veranstaltungSettingsGet.js' +import { veranstaltungSettingsPatchProcedure } from './veranstaltungSettingsPatch.js' import { veranstaltungVerwaltungCreateProcedure } from './veranstaltungVerwaltungCreate.js' import { veranstaltungVerwaltungGetProcedure } from './veranstaltungVerwaltungGet.js' import { @@ -17,6 +19,9 @@ export const veranstaltungRouter = mergeRouters( veranstaltungVerwaltungListProcedure, veranstaltungVerwaltungCountProcedure, veranstaltungVerwaltungPatchProcedure, - veranstaltungGliederungListProcedure + veranstaltungGliederungListProcedure, + + veranstaltungSettingsGetProcedure, + veranstaltungSettingsPatchProcedure // Add Routes here - do not delete this line ) diff --git a/apps/api/src/services/veranstaltung/veranstaltung.schema.ts b/apps/api/src/services/veranstaltung/veranstaltung.schema.ts new file mode 100644 index 00000000..c577ca83 --- /dev/null +++ b/apps/api/src/services/veranstaltung/veranstaltung.schema.ts @@ -0,0 +1,31 @@ +import { z } from 'zod' +import client from '../../prisma.js' +import type { JsonValue } from '@prisma/client/runtime/library' + +export const veranstaltungSettingsSchema = z.strictObject({ + enablePhotoUpload: z.boolean(), +}) + +export type VeranstaltungSettings = z.infer + +export const veranstaltungSettingsDefaults: VeranstaltungSettings = { + enablePhotoUpload: false, +} + +export async function populateVeranstaltungSettings(veranstaltungId: number) { + await client.veranstaltung.update({ + where: { + id: veranstaltungId, + }, + data: { + settings: veranstaltungSettingsDefaults, + }, + }) +} + +export function veranstaltungSettingsGet( + raw: JsonValue, + key: K +): VeranstaltungSettings[K] { + return (raw as VeranstaltungSettings)[key] +} diff --git a/apps/api/src/services/veranstaltung/veranstaltungSettingsGet.ts b/apps/api/src/services/veranstaltung/veranstaltungSettingsGet.ts new file mode 100644 index 00000000..8c699362 --- /dev/null +++ b/apps/api/src/services/veranstaltung/veranstaltungSettingsGet.ts @@ -0,0 +1,24 @@ +import { z } from 'zod' +import { defineProtectedQueryProcedure } from '../../types/defineProcedure.js' +import client from '../../prisma.js' +import type { VeranstaltungSettings } from './veranstaltung.schema.js' + +export const veranstaltungSettingsGetProcedure = defineProtectedQueryProcedure({ + key: 'settingsGet', + roleIds: ['ADMIN'], + inputSchema: z.strictObject({ + veranstaltungId: z.number().int(), + }), + handler: async ({ input }) => { + const { settings } = await client.veranstaltung.findFirstOrThrow({ + where: { + id: input.veranstaltungId, + }, + select: { + settings: true, + }, + }) + + return settings as VeranstaltungSettings + }, +}) diff --git a/apps/api/src/services/veranstaltung/veranstaltungSettingsPatch.ts b/apps/api/src/services/veranstaltung/veranstaltungSettingsPatch.ts new file mode 100644 index 00000000..f7e68361 --- /dev/null +++ b/apps/api/src/services/veranstaltung/veranstaltungSettingsPatch.ts @@ -0,0 +1,23 @@ +import { z } from 'zod' +import client from '../../prisma.js' +import { defineProtectedMutateProcedure } from '../../types/defineProcedure.js' +import { veranstaltungSettingsSchema } from './veranstaltung.schema.js' + +export const veranstaltungSettingsPatchProcedure = defineProtectedMutateProcedure({ + key: 'settingsPatch', + roleIds: ['ADMIN'], + inputSchema: z.strictObject({ + veranstaltungId: z.number().int(), + settings: veranstaltungSettingsSchema, + }), + handler: async ({ input }) => { + await client.veranstaltung.update({ + where: { + id: input.veranstaltungId, + }, + data: { + settings: input.settings, + }, + }) + }, +}) diff --git a/apps/frontend/src/components/forms/veranstaltung/FormVeranstaltungSettings.vue b/apps/frontend/src/components/forms/veranstaltung/FormVeranstaltungSettings.vue new file mode 100644 index 00000000..6de8d0e9 --- /dev/null +++ b/apps/frontend/src/components/forms/veranstaltung/FormVeranstaltungSettings.vue @@ -0,0 +1,80 @@ + + + diff --git a/apps/frontend/src/views/Verwaltung/Veranstaltungen/VeranstaltungDetail.vue b/apps/frontend/src/views/Verwaltung/Veranstaltungen/VeranstaltungDetail.vue index 7a7e6b75..fe410f64 100644 --- a/apps/frontend/src/views/Verwaltung/Veranstaltungen/VeranstaltungDetail.vue +++ b/apps/frontend/src/views/Verwaltung/Veranstaltungen/VeranstaltungDetail.vue @@ -2,6 +2,7 @@ import { CameraIcon, ClipboardDocumentListIcon, + CogIcon, DocumentIcon, MegaphoneIcon, SquaresPlusIcon, @@ -24,6 +25,7 @@ import UnterveranstaltungenTable from '@/components/UnterveranstaltungenTable.vu import { useRouteTitle } from '@/composables/useRouteTitle' import { formatDateWith } from '@codeanker/helpers' import { PlusIcon } from '@heroicons/vue/24/solid' +import FormVeranstaltungSettings from '@/components/forms/veranstaltung/FormVeranstaltungSettings.vue' const { setTitle } = useRouteTitle() @@ -76,6 +78,7 @@ const tabs = computed(() => { { name: 'Bedingungen', icon: ClipboardDocumentListIcon }, { name: 'Ausschreibungen', icon: MegaphoneIcon }, { name: 'Felder', icon: SquaresPlusIcon }, + { name: 'Einstellungen', icon: CogIcon }, ] return tabs }) @@ -245,5 +248,16 @@ const files: ExportedFileType[] = [ entity="veranstaltung" /> + +
+
Einstellungen
+

Hier können veranstaltungsweite Eintellungen getroffen werden.

+
+ + +