v1.3.0: Refactoring Phase 3+4, Symbol-Verwaltung Redesign, Schlauch-Labels Fix
- Refactoring: Error Boundaries, apiFetch Wrapper, Socket Status-Tracking - Refactoring: UI Kontrast (theme-aware colors), unused imports bereinigt - Symbol-Verwaltung: Neues Split-Panel (Meine Symbole + Bibliothek) - Symbol-Verwaltung: Umbenennen (TLF rot/blau), Duplikate erlaubt - Symbol-Verwaltung: Karten-Sidebar zeigt eigene Symbole bevorzugt - Schlauch-Labels: Groessere Schrift (13px/10px), verschiebbar (Drag) - Schema: TenantSymbol customName, sortOrder, unique constraint entfernt - Open Source Referenz entfernt (kostenloses Projekt)
This commit is contained in:
@@ -2,82 +2,151 @@ 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
|
||||
async function getTenantId() {
|
||||
const user = await getSession()
|
||||
if (!user) return { error: 'Nicht autorisiert', status: 401 }
|
||||
if (user.role !== 'TENANT_ADMIN' && user.role !== 'SERVER_ADMIN') {
|
||||
return { error: 'Keine Berechtigung', status: 403 }
|
||||
}
|
||||
if (!user.tenantId) return { error: 'Kein Mandant zugeordnet', status: 400 }
|
||||
return { tenantId: user.tenantId }
|
||||
}
|
||||
|
||||
// GET: Returns library (all system icons) + tenant's own symbol collection
|
||||
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 auth = await getTenantId()
|
||||
if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status })
|
||||
const { tenantId } = auth
|
||||
|
||||
const tenantId = user.tenantId
|
||||
if (!tenantId) return NextResponse.json({ error: 'Kein Mandant zugeordnet' }, { status: 400 })
|
||||
|
||||
// Get all system icons (active ones)
|
||||
// All system icons grouped by category (the library)
|
||||
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) => ({
|
||||
const library = 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 })
|
||||
// Tenant's own symbol collection
|
||||
const tenantSymbols = await (prisma as any).tenantSymbol.findMany({
|
||||
where: { tenantId },
|
||||
include: { icon: { select: { id: true, name: true, mimeType: true, iconType: true, category: { select: { name: true } } } } },
|
||||
orderBy: { sortOrder: 'asc' },
|
||||
})
|
||||
|
||||
const mySymbols = tenantSymbols.map((ts: any) => ({
|
||||
id: ts.id,
|
||||
iconId: ts.iconId,
|
||||
name: ts.customName || ts.icon.name,
|
||||
customName: ts.customName,
|
||||
baseName: ts.icon.name,
|
||||
mimeType: ts.icon.mimeType,
|
||||
iconType: ts.icon.iconType,
|
||||
categoryName: ts.icon.category?.name || 'Ohne Kategorie',
|
||||
sortOrder: ts.sortOrder,
|
||||
}))
|
||||
|
||||
return NextResponse.json({ library, mySymbols })
|
||||
} 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) {
|
||||
// POST: Add a symbol from the library to "my symbols"
|
||||
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 auth = await getTenantId()
|
||||
if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status })
|
||||
const { tenantId } = auth
|
||||
|
||||
const tenantId = user.tenantId
|
||||
if (!tenantId) return NextResponse.json({ error: 'Kein Mandant zugeordnet' }, { status: 400 })
|
||||
const { iconId, customName } = await req.json()
|
||||
if (!iconId) return NextResponse.json({ error: 'iconId erforderlich' }, { status: 400 })
|
||||
|
||||
const { updates } = await req.json()
|
||||
if (!Array.isArray(updates)) {
|
||||
return NextResponse.json({ error: 'updates Array erforderlich' }, { status: 400 })
|
||||
}
|
||||
// Get max sortOrder for this tenant
|
||||
const maxSort = await (prisma as any).tenantSymbol.aggregate({
|
||||
where: { tenantId },
|
||||
_max: { sortOrder: true },
|
||||
})
|
||||
|
||||
// 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 },
|
||||
})
|
||||
)
|
||||
)
|
||||
const symbol = await (prisma as any).tenantSymbol.create({
|
||||
data: {
|
||||
tenantId,
|
||||
iconId,
|
||||
customName: customName || null,
|
||||
sortOrder: (maxSort._max.sortOrder ?? -1) + 1,
|
||||
},
|
||||
include: { icon: { select: { name: true, mimeType: true, iconType: true, category: { select: { name: true } } } } },
|
||||
})
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
return NextResponse.json({
|
||||
id: symbol.id,
|
||||
iconId: symbol.iconId,
|
||||
name: symbol.customName || symbol.icon.name,
|
||||
customName: symbol.customName,
|
||||
baseName: symbol.icon.name,
|
||||
mimeType: symbol.icon.mimeType,
|
||||
iconType: symbol.icon.iconType,
|
||||
categoryName: symbol.icon.category?.name || 'Ohne Kategorie',
|
||||
sortOrder: symbol.sortOrder,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error updating tenant symbols:', error)
|
||||
console.error('Error adding tenant symbol:', error)
|
||||
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
// PATCH: Rename a symbol or update sortOrder
|
||||
export async function PATCH(req: NextRequest) {
|
||||
try {
|
||||
const auth = await getTenantId()
|
||||
if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status })
|
||||
const { tenantId } = auth
|
||||
|
||||
const { id, customName, sortOrder } = await req.json()
|
||||
if (!id) return NextResponse.json({ error: 'id erforderlich' }, { status: 400 })
|
||||
|
||||
const data: any = {}
|
||||
if (customName !== undefined) data.customName = customName || null
|
||||
if (sortOrder !== undefined) data.sortOrder = sortOrder
|
||||
|
||||
await (prisma as any).tenantSymbol.updateMany({
|
||||
where: { id, tenantId },
|
||||
data,
|
||||
})
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error('Error updating tenant symbol:', error)
|
||||
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE: Remove a symbol from "my symbols"
|
||||
export async function DELETE(req: NextRequest) {
|
||||
try {
|
||||
const auth = await getTenantId()
|
||||
if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status })
|
||||
const { tenantId } = auth
|
||||
|
||||
const { id } = await req.json()
|
||||
if (!id) return NextResponse.json({ error: 'id erforderlich' }, { status: 400 })
|
||||
|
||||
await (prisma as any).tenantSymbol.deleteMany({
|
||||
where: { id, tenantId },
|
||||
})
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
console.error('Error deleting tenant symbol:', error)
|
||||
return NextResponse.json({ error: 'Interner Fehler' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user