v1.2.0: Symbol-Verwaltung, SOMA-Admin, Light Mode Farbsystem, Onboarding-Tour, Credit-Link
This commit is contained in:
@@ -38,20 +38,28 @@ export async function GET() {
|
||||
},
|
||||
})
|
||||
|
||||
// Get tenant's hidden icon IDs
|
||||
// Get tenant's hidden icon IDs (legacy) + TenantSymbol overrides
|
||||
let hiddenIconIds: string[] = []
|
||||
let deactivatedIconIds = new Set<string>()
|
||||
if (user?.tenantId) {
|
||||
const tenant = await (prisma as any).tenant.findUnique({
|
||||
where: { id: user.tenantId },
|
||||
select: { hiddenIconIds: true },
|
||||
})
|
||||
const [tenant, tenantSymbols] = await Promise.all([
|
||||
(prisma as any).tenant.findUnique({
|
||||
where: { id: user.tenantId },
|
||||
select: { hiddenIconIds: true },
|
||||
}),
|
||||
(prisma as any).tenantSymbol.findMany({
|
||||
where: { tenantId: user.tenantId, isActive: false },
|
||||
select: { iconId: true },
|
||||
}),
|
||||
])
|
||||
hiddenIconIds = tenant?.hiddenIconIds || []
|
||||
deactivatedIconIds = new Set(tenantSymbols.map((ts: any) => ts.iconId))
|
||||
}
|
||||
|
||||
const categoriesWithUrls = categories.map((cat: any) => ({
|
||||
...cat,
|
||||
icons: cat.icons
|
||||
.filter((icon: any) => !hiddenIconIds.includes(icon.id))
|
||||
.filter((icon: any) => !hiddenIconIds.includes(icon.id) && !deactivatedIconIds.has(icon.id))
|
||||
.map((icon: any) => ({
|
||||
...icon,
|
||||
url: `/api/icons/${icon.id}/image`,
|
||||
|
||||
@@ -24,10 +24,17 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
if (existing.length > 0) {
|
||||
return NextResponse.json(existing)
|
||||
}
|
||||
const templates = await (prisma as any).journalCheckTemplate.findMany({
|
||||
where: { isActive: true },
|
||||
// Prefer tenant-specific templates; fall back to global (tenantId=null) if none exist
|
||||
let templates = await (prisma as any).journalCheckTemplate.findMany({
|
||||
where: { isActive: true, tenantId: user.tenantId || null },
|
||||
orderBy: { sortOrder: 'asc' },
|
||||
})
|
||||
if (templates.length === 0 && user.tenantId) {
|
||||
templates = await (prisma as any).journalCheckTemplate.findMany({
|
||||
where: { isActive: true, tenantId: null },
|
||||
orderBy: { sortOrder: 'asc' },
|
||||
})
|
||||
}
|
||||
const items = await Promise.all(
|
||||
templates.map((tpl: any, i: number) =>
|
||||
(prisma as any).journalCheckItem.create({
|
||||
|
||||
108
src/app/api/tenant/soma-templates/route.ts
Normal file
108
src/app/api/tenant/soma-templates/route.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/db'
|
||||
import { getSession } from '@/lib/auth'
|
||||
|
||||
// GET: List SOMA templates for the current tenant
|
||||
export async function GET() {
|
||||
try {
|
||||
const user = await getSession()
|
||||
if (!user) return NextResponse.json({ error: 'Nicht autorisiert' }, { status: 401 })
|
||||
if (user.role !== 'TENANT_ADMIN' && user.role !== 'SERVER_ADMIN') {
|
||||
return NextResponse.json({ error: 'Keine Berechtigung' }, { status: 403 })
|
||||
}
|
||||
|
||||
const templates = await (prisma as any).journalCheckTemplate.findMany({
|
||||
where: { tenantId: user.tenantId || null },
|
||||
orderBy: { sortOrder: 'asc' },
|
||||
})
|
||||
|
||||
return NextResponse.json({ templates })
|
||||
} catch (error) {
|
||||
console.error('Error fetching SOMA templates:', error)
|
||||
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
// POST: Create a new SOMA template for the current tenant
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const user = await getSession()
|
||||
if (!user) return NextResponse.json({ error: 'Nicht autorisiert' }, { status: 401 })
|
||||
if (user.role !== 'TENANT_ADMIN' && user.role !== 'SERVER_ADMIN') {
|
||||
return NextResponse.json({ error: 'Keine Berechtigung' }, { status: 403 })
|
||||
}
|
||||
|
||||
const { label, sortOrder } = await req.json()
|
||||
if (!label?.trim()) {
|
||||
return NextResponse.json({ error: 'Label ist erforderlich' }, { status: 400 })
|
||||
}
|
||||
|
||||
const template = await (prisma as any).journalCheckTemplate.create({
|
||||
data: {
|
||||
label: label.trim(),
|
||||
sortOrder: sortOrder ?? 0,
|
||||
tenantId: user.tenantId || null,
|
||||
isActive: true,
|
||||
},
|
||||
})
|
||||
|
||||
return NextResponse.json({ template }, { status: 201 })
|
||||
} catch (error) {
|
||||
console.error('Error creating SOMA template:', error)
|
||||
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
// PATCH: Update multiple templates (bulk reorder/toggle)
|
||||
export async function PATCH(req: NextRequest) {
|
||||
try {
|
||||
const user = await getSession()
|
||||
if (!user) return NextResponse.json({ error: 'Nicht autorisiert' }, { status: 401 })
|
||||
if (user.role !== 'TENANT_ADMIN' && user.role !== 'SERVER_ADMIN') {
|
||||
return NextResponse.json({ error: 'Keine Berechtigung' }, { status: 403 })
|
||||
}
|
||||
|
||||
const { updates } = await req.json()
|
||||
if (!Array.isArray(updates)) {
|
||||
return NextResponse.json({ error: 'updates Array erforderlich' }, { status: 400 })
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
updates.map((u: { id: string; label?: string; sortOrder?: number; isActive?: boolean }) =>
|
||||
(prisma as any).journalCheckTemplate.update({
|
||||
where: { id: u.id },
|
||||
data: {
|
||||
...(u.label !== undefined && { label: u.label }),
|
||||
...(u.sortOrder !== undefined && { sortOrder: u.sortOrder }),
|
||||
...(u.isActive !== undefined && { isActive: u.isActive }),
|
||||
},
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error('Error updating SOMA templates:', error)
|
||||
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE: Delete a SOMA template
|
||||
export async function DELETE(req: NextRequest) {
|
||||
try {
|
||||
const user = await getSession()
|
||||
if (!user) return NextResponse.json({ error: 'Nicht autorisiert' }, { status: 401 })
|
||||
if (user.role !== 'TENANT_ADMIN' && user.role !== 'SERVER_ADMIN') {
|
||||
return NextResponse.json({ error: 'Keine Berechtigung' }, { status: 403 })
|
||||
}
|
||||
|
||||
const { id } = await req.json()
|
||||
if (!id) return NextResponse.json({ error: 'ID erforderlich' }, { status: 400 })
|
||||
|
||||
await (prisma as any).journalCheckTemplate.delete({ where: { id } })
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error('Error deleting SOMA template:', error)
|
||||
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
83
src/app/api/tenant/symbols/route.ts
Normal file
83
src/app/api/tenant/symbols/route.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/db'
|
||||
import { getSession } from '@/lib/auth'
|
||||
|
||||
// GET: List all icons with their tenant-specific active status
|
||||
export async function GET() {
|
||||
try {
|
||||
const user = await getSession()
|
||||
if (!user) return NextResponse.json({ error: 'Nicht autorisiert' }, { status: 401 })
|
||||
if (user.role !== 'TENANT_ADMIN' && user.role !== 'SERVER_ADMIN') {
|
||||
return NextResponse.json({ error: 'Keine Berechtigung' }, { status: 403 })
|
||||
}
|
||||
|
||||
const tenantId = user.tenantId
|
||||
if (!tenantId) return NextResponse.json({ error: 'Kein Mandant zugeordnet' }, { status: 400 })
|
||||
|
||||
// Get all system icons (active ones)
|
||||
const icons = await (prisma as any).iconAsset.findMany({
|
||||
where: { isActive: true },
|
||||
include: { category: { select: { id: true, name: true } } },
|
||||
orderBy: [{ category: { sortOrder: 'asc' } }, { name: 'asc' }],
|
||||
})
|
||||
|
||||
// Get tenant-specific overrides
|
||||
const overrides = await (prisma as any).tenantSymbol.findMany({
|
||||
where: { tenantId },
|
||||
})
|
||||
|
||||
const overrideMap = new Map(overrides.map((o: any) => [o.iconId, o.isActive]))
|
||||
|
||||
// Merge: default is active (true) unless override says otherwise
|
||||
const symbols = icons.map((icon: any) => ({
|
||||
id: icon.id,
|
||||
name: icon.name,
|
||||
fileKey: icon.fileKey,
|
||||
mimeType: icon.mimeType,
|
||||
iconType: icon.iconType,
|
||||
categoryId: icon.categoryId,
|
||||
categoryName: icon.category?.name || 'Ohne Kategorie',
|
||||
isActive: overrideMap.has(icon.id) ? overrideMap.get(icon.id) : true,
|
||||
}))
|
||||
|
||||
return NextResponse.json({ symbols })
|
||||
} catch (error) {
|
||||
console.error('Error fetching tenant symbols:', error)
|
||||
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
// PATCH: Update symbol visibility for the tenant (bulk)
|
||||
export async function PATCH(req: NextRequest) {
|
||||
try {
|
||||
const user = await getSession()
|
||||
if (!user) return NextResponse.json({ error: 'Nicht autorisiert' }, { status: 401 })
|
||||
if (user.role !== 'TENANT_ADMIN' && user.role !== 'SERVER_ADMIN') {
|
||||
return NextResponse.json({ error: 'Keine Berechtigung' }, { status: 403 })
|
||||
}
|
||||
|
||||
const tenantId = user.tenantId
|
||||
if (!tenantId) return NextResponse.json({ error: 'Kein Mandant zugeordnet' }, { status: 400 })
|
||||
|
||||
const { updates } = await req.json()
|
||||
if (!Array.isArray(updates)) {
|
||||
return NextResponse.json({ error: 'updates Array erforderlich' }, { status: 400 })
|
||||
}
|
||||
|
||||
// Upsert each symbol override
|
||||
await Promise.all(
|
||||
updates.map((u: { iconId: string; isActive: boolean }) =>
|
||||
(prisma as any).tenantSymbol.upsert({
|
||||
where: { tenantId_iconId: { tenantId, iconId: u.iconId } },
|
||||
update: { isActive: u.isActive },
|
||||
create: { tenantId, iconId: u.iconId, isActive: u.isActive },
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error('Error updating tenant symbols:', error)
|
||||
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user