diff --git a/apps/api/package.json b/apps/api/package.json
index 03168c28..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",
@@ -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/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/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)
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..2b0f974a
--- /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.string().uuid(),
+ fields: z.array(z.string().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..3cc63732
--- /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.string().uuid(),
+ fields: z.array(z.string().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/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,
},
})
},
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/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 {
>
-
Teilnahmebedingungen
-
+
Teilnahmebedingungen der Gliederung
+
+
+
+
Allgemeine Teilnahmebedingungen
-
+
+
+
+
+
+
@@ -145,6 +158,7 @@ const {
:is-public-anmeldung="props.isPublic"
@submit="(value) => createAnmeldung(undefined, value)"
@show-terms="showBedingungen = true"
+ @show-privacy="showDatenschutz = true"
>
+import { apiClient } from '@/api'
+import Notification from '@/components/LayoutComponents/Notifications.vue'
+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 { groupBy } from '@codeanker/helpers'
+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')
+
+const unterveranstaltungId = useRouteParams
('unterveranstaltungId')
+
+const { data, dataUpdatedAt, refetch } = useQuery({
+ queryKey: ['customFields', 'unterveranstaltung', unterveranstaltungId],
+ queryFn: () =>
+ apiClient.customFields.list.query({
+ entity: 'unterveranstaltung',
+ entityId: unterveranstaltungId.value,
+ }),
+ enabled: () => !!unterveranstaltungId.value,
+})
+
+const copy = ref([])
+
+watch(dataUpdatedAt, () => {
+ if (data.value) {
+ copy.value = [...data.value]
+ }
+})
+
+function formatType(field: CustomField) {
+ const { human } = CustomFieldTypeMapping[field.type]
+ return human
+}
+
+function formatSource(field: CustomField) {
+ if (field.veranstaltungId) {
+ return 'Veranstaltung'
+ } else if (field.unterveranstaltungId) {
+ return 'Ausschreibung'
+ }
+ return '-'
+}
+
+type Grouped = {
+ veranstaltung: CustomField[]
+ ausschreibung: CustomField[]
+}
+
+const fields = computed(() => {
+ return groupBy(copy.value, (field) => {
+ if (field.veranstaltungId !== null) {
+ return 'veranstaltung'
+ }
+ if (field.unterveranstaltungId !== null) {
+ return 'ausschreibung'
+ }
+ return 'invalid'
+ })
+})
+
+function moveUp(index: number) {
+ const offset = fields.value.veranstaltung.length
+ const realIndex = index + offset
+
+ const rows = [copy.value[realIndex - 1], copy.value[realIndex]]
+ copy.value.splice(realIndex - 1, 2, rows[1], rows[0])
+}
+
+function moveDown(index: number) {
+ const offset = fields.value.veranstaltung.length
+ const realIndex = index + offset
+
+ const rows = [copy.value[realIndex], copy.value[realIndex + 1]]
+ copy.value.splice(realIndex, 2, rows[1], rows[0])
+}
+
+const showNotification = ref(false)
+
+const { mutate, isPending } = useMutation({
+ mutationFn: async () => {
+ const ids = fields.value.ausschreibung.map(({ id }) => id)
+ await apiClient.customFields.unterveranstaltungOrder.mutate({
+ unterveranstaltungId: unterveranstaltungId.value,
+ fields: ids,
+ })
+ showNotification.value = true
+ await refetch()
+ },
+})
+
+
+
+
+
+
+ Zurück zur Ausschreibung
+
+
+
+
+
Reihenfolge ändern
+
+ Hier kann die Reihenfolge der benutzerdefinierten Felder deiner Ausschreibung verändert werden.
+
+
+
+
+
+
+ Reihenfolge
+ Name
+ Typ
+ Quelle
+
+
+
+
+
+
+
+ {{ field.order || '-' }}
+ ( {{ index + 1 }} )
+
+
+
+
+
+
+
+
+
+
+ {{ field.name }}
+ {{ formatType(field) }}
+ {{ formatSource(field) }}
+
+
+
+
+
+
+ Hinweis : Die Spalte Reihenfolge gliedert sich wie folgt: Die erste Zahl zeigt die aktuelle Position in
+ der Reihenfolge wohingegen die Zahl in der Klammer die neue Position anzeigt.
+
+
+
+
+ Speichern
+
+
+ Abbrechen
+
+
+
+
+ Erfolgreich gespeichert
+
+ Deine Änderungen wurden erfolgreich gespeichert.
+
+
+
diff --git a/apps/frontend/src/views/Unterveranstaltung/UnterveranstaltungDetail.vue b/apps/frontend/src/views/Unterveranstaltung/UnterveranstaltungDetail.vue
index 8c8f7e8a..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,
@@ -371,15 +373,28 @@ const anmeldeLinkCreateModal = useTemplateRef('anmeldeLinkCreateModal')
Hier können benutzerdefinierte Felder erstellt werden, welche für alle Unterveranstaltungen gelten.
+
- Neues Feld
+
+ Neues Feld
+
+
+ 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..ddcef8b9
--- /dev/null
+++ b/apps/frontend/src/views/Verwaltung/Veranstaltungen/CustomFields/CustomFieldVeranstaltungOrder.vue
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+ Zurück zur Veranstaltung
+
+
+
+
+
Reihenfolge ändern
+
+ Hier kann die Reihenfolge benutzerdefinierter Felder der Veranstaltung verändert werden.
+
+
+
+
+
+
+ Reihenfolge
+ Name
+ Typ
+ Quelle
+
+
+
+
+
+
+ {{ field.order || '-' }}
+ ( {{ index + 1 }} )
+
+
+
+
+
+
+
+
+
+
+ {{ field.name }}
+ {{ formatType(field) }}
+ Veranstaltung
+
+
+
+
+
+ Hinweis : Die Spalte Reihenfolge gliedert sich wie folgt: Die erste Zahl zeigt die aktuelle Position in
+ der Reihenfolge wohingegen die Zahl in der Klammer die neue Position anzeigt.
+
+
+
+
+ Speichern
+
+
+ Abbrechen
+
+
+
+
+ Erfolgreich gespeichert
+
+ Deine Änderungen wurden erfolgreich gespeichert.
+
+
+
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',
diff --git a/package.json b/package.json
index f8050799..24c49fdf 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "codeanker-project",
"author": "CODEANKER GmbH",
- "version": "2.10.0",
+ "version": "2.11.0",
"description": "",
"license": "CC-BY-3.0-DE",
"workspaces": [
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 94e82143..e3f779a0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -117,11 +117,11 @@ importers:
specifier: 'catalog:'
version: 10.1.0
'@hono/node-server':
- specifier: ^1.19.6
- version: 1.19.7(hono@4.12.0)
+ 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.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
@@ -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
@@ -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.10(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