import { NextRequest, NextResponse } from 'next/server' import { prisma } from '@/lib/db' import { getSession } from '@/lib/auth' async function ensureTables() { await prisma.$executeRawUnsafe(` CREATE TABLE IF NOT EXISTS tenant_symbols ( id TEXT PRIMARY KEY DEFAULT gen_random_uuid(), "isActive" BOOLEAN NOT NULL DEFAULT true, "tenantId" TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, "iconId" TEXT REFERENCES icon_assets(id) ON DELETE SET NULL, "name" TEXT, "svgPath" TEXT, "isUploaded" BOOLEAN NOT NULL DEFAULT false, "categoryId" TEXT, "migratedFromIconId" TEXT, "customName" TEXT, "sortOrder" INTEGER, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP ) `) await prisma.$executeRawUnsafe(` CREATE TABLE IF NOT EXISTS tenant_categories ( id TEXT PRIMARY KEY DEFAULT gen_random_uuid(), "name" TEXT NOT NULL, "description" TEXT, "sortOrder" INTEGER NOT NULL DEFAULT 0, "isActive" BOOLEAN NOT NULL DEFAULT true, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "tenantId" TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE ) `) } export async function GET() { /* ─── 1. Global library (legacy IconAsset) ─── */ let categoriesWithUrls: any[] = [] try { const user = await getSession() const tenantId = user?.tenantId const categories = await prisma.$queryRawUnsafe( `SELECT * FROM icon_categories WHERE "tenantId" IS NULL OR "tenantId" = $1 ORDER BY "sortOrder" ASC`, tenantId || null ) as any[] let hiddenIconIds: string[] = [] if (tenantId) { const tenant = await prisma.$queryRawUnsafe( `SELECT "hiddenIconIds" FROM tenants WHERE id = $1 LIMIT 1`, tenantId ) as any[] hiddenIconIds = tenant[0]?.hiddenIconIds || [] } for (const cat of categories) { const icons = await prisma.$queryRawUnsafe( `SELECT * FROM icon_assets WHERE "categoryId" = $1 AND "isActive" = true AND ("tenantId" IS NULL OR "tenantId" = $2) ORDER BY name ASC`, cat.id, tenantId || null ) as any[] const filteredIcons = icons .filter((icon: any) => !hiddenIconIds.includes(icon.id)) .map((icon: any) => ({ ...icon, url: `/api/icons/${icon.id}/image`, })) if (filteredIcons.length > 0) { categoriesWithUrls.push({ ...cat, icons: filteredIcons, }) } } } catch (err) { console.error('Error fetching legacy icon categories:', err) categoriesWithUrls = [] } /* ─── 2. Tenant symbols (Phase 1 architecture) ─── */ let tenantSymbolGroups: any[] = [] let flatTenantSymbols: any[] = [] try { const user = await getSession() const tenantId = user?.tenantId if (tenantId) { await ensureTables() const tenantSymbols = await prisma.$queryRawUnsafe( `SELECT ts.*, tc.id as "catId", tc.name as "catName" FROM tenant_symbols ts LEFT JOIN tenant_categories tc ON ts."categoryId" = tc.id WHERE ts."tenantId" = $1 AND ts."isActive" = true ORDER BY COALESCE(tc."sortOrder", 9999) ASC, COALESCE(ts."sortOrder", 9999) ASC`, tenantId ) as any[] flatTenantSymbols = tenantSymbols.map((ts: any) => ({ id: ts.id, name: ts.customName || ts.name, customName: ts.customName, categoryId: ts.categoryId, categoryName: ts.catName || null, isUploaded: ts.isUploaded, migratedFromIconId: ts.migratedFromIconId, imageUrl: ts.isUploaded ? `/api/tenant/symbols/${ts.id}/image` : ts.migratedFromIconId ? `/api/icons/${ts.migratedFromIconId}/image` : `/api/icons/${ts.id}/image`, })) const groups = new Map() for (const sym of flatTenantSymbols) { const key = sym.categoryId || '__none__' if (!groups.has(key)) groups.set(key, []) groups.get(key)!.push(sym) } const catIds = Array.from(groups.keys()).filter(k => k !== '__none__') as string[] let tenantCategories: any[] = [] if (catIds.length > 0) { tenantCategories = await prisma.$queryRawUnsafe( `SELECT * FROM tenant_categories WHERE id = ANY($1) AND "tenantId" = $2 ORDER BY "sortOrder" ASC`, catIds, tenantId ) as any[] } const catMap = new Map(tenantCategories.map((c: any) => [c.id, c])) tenantSymbolGroups = Array.from(groups.entries()).map(([catId, symbols]) => { const cat = catId !== '__none__' ? catMap.get(catId) : null return { category: cat ? { id: cat.id, name: cat.name } : null, symbols, } }) } } catch (err) { console.error('Error fetching tenant symbols:', err) tenantSymbolGroups = [] flatTenantSymbols = [] } /* ─── 3. Legacy mySymbols (keep for old clients during transition) ─── */ let mySymbolsLegacy: any[] = [] try { const user = await getSession() const tenantId = user?.tenantId if (tenantId) { const legacy = await prisma.$queryRawUnsafe( `SELECT ts.*, ia.id as "iconId", ia.name as "iconName", ia."mimeType", ia."iconType" FROM tenant_symbols ts JOIN icon_assets ia ON ts."iconId" = ia.id WHERE ts."tenantId" = $1 AND ts."iconId" IS NOT NULL ORDER BY COALESCE(ts."sortOrder", 9999) ASC`, tenantId ) as any[] mySymbolsLegacy = legacy.map((ts: any) => ({ id: ts.iconId, tenantSymbolId: ts.id, name: ts.customName || ts.iconName, customName: ts.customName, mimeType: ts.mimeType, iconType: ts.iconType, url: `/api/icons/${ts.iconId}/image`, })) } } catch (err) { console.error('Error fetching legacy mySymbols:', err) mySymbolsLegacy = [] } return NextResponse.json({ categories: categoriesWithUrls, mySymbols: mySymbolsLegacy, tenantSymbols: flatTenantSymbols, tenantSymbolGroups, }) }