Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "CustomField" ADD COLUMN "order" INTEGER;
1 change: 1 addition & 0 deletions apps/api/prisma/schema/CustomField.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand Down
11 changes: 10 additions & 1 deletion apps/api/src/routes/oidc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 5 additions & 1 deletion apps/api/src/services/customFields/customFields.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -21,6 +23,8 @@ export const customFieldsRouter = mergeRouters(
customFieldsUnterveranstaltungDelete,
customFieldsUnterveranstaltungCreate,
customFieldValuesUpdate,
customFieldsTemplates
customFieldsTemplates,
customFieldVeranstaltungOrder,
customFieldUnterveranstaltungOrder
// Add Routes here - do not delete this line
)
73 changes: 48 additions & 25 deletions apps/api/src/services/customFields/customFieldsList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ 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,
defineQueryResponse,
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']),
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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: {
Expand All @@ -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,
Expand All @@ -93,22 +108,39 @@ 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)

if (input.entity === 'veranstaltung') {
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,
Expand All @@ -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,
Expand Down
26 changes: 18 additions & 8 deletions apps/api/src/services/customFields/schema/customField.schema.ts
Original file line number Diff line number Diff line change
@@ -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',
})
}
})
Original file line number Diff line number Diff line change
@@ -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,
},
})
)
)
},
})
Original file line number Diff line number Diff line change
@@ -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,
},
})
)
)
},
})
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const gliederungVerwaltungGetProcedure = defineProtectedQueryProcedure({
id: true,
name: true,
edv: true,
domain: true,
},
})
},
Expand Down
13 changes: 13 additions & 0 deletions apps/frontend/src/components/CustomFields/CustomFieldsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ const router = useRouter()

const column = createColumnHelper<CustomField>()
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,
Expand Down Expand Up @@ -174,6 +186,7 @@ function onClick(field: CustomField) {
<DataTable
:query="query"
:columns="columns"
:initial-sort="[{ id: 'order', desc: false }]"
@click="onClick"
/>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const props = defineProps<{
const emit = defineEmits<{
submit: [data: FormPersonGeneralSubmit]
showTerms: []
showPrivacy: []
}>()

async function queryGliederungen(searchTerm: string) {
Expand Down Expand Up @@ -237,7 +238,7 @@ const submit = () => {
<span>Ich habe die gesonderten </span>
<u
class="cursor-pointer"
@click="emit('showTerms')"
@click="emit('showPrivacy')"
>
<span>Datenschutzerklärung</span>
<ArrowTopRightOnSquareIcon class="h-4 inline ml-1" />
Expand Down
Loading
Loading