From e1fe63c3f5b5fcade62c00688e6c382b3f1ba9c3 Mon Sep 17 00:00:00 2001 From: Axel Rindle Date: Sat, 28 Feb 2026 21:29:44 +0100 Subject: [PATCH 01/11] fix: select gliederung domain --- apps/api/src/services/gliederung/gliederungVerwaltungGet.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/api/src/services/gliederung/gliederungVerwaltungGet.ts b/apps/api/src/services/gliederung/gliederungVerwaltungGet.ts index c5c42340..376af311 100644 --- a/apps/api/src/services/gliederung/gliederungVerwaltungGet.ts +++ b/apps/api/src/services/gliederung/gliederungVerwaltungGet.ts @@ -19,6 +19,7 @@ export const gliederungVerwaltungGetProcedure = defineProtectedQueryProcedure({ id: true, name: true, edv: true, + domain: true, }, }) }, From a2fdc7216f5b326aa89ee9f65e6ec84b5039e317 Mon Sep 17 00:00:00 2001 From: Axel Rindle Date: Sun, 1 Mar 2026 09:48:51 +0100 Subject: [PATCH 02/11] refactor: show privacy in it's own drawer --- .../forms/person/FormPersonGeneral.vue | 3 ++- apps/frontend/src/helpers/sanitizeHtml.ts | 6 ++++- .../views/Anmeldung/AnmeldungFormGeneral.vue | 26 ++++++++++++++----- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/apps/frontend/src/components/forms/person/FormPersonGeneral.vue b/apps/frontend/src/components/forms/person/FormPersonGeneral.vue index 39b4204a..799d19ef 100644 --- a/apps/frontend/src/components/forms/person/FormPersonGeneral.vue +++ b/apps/frontend/src/components/forms/person/FormPersonGeneral.vue @@ -45,6 +45,7 @@ const props = defineProps<{ const emit = defineEmits<{ submit: [data: FormPersonGeneralSubmit] showTerms: [] + showPrivacy: [] }>() async function queryGliederungen(searchTerm: string) { @@ -237,7 +238,7 @@ const submit = () => { Ich habe die gesonderten Datenschutzerklärung diff --git a/apps/frontend/src/helpers/sanitizeHtml.ts b/apps/frontend/src/helpers/sanitizeHtml.ts index 1d978ad5..41c7157b 100644 --- a/apps/frontend/src/helpers/sanitizeHtml.ts +++ b/apps/frontend/src/helpers/sanitizeHtml.ts @@ -1,6 +1,10 @@ import doSanitizeHtml from 'sanitize-html' -export function sanitizeHtml(html: string): string { +export function sanitizeHtml(html?: string | null): string { + if (!html) { + return '' + } + return doSanitizeHtml(html, { allowedTags: [ 'h2', diff --git a/apps/frontend/src/views/Anmeldung/AnmeldungFormGeneral.vue b/apps/frontend/src/views/Anmeldung/AnmeldungFormGeneral.vue index 1cc44901..20fda7b4 100644 --- a/apps/frontend/src/views/Anmeldung/AnmeldungFormGeneral.vue +++ b/apps/frontend/src/views/Anmeldung/AnmeldungFormGeneral.vue @@ -10,6 +10,7 @@ import FormPersonGeneral, { type FormPersonGeneralSubmit } from '@/components/fo import Drawer from '@/components/LayoutComponents/Drawer.vue' import { injectUnterveranstaltung } from '@/layouts/AnmeldungLayout.vue' import { type NahrungsmittelIntoleranz } from '@codeanker/api' +import { sanitizeHtml } from '@/helpers/sanitizeHtml' const props = withDefaults( defineProps<{ @@ -42,6 +43,7 @@ watch(unterveranstaltung, () => loadCustomFields()) const customFieldValues = ref>({}) const showBedingungen = ref(false) +const showDatenschutz = ref(false) const { execute: createAnmeldung, @@ -109,22 +111,33 @@ const { > + + + @@ -145,6 +158,7 @@ const { :is-public-anmeldung="props.isPublic" @submit="(value) => createAnmeldung(undefined, value)" @show-terms="showBedingungen = true" + @show-privacy="showDatenschutz = true" >
Date: Sun, 1 Mar 2026 22:27:03 +0100 Subject: [PATCH 03/11] fix: redirect based on request host --- apps/api/src/routes/oidc/index.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/api/src/routes/oidc/index.ts b/apps/api/src/routes/oidc/index.ts index fe904366..76355987 100644 --- a/apps/api/src/routes/oidc/index.ts +++ b/apps/api/src/routes/oidc/index.ts @@ -136,7 +136,16 @@ oidcRouter.get('/dlrg/login', async (c) => { const as = await oauth.processDiscoveryResponse(issuer, discoveryRequestResponse) const authorizationUrl = new URL(as.authorization_endpoint!) - const redirectUri = new URL('/api/connect/dlrg/callback', config.clientUrl) + const host = await prisma.hostname.findFirst({ + where: { + hostname: c.req.header('Host'), + } + }) + if (host === null) { + return c.text('Invalid domain', 400) + } + + const redirectUri = new URL('/api/connect/dlrg/callback', host.hostname) const registerAs = c.req.query('as')?.trim() if (registerAs !== undefined && registerAs?.length > 0) { redirectUri.searchParams.set('as', registerAs) From 8a4b39e87c9ff42ad273f67bcaa6560eb32a83b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 20:49:43 +0000 Subject: [PATCH 04/11] chore(deps): bump hono from 4.12.0 to 4.12.4 Bumps [hono](https://github.com/honojs/hono) from 4.12.0 to 4.12.4. - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.12.0...v4.12.4) --- updated-dependencies: - dependency-name: hono dependency-version: 4.12.4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- apps/api/package.json | 2 +- pnpm-lock.yaml | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/apps/api/package.json b/apps/api/package.json index 03168c28..f3d74cd6 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -45,7 +45,7 @@ "dot-prop": "^9.0.0", "fast-csv": "^5.0.1", "handlebars": "^4.7.8", - "hono": "^4.12.0", + "hono": "^4.12.4", "http-errors": "^2.0.1", "jsonwebtoken": "^9.0.2", "meilisearch": "^0.37.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94e82143..2726fdbc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,10 +118,10 @@ importers: version: 10.1.0 '@hono/node-server': specifier: ^1.19.6 - version: 1.19.7(hono@4.12.0) + version: 1.19.7(hono@4.12.4) '@hono/trpc-server': specifier: ^0.4.0 - version: 0.4.1(@trpc/server@11.0.0-rc.682(typescript@5.7.2))(hono@4.12.0) + version: 0.4.1(@trpc/server@11.0.0-rc.682(typescript@5.7.2))(hono@4.12.4) '@prisma/client': specifier: ^5.19.1 version: 5.22.0(prisma@5.22.0) @@ -153,8 +153,8 @@ importers: specifier: ^4.7.8 version: 4.7.8 hono: - specifier: ^4.12.0 - version: 4.12.0 + specifier: ^4.12.4 + version: 4.12.4 http-errors: specifier: ^2.0.1 version: 2.0.1 @@ -3625,8 +3625,8 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true - hono@4.12.0: - resolution: {integrity: sha512-NekXntS5M94pUfiVZ8oXXK/kkri+5WpX2/Ik+LVsl+uvw+soj4roXIsPqO+XsWrAw20mOzaXOZf3Q7PfB9A/IA==} + hono@4.12.4: + resolution: {integrity: sha512-ooiZW1Xy8rQ4oELQ++otI2T9DsKpV0M6c6cO6JGx4RTfav9poFFLlet9UMXHZnoM1yG0HWGlQLswBGX3RZmHtg==} engines: {node: '>=16.9.0'} hookable@5.5.3: @@ -3917,6 +3917,9 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + logform@2.7.0: resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} engines: {node: '>= 12.0.0'} @@ -6047,14 +6050,14 @@ snapshots: dependencies: vue: 3.5.13(typescript@5.7.2) - '@hono/node-server@1.19.7(hono@4.12.0)': + '@hono/node-server@1.19.7(hono@4.12.4)': dependencies: - hono: 4.12.0 + hono: 4.12.4 - '@hono/trpc-server@0.4.1(@trpc/server@11.0.0-rc.682(typescript@5.7.2))(hono@4.12.0)': + '@hono/trpc-server@0.4.1(@trpc/server@11.0.0-rc.682(typescript@5.7.2))(hono@4.12.4)': dependencies: '@trpc/server': 11.0.0-rc.682(typescript@5.7.2) - hono: 4.12.0 + hono: 4.12.4 '@humanfs/core@0.19.1': {} @@ -8292,7 +8295,7 @@ snapshots: he@1.2.0: {} - hono@4.12.0: {} + hono@4.12.4: {} hookable@5.5.3: {} @@ -8564,6 +8567,8 @@ snapshots: lodash@4.17.21: {} + lodash@4.17.23: {} + logform@2.7.0: dependencies: '@colors/colors': 1.6.0 @@ -10127,7 +10132,7 @@ snapshots: eslint-visitor-keys: 3.4.3 espree: 9.6.1 esquery: 1.7.0 - lodash: 4.17.21 + lodash: 4.17.23 semver: 7.7.4 transitivePeerDependencies: - supports-color From 24821fde6e1acf9f53842ac6cc76df769923bc99 Mon Sep 17 00:00:00 2001 From: Axel Rindle Date: Wed, 4 Mar 2026 21:55:06 +0100 Subject: [PATCH 05/11] feat: custom field ordering --- .../migration.sql | 2 + apps/api/prisma/schema/CustomField.prisma | 1 + .../customFields/customFields.router.ts | 6 +- .../services/customFields/customFieldsList.ts | 73 +++++--- .../customFields/schema/customField.schema.ts | 26 ++- .../customFieldsUnterveranstaltungOrder.ts | 54 ++++++ .../schema/customFieldsVeranstaltungOrder.ts | 44 +++++ .../CustomFields/CustomFieldsTable.vue | 13 ++ .../CustomFieldUnterveranstaltungOrder.vue | 168 ++++++++++++++++++ .../UnterveranstaltungDetail.vue | 11 ++ .../src/views/Unterveranstaltung/routes.ts | 13 ++ .../CustomFieldVeranstaltungOrder.vue | 126 +++++++++++++ .../Veranstaltungen/VeranstaltungDetail.vue | 26 ++- .../Verwaltung/Veranstaltungen/routes.ts | 13 ++ 14 files changed, 536 insertions(+), 40 deletions(-) create mode 100644 apps/api/prisma/migrations/20260304182124_custom_field_order/migration.sql create mode 100644 apps/api/src/services/customFields/schema/customFieldsUnterveranstaltungOrder.ts create mode 100644 apps/api/src/services/customFields/schema/customFieldsVeranstaltungOrder.ts create mode 100644 apps/frontend/src/views/Unterveranstaltung/CustomFields/CustomFieldUnterveranstaltungOrder.vue create mode 100644 apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue diff --git a/apps/api/prisma/migrations/20260304182124_custom_field_order/migration.sql b/apps/api/prisma/migrations/20260304182124_custom_field_order/migration.sql new file mode 100644 index 00000000..79c1d2b8 --- /dev/null +++ b/apps/api/prisma/migrations/20260304182124_custom_field_order/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "CustomField" ADD COLUMN "order" INTEGER; diff --git a/apps/api/prisma/schema/CustomField.prisma b/apps/api/prisma/schema/CustomField.prisma index 0a1415df..ac05f0a7 100644 --- a/apps/api/prisma/schema/CustomField.prisma +++ b/apps/api/prisma/schema/CustomField.prisma @@ -19,6 +19,7 @@ model CustomField { id String @id @default(uuid(7)) name String description String? + order Int? type CustomFieldType required Boolean @default(false) options String[] diff --git a/apps/api/src/services/customFields/customFields.router.ts b/apps/api/src/services/customFields/customFields.router.ts index 3e38d8cc..5f861fa8 100644 --- a/apps/api/src/services/customFields/customFields.router.ts +++ b/apps/api/src/services/customFields/customFields.router.ts @@ -9,6 +9,8 @@ import { customFieldsVeranstaltungCreate } from './customFieldsVeranstaltungCrea import { customFieldsVeranstaltungDelete, customFieldsUnterveranstaltungDelete } from './customFieldsDelete.js' import { customFieldsUpdate } from './customFieldsUpdate.js' import { customFieldValuesUpdate } from './customFieldValuesUpdate.js' +import { customFieldUnterveranstaltungOrder } from './schema/customFieldsUnterveranstaltungOrder.js' +import { customFieldVeranstaltungOrder } from './schema/customFieldsVeranstaltungOrder.js' // Import Routes here - do not delete this line export const customFieldsRouter = mergeRouters( @@ -21,6 +23,8 @@ export const customFieldsRouter = mergeRouters( customFieldsUnterveranstaltungDelete, customFieldsUnterveranstaltungCreate, customFieldValuesUpdate, - customFieldsTemplates + customFieldsTemplates, + customFieldVeranstaltungOrder, + customFieldUnterveranstaltungOrder // Add Routes here - do not delete this line ) diff --git a/apps/api/src/services/customFields/customFieldsList.ts b/apps/api/src/services/customFields/customFieldsList.ts index f7d9a09c..35fbd0f9 100644 --- a/apps/api/src/services/customFields/customFieldsList.ts +++ b/apps/api/src/services/customFields/customFieldsList.ts @@ -2,7 +2,7 @@ import { z } from 'zod' import { CustomFieldPosition, CustomFieldType, Prisma } from '@prisma/client' import prisma from '../../prisma.js' -import { definePublicQueryProcedure } from '../../types/defineProcedure.js' +import { defineProtectedQueryProcedure, definePublicQueryProcedure } from '../../types/defineProcedure.js' import { calculatePagination, defineEmptyQueryResponse, @@ -10,6 +10,7 @@ import { defineTableInput, } from '../../types/defineTableProcedure.js' import { boolish } from '../../util/zod.js' +import { getGliederungRequireAdmin } from '../../util/getGliederungRequireAdmin.js' const baseFilter = z.strictObject({ entity: z.enum(['veranstaltung', 'unterveranstaltung']), @@ -23,6 +24,7 @@ export const customFieldsTable = definePublicQueryProcedure({ async handler({ input: { entity, entityId, position } }) { if (entity === 'veranstaltung') { return await prisma.customField.findMany({ + orderBy: [{ order: { sort: 'asc', nulls: 'last' } }], where: { veranstaltungId: entityId, positions: @@ -35,6 +37,14 @@ export const customFieldsTable = definePublicQueryProcedure({ }) } else if (entity === 'unterveranstaltung') { return await prisma.customField.findMany({ + orderBy: [ + { + unterveranstaltungId: { sort: 'asc', nulls: 'first' }, + }, + { + order: { sort: 'asc', nulls: 'last' }, + }, + ], where: { positions: position === undefined @@ -64,8 +74,9 @@ export const customFieldsTable = definePublicQueryProcedure({ }, }) -export const customFieldsList = definePublicQueryProcedure({ +export const customFieldsList = defineProtectedQueryProcedure({ key: 'table', + roleIds: ['ADMIN', 'GLIEDERUNG_ADMIN'], inputSchema: baseFilter.extend({ table: defineTableInput({ filter: { @@ -74,10 +85,14 @@ export const customFieldsList = definePublicQueryProcedure({ required: boolish, position: z.nativeEnum(CustomFieldPosition), }, - orderBy: ['name'], + orderBy: ['name', 'order'], }), }), - async handler({ input }) { + async handler({ ctx, input }) { + if (ctx.account.role === 'GLIEDERUNG_ADMIN') { + await getGliederungRequireAdmin(ctx.accountId) + } + const where: Prisma.CustomFieldWhereInput = { name: { contains: input.table?.filter?.name, @@ -93,6 +108,25 @@ export const customFieldsList = definePublicQueryProcedure({ }, } + if (input.entity === 'veranstaltung') { + where.veranstaltungId = input.entityId + } else if (input.entity === 'unterveranstaltung') { + where.OR = [ + { + unterveranstaltungId: input.entityId, + }, + { + veranstaltung: { + unterveranstaltungen: { + some: { + id: input.entityId, + }, + }, + }, + }, + ] + } + const total = await prisma.customField.count({ where }) const { pageIndex, pageSize, pages } = calculatePagination(total, input.table?.pagination) @@ -100,15 +134,13 @@ export const customFieldsList = definePublicQueryProcedure({ const customFields = await prisma.customField.findMany({ take: pageSize, skip: pageSize * pageIndex, - where: { - ...where, - veranstaltungId: input.entityId, - }, + where, orderBy: input.table?.orderBy, select: { id: true, name: true, description: true, + order: true, type: true, positions: true, required: true, @@ -122,27 +154,18 @@ export const customFieldsList = definePublicQueryProcedure({ const customFields = await prisma.customField.findMany({ take: pageSize, skip: pageSize * pageIndex, - where: { - ...where, - OR: [ - { - unterveranstaltungId: input.entityId, - }, - { - veranstaltung: { - unterveranstaltungen: { - some: { - id: input.entityId, - }, - }, - }, - }, - ], - }, + orderBy: [ + { + unterveranstaltungId: { sort: 'asc', nulls: 'first' }, + }, + ...(input.table?.orderBy ?? []), + ], + where, select: { id: true, name: true, description: true, + order: true, type: true, positions: true, required: true, diff --git a/apps/api/src/services/customFields/schema/customField.schema.ts b/apps/api/src/services/customFields/schema/customField.schema.ts index 8172e451..af4585c1 100644 --- a/apps/api/src/services/customFields/schema/customField.schema.ts +++ b/apps/api/src/services/customFields/schema/customField.schema.ts @@ -1,11 +1,21 @@ import { CustomFieldPosition, CustomFieldType } from '@prisma/client' import { z } from 'zod' -export const customFieldSchema = z.strictObject({ - name: z.string().min(1), - description: z.string().nullable(), - type: z.nativeEnum(CustomFieldType), - required: z.boolean(), - options: z.array(z.string()), - positions: z.nativeEnum(CustomFieldPosition).array(), -}) +export const customFieldSchema = z + .strictObject({ + name: z.string().min(1), + description: z.string().nullable(), + type: z.nativeEnum(CustomFieldType), + required: z.boolean(), + options: z.array(z.string()), + positions: z.nativeEnum(CustomFieldPosition).array().min(1), + }) + .superRefine((values, ctx) => { + const optionTypes = ['BASIC_DROPDOWN', 'BASIC_RADIO', 'BASIC_SWITCH'] as CustomFieldType[] + if (optionTypes.includes(values.type) && values.options.length === 0) { + ctx.addIssue({ + code: 'custom', + message: 'Es muss mindestens eine Auswahlmöglichkeit angegeben werden', + }) + } + }) diff --git a/apps/api/src/services/customFields/schema/customFieldsUnterveranstaltungOrder.ts b/apps/api/src/services/customFields/schema/customFieldsUnterveranstaltungOrder.ts new file mode 100644 index 00000000..8f672d27 --- /dev/null +++ b/apps/api/src/services/customFields/schema/customFieldsUnterveranstaltungOrder.ts @@ -0,0 +1,54 @@ +import z from 'zod' +import { defineProtectedMutateProcedure } from '../../../types/defineProcedure.js' +import { getGliederungRequireAdmin } from '../../../util/getGliederungRequireAdmin.js' +import { TRPCError } from '@trpc/server' +import prisma from '../../../prisma.js' + +export const customFieldUnterveranstaltungOrder = defineProtectedMutateProcedure({ + key: 'unterveranstaltungOrder', + roleIds: ['ADMIN', 'GLIEDERUNG_ADMIN'], + inputSchema: z.strictObject({ + unterveranstaltungId: z.uuid(), + fields: z.array(z.uuid()), + }), + handler: async ({ ctx, input }) => { + if (ctx.account.role === 'GLIEDERUNG_ADMIN') { + const gliederung = await getGliederungRequireAdmin(ctx.accountId) + if (gliederung.id !== input.unterveranstaltungId) { + throw new TRPCError({ + code: 'FORBIDDEN', + }) + } + } + + const fields = await prisma.customField.findMany({ + where: { + unterveranstaltungId: input.unterveranstaltungId, + id: { + in: input.fields, + }, + }, + }) + + if (fields.length !== input.fields.length) { + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'Some supplied fields do not belong to the given unterveranstaltung!', + }) + } + + await prisma.$transaction( + input.fields.map((field, index) => + prisma.customField.update({ + where: { + unterveranstaltungId: input.unterveranstaltungId, + id: field, + }, + data: { + order: index + 1, + }, + }) + ) + ) + }, +}) diff --git a/apps/api/src/services/customFields/schema/customFieldsVeranstaltungOrder.ts b/apps/api/src/services/customFields/schema/customFieldsVeranstaltungOrder.ts new file mode 100644 index 00000000..ac435865 --- /dev/null +++ b/apps/api/src/services/customFields/schema/customFieldsVeranstaltungOrder.ts @@ -0,0 +1,44 @@ +import { TRPCError } from '@trpc/server' +import z from 'zod' +import prisma from '../../../prisma.js' +import { defineProtectedMutateProcedure } from '../../../types/defineProcedure.js' + +export const customFieldVeranstaltungOrder = defineProtectedMutateProcedure({ + key: 'veranstaltungOrder', + roleIds: ['ADMIN'], + inputSchema: z.strictObject({ + veranstaltungId: z.uuid(), + fields: z.array(z.uuid()), + }), + handler: async ({ input }) => { + const fields = await prisma.customField.findMany({ + where: { + veranstaltungId: input.veranstaltungId, + id: { + in: input.fields, + }, + }, + }) + + if (fields.length !== input.fields.length) { + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'Some supplied fields do not belong to the given veranstaltung!', + }) + } + + await prisma.$transaction( + input.fields.map((field, index) => + prisma.customField.update({ + where: { + veranstaltungId: input.veranstaltungId, + id: field, + }, + data: { + order: index + 1, + }, + }) + ) + ) + }, +}) diff --git a/apps/frontend/src/components/CustomFields/CustomFieldsTable.vue b/apps/frontend/src/components/CustomFields/CustomFieldsTable.vue index f9bcf945..2ec38a34 100644 --- a/apps/frontend/src/components/CustomFields/CustomFieldsTable.vue +++ b/apps/frontend/src/components/CustomFields/CustomFieldsTable.vue @@ -32,6 +32,18 @@ const router = useRouter() const column = createColumnHelper() const columns = [ + column.accessor('order', { + header: 'Reihenfolge', + enableSorting: true, + cell({ getValue }) { + const order = getValue() + if (!order) { + return '-' + } + + return order + }, + }), column.accessor('name', { header: 'Name', enableColumnFilter: true, @@ -174,6 +186,7 @@ function onClick(field: CustomField) { diff --git a/apps/frontend/src/views/Unterveranstaltung/CustomFields/CustomFieldUnterveranstaltungOrder.vue b/apps/frontend/src/views/Unterveranstaltung/CustomFields/CustomFieldUnterveranstaltungOrder.vue new file mode 100644 index 00000000..d324a12e --- /dev/null +++ b/apps/frontend/src/views/Unterveranstaltung/CustomFields/CustomFieldUnterveranstaltungOrder.vue @@ -0,0 +1,168 @@ + + + diff --git a/apps/frontend/src/views/Unterveranstaltung/UnterveranstaltungDetail.vue b/apps/frontend/src/views/Unterveranstaltung/UnterveranstaltungDetail.vue index 8c8f7e8a..6a8eecbf 100644 --- a/apps/frontend/src/views/Unterveranstaltung/UnterveranstaltungDetail.vue +++ b/apps/frontend/src/views/Unterveranstaltung/UnterveranstaltungDetail.vue @@ -371,6 +371,7 @@ const anmeldeLinkCreateModal = useTemplateRef('anmeldeLinkCreateModal') Hier können benutzerdefinierte Felder erstellt werden, welche für alle Unterveranstaltungen gelten.

+
+ Reihenfolge ändern + +
import('./CustomFields/CustomFieldUnterveranstaltungOrder.vue'), + meta: { + breadcrumbs: [ + detailCrumb, + { + text: 'Benutzerdefinierte Felder sortieren', + }, + ], + }, + }, { name: 'Unterveranstaltung Custom Field bearbeiten', path: ':unterveranstaltungId/fields/:fieldId', diff --git a/apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue b/apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue new file mode 100644 index 00000000..347fd5f3 --- /dev/null +++ b/apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue @@ -0,0 +1,126 @@ + + + diff --git a/apps/frontend/src/views/Verwaltung/Veranstaltungen/VeranstaltungDetail.vue b/apps/frontend/src/views/Verwaltung/Veranstaltungen/VeranstaltungDetail.vue index 7730e943..74d2d789 100644 --- a/apps/frontend/src/views/Verwaltung/Veranstaltungen/VeranstaltungDetail.vue +++ b/apps/frontend/src/views/Verwaltung/Veranstaltungen/VeranstaltungDetail.vue @@ -10,6 +10,7 @@ import { RocketLaunchIcon, DocumentDuplicateIcon, LinkIcon, + ChevronUpDownIcon, } from '@heroicons/vue/24/outline' import { useAsyncState } from '@vueuse/core' import { computed } from 'vue' @@ -352,12 +353,25 @@ function copyProgramLink() { Hier können benutzerdefinierte Felder erstellt werden, welche für alle Unterveranstaltungen gelten.

- - Neues Feld - +
+ + + Neues Feld + + + + Reihenfolge ändern + +
import('./CustomFields/CustomFieldVeranstaltungOrder.vue'), + meta: { + breadcrumbs: [ + detailCrumb, + { + text: 'Benutzerdefinierte Felder sortieren', + }, + ], + }, + }, { name: 'Verwaltung Custom Field bearbeiten', path: ':veranstaltungId/fields/:fieldId', From 82b0b30c1c640ab5ca58705f0c15b14e6387ebbe Mon Sep 17 00:00:00 2001 From: Axel Rindle Date: Wed, 4 Mar 2026 22:50:52 +0100 Subject: [PATCH 06/11] chore: remove unused import --- .../CustomFields/CustomFieldVeranstaltungOrder.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue b/apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue index 347fd5f3..0e0d5c20 100644 --- a/apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue +++ b/apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue @@ -2,7 +2,6 @@ import { apiClient } from '@/api' import Button from '@/components/UIComponents/Button.vue' import { useRouteTitle } from '@/composables/useRouteTitle' -import cn from '@/helpers/cn' import { CustomFieldTypeMapping, type CustomField } from '@codeanker/api' import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/outline' import { useMutation, useQuery } from '@tanstack/vue-query' From 0ab0d2aa4679307b7ea42bd9b2820b09ba7def51 Mon Sep 17 00:00:00 2001 From: Axel Rindle Date: Wed, 4 Mar 2026 23:00:42 +0100 Subject: [PATCH 07/11] fix: zod schema --- .../schema/customFieldsUnterveranstaltungOrder.ts | 4 ++-- .../customFields/schema/customFieldsVeranstaltungOrder.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/api/src/services/customFields/schema/customFieldsUnterveranstaltungOrder.ts b/apps/api/src/services/customFields/schema/customFieldsUnterveranstaltungOrder.ts index 8f672d27..2b0f974a 100644 --- a/apps/api/src/services/customFields/schema/customFieldsUnterveranstaltungOrder.ts +++ b/apps/api/src/services/customFields/schema/customFieldsUnterveranstaltungOrder.ts @@ -8,8 +8,8 @@ export const customFieldUnterveranstaltungOrder = defineProtectedMutateProcedure key: 'unterveranstaltungOrder', roleIds: ['ADMIN', 'GLIEDERUNG_ADMIN'], inputSchema: z.strictObject({ - unterveranstaltungId: z.uuid(), - fields: z.array(z.uuid()), + unterveranstaltungId: z.string().uuid(), + fields: z.array(z.string().uuid()), }), handler: async ({ ctx, input }) => { if (ctx.account.role === 'GLIEDERUNG_ADMIN') { diff --git a/apps/api/src/services/customFields/schema/customFieldsVeranstaltungOrder.ts b/apps/api/src/services/customFields/schema/customFieldsVeranstaltungOrder.ts index ac435865..3cc63732 100644 --- a/apps/api/src/services/customFields/schema/customFieldsVeranstaltungOrder.ts +++ b/apps/api/src/services/customFields/schema/customFieldsVeranstaltungOrder.ts @@ -7,8 +7,8 @@ export const customFieldVeranstaltungOrder = defineProtectedMutateProcedure({ key: 'veranstaltungOrder', roleIds: ['ADMIN'], inputSchema: z.strictObject({ - veranstaltungId: z.uuid(), - fields: z.array(z.uuid()), + veranstaltungId: z.string().uuid(), + fields: z.array(z.string().uuid()), }), handler: async ({ input }) => { const fields = await prisma.customField.findMany({ From 21c880347e8e1745a263290542a397ab905ae410 Mon Sep 17 00:00:00 2001 From: Axel Rindle Date: Wed, 4 Mar 2026 23:17:45 +0100 Subject: [PATCH 08/11] chore: ui updates --- .../CustomFieldUnterveranstaltungOrder.vue | 28 ++++++++++++++++--- .../UnterveranstaltungDetail.vue | 12 +++++--- .../CustomFieldVeranstaltungOrder.vue | 25 +++++++++++++++-- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/apps/frontend/src/views/Unterveranstaltung/CustomFields/CustomFieldUnterveranstaltungOrder.vue b/apps/frontend/src/views/Unterveranstaltung/CustomFields/CustomFieldUnterveranstaltungOrder.vue index d324a12e..ae0387af 100644 --- a/apps/frontend/src/views/Unterveranstaltung/CustomFields/CustomFieldUnterveranstaltungOrder.vue +++ b/apps/frontend/src/views/Unterveranstaltung/CustomFields/CustomFieldUnterveranstaltungOrder.vue @@ -5,10 +5,11 @@ import { useRouteTitle } from '@/composables/useRouteTitle' import cn from '@/helpers/cn' import { CustomFieldTypeMapping, type CustomField } from '@codeanker/api' import { groupBy } from '@codeanker/helpers' -import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/outline' +import { ArrowLeftIcon, ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/outline' import { useMutation, useQuery } from '@tanstack/vue-query' import { useRouteParams } from '@vueuse/router' import { computed, ref, watch } from 'vue' +import { RouterLink } from 'vue-router' const { setTitle } = useRouteTitle() setTitle('Benutzerdefinierte Felder sortieren') @@ -93,6 +94,23 @@ const { mutate, isPending } = useMutation({ diff --git a/apps/frontend/src/views/Unterveranstaltung/UnterveranstaltungDetail.vue b/apps/frontend/src/views/Unterveranstaltung/UnterveranstaltungDetail.vue index 6a8eecbf..83b18296 100644 --- a/apps/frontend/src/views/Unterveranstaltung/UnterveranstaltungDetail.vue +++ b/apps/frontend/src/views/Unterveranstaltung/UnterveranstaltungDetail.vue @@ -2,12 +2,14 @@ import { CameraIcon, ChatBubbleLeftRightIcon, + ChevronUpDownIcon, CodeBracketIcon, DocumentDuplicateIcon, DocumentIcon, HandRaisedIcon, LinkIcon, MegaphoneIcon, + PlusIcon, RocketLaunchIcon, SquaresPlusIcon, UserGroupIcon, @@ -373,22 +375,24 @@ const anmeldeLinkCreateModal = useTemplateRef('anmeldeLinkCreateModal')
- Neues Feld + + Neues Feld - Reihenfolge ändern + + Reihenfolge ändern
diff --git a/apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue b/apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue index 0e0d5c20..df07c4a2 100644 --- a/apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue +++ b/apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue @@ -3,7 +3,7 @@ import { apiClient } from '@/api' import Button from '@/components/UIComponents/Button.vue' import { useRouteTitle } from '@/composables/useRouteTitle' import { CustomFieldTypeMapping, type CustomField } from '@codeanker/api' -import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/outline' +import { ArrowLeftIcon, ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/outline' import { useMutation, useQuery } from '@tanstack/vue-query' import { useRouteParams } from '@vueuse/router' import { ref, watch } from 'vue' @@ -60,6 +60,23 @@ const { mutate, isPending } = useMutation({ From ed57005b6049788a3e836e9b51c44f3975117696 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 22:23:07 +0000 Subject: [PATCH 09/11] chore(deps): bump @hono/node-server from 1.19.7 to 1.19.10 Bumps [@hono/node-server](https://github.com/honojs/node-server) from 1.19.7 to 1.19.10. - [Release notes](https://github.com/honojs/node-server/releases) - [Commits](https://github.com/honojs/node-server/compare/v1.19.7...v1.19.10) --- updated-dependencies: - dependency-name: "@hono/node-server" dependency-version: 1.19.10 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- apps/api/package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/api/package.json b/apps/api/package.json index f3d74cd6..26eb35cf 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -33,7 +33,7 @@ "@codeanker/service-sms": "file:../../vendor/codeanker-service-sms-0.0.2.tar.gz", "@e965/xlsx": "^0.20.3", "@faker-js/faker": "catalog:", - "@hono/node-server": "^1.19.6", + "@hono/node-server": "^1.19.10", "@hono/trpc-server": "^0.4.0", "@prisma/client": "^5.19.1", "@prisma/extension-accelerate": "^1.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2726fdbc..e3f779a0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,8 +117,8 @@ importers: specifier: 'catalog:' version: 10.1.0 '@hono/node-server': - specifier: ^1.19.6 - version: 1.19.7(hono@4.12.4) + specifier: ^1.19.10 + version: 1.19.10(hono@4.12.4) '@hono/trpc-server': specifier: ^0.4.0 version: 0.4.1(@trpc/server@11.0.0-rc.682(typescript@5.7.2))(hono@4.12.4) @@ -1430,8 +1430,8 @@ packages: peerDependencies: vue: '>= 3' - '@hono/node-server@1.19.7': - resolution: {integrity: sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==} + '@hono/node-server@1.19.10': + resolution: {integrity: sha512-hZ7nOssGqRgyV3FVVQdfi+U4q02uB23bpnYpdvNXkYTRRyWx84b7yf1ans+dnJ/7h41sGL3CeQTfO+ZGxuO+Iw==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 @@ -6050,7 +6050,7 @@ snapshots: dependencies: vue: 3.5.13(typescript@5.7.2) - '@hono/node-server@1.19.7(hono@4.12.4)': + '@hono/node-server@1.19.10(hono@4.12.4)': dependencies: hono: 4.12.4 From 7a0779c3932d75cd84134b7bf6e3076ea828b1a3 Mon Sep 17 00:00:00 2001 From: Axel Rindle Date: Thu, 5 Mar 2026 09:18:21 +0100 Subject: [PATCH 10/11] feat: show notification --- .../CustomFieldUnterveranstaltungOrder.vue | 15 +++++++++++++++ .../CustomFieldVeranstaltungOrder.vue | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/apps/frontend/src/views/Unterveranstaltung/CustomFields/CustomFieldUnterveranstaltungOrder.vue b/apps/frontend/src/views/Unterveranstaltung/CustomFields/CustomFieldUnterveranstaltungOrder.vue index ae0387af..8cb874a9 100644 --- a/apps/frontend/src/views/Unterveranstaltung/CustomFields/CustomFieldUnterveranstaltungOrder.vue +++ b/apps/frontend/src/views/Unterveranstaltung/CustomFields/CustomFieldUnterveranstaltungOrder.vue @@ -1,5 +1,6 @@