@open-mercato/core 0.6.6-develop.5637.1.7a68607cc6 → 0.6.6-develop.5651.1.c43359070c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/directory/api/organizations/route.js +3 -0
- package/dist/modules/directory/api/organizations/route.js.map +3 -3
- package/dist/modules/entities/api/entities.js +21 -1
- package/dist/modules/entities/api/entities.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/page.js +3 -1
- package/dist/modules/entities/backend/entities/user/[entityId]/page.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/directory/api/organizations/route.ts +3 -0
- package/src/modules/entities/api/entities.ts +21 -0
- package/src/modules/entities/backend/entities/user/[entityId]/page.tsx +4 -1
|
@@ -298,9 +298,11 @@ async function GET(req) {
|
|
|
298
298
|
byTenant.get(tid).push(org);
|
|
299
299
|
}
|
|
300
300
|
const slugByOrgId2 = /* @__PURE__ */ new Map();
|
|
301
|
+
const updatedAtByOrgId2 = /* @__PURE__ */ new Map();
|
|
301
302
|
const logoUrlByOrgId2 = /* @__PURE__ */ new Map();
|
|
302
303
|
for (const org of allOrgs) {
|
|
303
304
|
slugByOrgId2.set(String(org.id), org.slug ?? null);
|
|
305
|
+
updatedAtByOrgId2.set(String(org.id), org.updatedAt instanceof Date ? org.updatedAt.toISOString() : null);
|
|
304
306
|
logoUrlByOrgId2.set(String(org.id), org.logoUrl ?? null);
|
|
305
307
|
}
|
|
306
308
|
const tenantIds = Array.from(byTenant.keys());
|
|
@@ -375,6 +377,7 @@ async function GET(req) {
|
|
|
375
377
|
id: node.id,
|
|
376
378
|
name: node.name,
|
|
377
379
|
slug: slugByOrgId2.get(recordId) ?? null,
|
|
380
|
+
updatedAt: updatedAtByOrgId2.get(recordId) ?? null,
|
|
378
381
|
logoUrl: logoUrlByOrgId2.get(recordId) ?? null,
|
|
379
382
|
tenantId: tid,
|
|
380
383
|
tenantName: tenantNameMap[tid] ?? tid,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/directory/api/organizations/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { logCrudAccess, makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { Organization, Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { organizationCreateSchema, organizationUpdateSchema } from '@open-mercato/core/modules/directory/data/validators'\nimport {\n computeHierarchyForOrganizations,\n type ComputedHierarchy,\n type ComputedOrganizationNode,\n} from '@open-mercato/core/modules/directory/lib/hierarchy'\nimport { isAllOrganizationsSelection } from '@open-mercato/core/modules/directory/constants'\nimport {\n getSelectedOrganizationFromRequest,\n resolveOrganizationScopeForRequest,\n} from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { E } from '#generated/entities.ids.generated'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport { organizationCrudEvents, organizationCrudIndexer } from '@open-mercato/core/modules/directory/commands/organizations'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport {\n directoryTag,\n directoryErrorSchema,\n directoryOkSchema,\n organizationListResponseSchema,\n} from '../openapi'\nimport { resolveIsSuperAdmin, enforceTenantSelection } from '@open-mercato/core/modules/auth/lib/tenantAccess'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\ntype CrudInput = Record<string, unknown>\nconst rawBodySchema = z.object({}).passthrough()\n\ntype TreeNode = {\n id: string\n name: string\n parentId: string | null\n tenantId: string | null\n depth: number\n ancestorIds: string[]\n childIds: string[]\n descendantIds: string[]\n isActive: boolean\n treePath: string | null\n pathLabel: string\n children: TreeNode[]\n}\n\nconst viewSchema = z.object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(200).default(50),\n search: z.string().optional(),\n view: z.enum(['options', 'manage', 'tree']).default('options'),\n ids: z.string().optional(),\n tenantId: z.string().uuid().optional(),\n includeInactive: z.enum(['true', 'false']).optional(),\n status: z.enum(['all', 'active', 'inactive']).optional(),\n})\n\nfunction parseIds(raw: string | null): string[] | null {\n if (!raw) return null\n const ids = raw.split(',').map((s) => s.trim()).filter(Boolean)\n return ids.length ? Array.from(new Set(ids)) : null\n}\n\nfunction stringId(value: unknown): string {\n return String(value)\n}\n\nfunction enforceTenantScope(\n authTenantId: string | null,\n requestedTenantId: string | null,\n isSuperAdmin: boolean,\n): string | null {\n if (isSuperAdmin) {\n return requestedTenantId || authTenantId || null\n }\n if (authTenantId && requestedTenantId && requestedTenantId !== authTenantId) {\n return null\n }\n return requestedTenantId || authTenantId\n}\n\nconst crud = makeCrudRoute<CrudInput, CrudInput, Record<string, unknown>>({\n metadata: {\n GET: { requireAuth: true, requireFeatures: ['directory.organizations.view'] },\n POST: { requireAuth: true, requireFeatures: ['directory.organizations.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['directory.organizations.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['directory.organizations.manage'] },\n },\n orm: {\n entity: Organization,\n idField: 'id',\n orgField: null,\n tenantField: null,\n softDeleteField: 'deletedAt',\n },\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n actions: {\n create: {\n commandId: 'directory.organizations.create',\n schema: rawBodySchema,\n mapInput: ({ parsed }) => parsed,\n response: ({ result }) => ({ id: String(result.id) }),\n status: 201,\n },\n update: {\n commandId: 'directory.organizations.update',\n schema: rawBodySchema,\n mapInput: ({ parsed }) => parsed,\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'directory.organizations.delete',\n response: () => ({ ok: true }),\n },\n },\n})\n\nexport const metadata = crud.metadata\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ items: [] }, { status: 401 })\n\n const url = new URL(req.url)\n const parsed = viewSchema.safeParse({\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n search: url.searchParams.get('search') ?? undefined,\n view: url.searchParams.get('view') ?? undefined,\n ids: url.searchParams.get('ids') ?? undefined,\n tenantId: url.searchParams.get('tenantId') ?? undefined,\n includeInactive: url.searchParams.get('includeInactive') ?? undefined,\n status: url.searchParams.get('status') ?? undefined,\n })\n if (!parsed.success) return NextResponse.json({ items: [] }, { status: 400 })\n\n const query = parsed.data\n const ids = parseIds(query.ids ?? null)\n const requestedTenantRaw = query.tenantId ?? null\n const normalizedRequestedTenantId = requestedTenantRaw && requestedTenantRaw.toLowerCase() === 'all' ? null : requestedTenantRaw\n const authTenantId = auth.tenantId ?? null\n const container = await createRequestContainer()\n const isSuperAdmin = await resolveIsSuperAdmin({ auth, container })\n const em = (container.resolve('em') as EntityManager)\n const allowAllTenants = isSuperAdmin && !normalizedRequestedTenantId && query.view === 'manage'\n let tenantId = allowAllTenants ? null : enforceTenantScope(authTenantId, normalizedRequestedTenantId, isSuperAdmin)\n const status = query.status ?? 'all'\n const includeInactive = parseBooleanToken(query.includeInactive) === true || status !== 'active'\n\n if (!allowAllTenants && !tenantId && !authTenantId && ids?.length) {\n let scopedIds = ids\n if (!isSuperAdmin) {\n let allowedIds: string[] | null = null\n try {\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n allowedIds = Array.isArray(scope.allowedIds) ? scope.allowedIds : null\n } catch {}\n const allowedSet = allowedIds ? new Set(allowedIds) : new Set<string>()\n scopedIds = ids.filter((id) => allowedSet.has(id))\n }\n if (scopedIds.length) {\n const scopedOrgs: Organization[] = await findWithDecryption(\n em,\n Organization,\n { id: { $in: scopedIds }, deletedAt: null },\n { populate: ['tenant'] },\n { tenantId: authTenantId, organizationId: null },\n )\n const tenantCandidates = new Set<string>()\n for (const org of scopedOrgs) {\n const orgTenantId = org.tenant?.id ? stringId(org.tenant.id) : ''\n if (orgTenantId) tenantCandidates.add(orgTenantId)\n }\n if (tenantCandidates.size === 1) {\n const candidateTenantId = Array.from(tenantCandidates)[0] ?? null\n try {\n tenantId = await enforceTenantSelection({ auth, container }, candidateTenantId)\n } catch (error) {\n if (isCrudHttpError(error)) {\n return NextResponse.json(error.body, { status: error.status })\n }\n throw error\n }\n } else if (tenantCandidates.size > 1) {\n return NextResponse.json({ items: [], error: 'Tenant scope required' }, { status: 400 })\n }\n }\n }\n\n if (!allowAllTenants && !tenantId) {\n const candidateOrgIds = new Set<string>()\n const cookieOrgId = getSelectedOrganizationFromRequest(req)\n const effectiveCookieOrgId = cookieOrgId && !isAllOrganizationsSelection(cookieOrgId) ? cookieOrgId : null\n if (effectiveCookieOrgId) candidateOrgIds.add(effectiveCookieOrgId)\n if (auth.orgId) candidateOrgIds.add(auth.orgId)\n\n try {\n const scope = await resolveOrganizationScopeForRequest({\n container,\n auth,\n request: req,\n selectedId: effectiveCookieOrgId ?? undefined,\n })\n if (scope.selectedId) candidateOrgIds.add(scope.selectedId)\n if (Array.isArray(scope.filterIds) && scope.filterIds.length) {\n candidateOrgIds.add(scope.filterIds[0]!)\n }\n if (Array.isArray(scope.allowedIds) && scope.allowedIds.length) {\n candidateOrgIds.add(scope.allowedIds[0]!)\n }\n } catch {}\n\n for (const orgId of candidateOrgIds) {\n if (!orgId) continue\n const org = await findOneWithDecryption(\n em,\n Organization,\n { id: orgId, deletedAt: null },\n { populate: ['tenant'] },\n { tenantId: authTenantId, organizationId: orgId },\n )\n if (org?.tenant && org.tenant.id) {\n tenantId = stringId(org.tenant.id)\n break\n }\n }\n }\n\n if (!allowAllTenants && !tenantId) {\n return NextResponse.json({ items: [], error: 'Tenant scope required' }, { status: 400 })\n }\n\n if (query.view === 'options') {\n if (!tenantId) {\n return NextResponse.json({ items: [], error: 'Tenant scope required' }, { status: 400 })\n }\n const where: FilterQuery<Organization> = { tenant: tenantId, deletedAt: null }\n if (status === 'active') where.isActive = true\n if (status === 'inactive') where.isActive = false\n if (status === 'all' && !includeInactive) where.isActive = true\n if (ids) where.id = { $in: ids }\n const orgs = await em.find(Organization, where, { orderBy: { name: 'ASC' } })\n const items = orgs.map((org) => ({\n id: stringId(org.id),\n name: org.name,\n logoUrl: org.logoUrl ?? null,\n parentId: org.parentId ?? null,\n tenantId: tenantId,\n isActive: !!org.isActive,\n depth: org.depth ?? 0,\n treePath: org.treePath ?? stringId(org.id),\n }))\n await logCrudAccess({\n container,\n auth,\n request: req,\n items,\n idField: 'id',\n resourceKind: 'directory.organization',\n organizationId: null,\n tenantId,\n query,\n accessType: ids && ids.length === 1 ? 'read:item' : undefined,\n })\n return NextResponse.json({ items })\n }\n\n if (query.view === 'tree') {\n if (!tenantId) {\n return NextResponse.json({ items: [], error: 'Tenant scope required' }, { status: 400 })\n }\n const orgListFilter: FilterQuery<Organization> = { tenant: tenantId, deletedAt: null }\n const orgs = await em.find(Organization, orgListFilter, { orderBy: { name: 'ASC' } })\n const hierarchy = computeHierarchyForOrganizations(orgs, tenantId)\n const nodeMap = new Map<string, { node: ComputedOrganizationNode; children: TreeNode[] }>()\n const roots: TreeNode[] = []\n for (const node of hierarchy.ordered) {\n const treeNode: TreeNode = {\n id: node.id,\n name: node.name,\n parentId: node.parentId,\n tenantId: node.tenantId,\n depth: node.depth,\n ancestorIds: node.ancestorIds,\n childIds: node.childIds,\n descendantIds: node.descendantIds,\n isActive: node.isActive,\n treePath: node.treePath,\n pathLabel: node.pathLabel,\n children: [],\n }\n nodeMap.set(node.id, { node, children: treeNode.children })\n if (node.parentId && nodeMap.has(node.parentId)) {\n const parentEntry = nodeMap.get(node.parentId)!\n parentEntry.children.push(treeNode)\n } else {\n roots.push(treeNode)\n }\n }\n await logCrudAccess({\n container,\n auth,\n request: req,\n items: roots,\n idField: 'id',\n resourceKind: 'directory.organization',\n organizationId: null,\n tenantId,\n query,\n })\n return NextResponse.json({ items: roots })\n }\n\n if (query.view !== 'manage') {\n return NextResponse.json({ items: [] }, { status: 400 })\n }\n\n if (allowAllTenants) {\n // Multi-tenant aggregate view for super administrators\n const search = (query.search || '').trim().toLowerCase()\n const allOrgs = await findWithDecryption(\n em,\n Organization,\n { deletedAt: null },\n { orderBy: { name: 'ASC' }, populate: ['tenant'] },\n { tenantId: null, organizationId: null },\n )\n const byTenant = new Map<string, Organization[]>()\n for (const org of allOrgs) {\n const tenantEntity = org.tenant\n const tid = tenantEntity ? stringId(tenantEntity.id) : null\n if (!tid) continue\n if (!byTenant.has(tid)) byTenant.set(tid, [])\n byTenant.get(tid)!.push(org)\n }\n\n const slugByOrgId = new Map<string, string | null>()\n const logoUrlByOrgId = new Map<string, string | null>()\n for (const org of allOrgs) {\n slugByOrgId.set(String(org.id), org.slug ?? null)\n logoUrlByOrgId.set(String(org.id), org.logoUrl ?? null)\n }\n\n const tenantIds = Array.from(byTenant.keys())\n const tenants = tenantIds.length\n ? await em.find(Tenant, { id: { $in: tenantIds as unknown as string[] } })\n : []\n const tenantNameMap = tenants.reduce<Record<string, string>>((acc, tenant) => {\n const tid = stringId(tenant.id)\n const name = typeof tenant.name === 'string' && tenant.name.length > 0 ? tenant.name : tid\n acc[tid] = name\n return acc\n }, {})\n\n const hierarchies = new Map<string, ComputedHierarchy>()\n const orderedNodes: Array<{ tenantId: string; node: ComputedOrganizationNode }> = []\n for (const [tid, orgs] of byTenant.entries()) {\n const hierarchy = computeHierarchyForOrganizations(orgs, tid)\n hierarchies.set(tid, hierarchy)\n for (const node of hierarchy.ordered) {\n orderedNodes.push({ tenantId: tid, node })\n }\n }\n\n let rows = orderedNodes\n if (query.status === 'active') {\n rows = rows.filter((entry) => entry.node.isActive)\n } else if (query.status === 'inactive') {\n rows = rows.filter((entry) => !entry.node.isActive)\n }\n\n if (search) {\n rows = rows.filter(({ node }) => {\n const pathLabel = (node.pathLabel || '').toLowerCase()\n return node.name.toLowerCase().includes(search) || pathLabel.includes(search)\n })\n }\n if (ids) {\n const idSet = new Set(ids)\n rows = rows.filter(({ node }) => idSet.has(node.id))\n }\n\n rows.sort((a, b) => {\n const nameA = tenantNameMap[a.tenantId] ?? a.tenantId\n const nameB = tenantNameMap[b.tenantId] ?? b.tenantId\n const tenantCompare = nameA.localeCompare(nameB)\n if (tenantCompare !== 0) return tenantCompare\n return a.node.pathLabel.localeCompare(b.node.pathLabel)\n })\n\n const total = rows.length\n const pageSize = query.pageSize\n const page = query.page\n const start = (page - 1) * pageSize\n const paged = rows.slice(start, start + pageSize)\n const recordIds: string[] = []\n const tenantIdByRecord: Record<string, string | null> = {}\n const organizationIdByRecord: Record<string, string | null> = {}\n for (const entry of paged) {\n const recordId = String(entry.node.id)\n recordIds.push(recordId)\n tenantIdByRecord[recordId] = entry.tenantId\n organizationIdByRecord[recordId] = recordId\n }\n const tenantFallbacks = Array.from(new Set(recordIds.map((id) => tenantIdByRecord[id]).filter((value): value is string => typeof value === 'string' && value.length > 0)))\n const cfByOrg = recordIds.length\n ? await loadCustomFieldValues({\n em,\n entityId: E.directory.organization,\n recordIds,\n tenantIdByRecord,\n organizationIdByRecord,\n tenantFallbacks,\n })\n : {}\n const items = paged.map(({ tenantId: tid, node }) => {\n const hierarchy = hierarchies.get(tid)\n const parentName = node.parentId && hierarchy ? hierarchy.map.get(node.parentId)?.name ?? null : null\n const pathLabel = node.pathLabel || node.name\n const recordId = String(node.id)\n return {\n id: node.id,\n name: node.name,\n slug: slugByOrgId.get(recordId) ?? null,\n logoUrl: logoUrlByOrgId.get(recordId) ?? null,\n tenantId: tid,\n tenantName: tenantNameMap[tid] ?? tid,\n parentId: node.parentId,\n parentName,\n depth: node.depth,\n rootId: node.rootId,\n treePath: node.treePath,\n pathLabel,\n ancestorIds: node.ancestorIds,\n childIds: node.childIds,\n descendantIds: node.descendantIds,\n childrenCount: node.childIds.length,\n descendantsCount: node.descendantIds.length,\n isActive: node.isActive,\n ...(cfByOrg[recordId] ?? {}),\n }\n })\n const totalPages = Math.max(1, Math.ceil(total / pageSize))\n await logCrudAccess({\n container,\n auth,\n request: req,\n items,\n idField: 'id',\n resourceKind: 'directory.organization',\n organizationId: null,\n tenantId: null,\n query,\n accessType: ids && ids.length === 1 ? 'read:item' : undefined,\n })\n return NextResponse.json({ items, total, page, pageSize, totalPages, isSuperAdmin })\n }\n\n if (!tenantId) {\n return NextResponse.json({ items: [], error: 'Tenant scope required' }, { status: 400 })\n }\n\n const orgListFilter: FilterQuery<Organization> = { tenant: tenantId, deletedAt: null }\n const orgs = await em.find(Organization, orgListFilter, { orderBy: { name: 'ASC' } })\n const hierarchy = computeHierarchyForOrganizations(orgs, tenantId)\n const slugByOrgId = new Map<string, string | null>()\n const logoUrlByOrgId = new Map<string, string | null>()\n const updatedAtByOrgId = new Map<string, string | null>()\n for (const org of orgs) {\n slugByOrgId.set(String(org.id), org.slug ?? null)\n logoUrlByOrgId.set(String(org.id), org.logoUrl ?? null)\n updatedAtByOrgId.set(String(org.id), org.updatedAt instanceof Date ? org.updatedAt.toISOString() : null)\n }\n\n // Manage view: paginated flat list for a single tenant\n const search = (query.search || '').trim().toLowerCase()\n let rows = hierarchy.ordered\n if (status === 'active') {\n rows = rows.filter((node) => node.isActive)\n } else if (status === 'inactive') {\n rows = rows.filter((node) => !node.isActive)\n }\n\n if (search) {\n rows = rows.filter((node) => {\n const pathLabel = (node.pathLabel || '').toLowerCase()\n return node.name.toLowerCase().includes(search) || pathLabel.includes(search)\n })\n }\n if (ids) {\n const idSet = new Set(ids)\n rows = rows.filter((node) => idSet.has(node.id))\n }\n\n const total = rows.length\n const pageSize = query.pageSize\n const page = query.page\n const start = (page - 1) * pageSize\n const paged = rows.slice(start, start + pageSize)\n const recordIds: string[] = []\n const tenantIdByRecord: Record<string, string | null> = {}\n const organizationIdByRecord: Record<string, string | null> = {}\n for (const node of paged) {\n const recordId = String(node.id)\n recordIds.push(recordId)\n tenantIdByRecord[recordId] = node.tenantId ? String(node.tenantId) : null\n organizationIdByRecord[recordId] = recordId\n }\n const cfByOrg = recordIds.length\n ? await loadCustomFieldValues({\n em,\n entityId: E.directory.organization,\n recordIds,\n tenantIdByRecord,\n organizationIdByRecord,\n tenantFallbacks: tenantId ? [tenantId] : [],\n })\n : {}\n let tenantName: string | null = null\n if (isSuperAdmin && tenantId) {\n const tenant = await em.findOne(Tenant, { id: tenantId })\n if (tenant) {\n const display = typeof tenant.name === 'string' && tenant.name.length > 0 ? tenant.name : stringId(tenant.id)\n tenantName = display\n }\n }\n const items = paged.map((node) => {\n const parentName = node.parentId ? hierarchy.map.get(node.parentId)?.name ?? null : null\n const pathLabel = node.pathLabel || node.name\n const recordId = String(node.id)\n return {\n id: node.id,\n name: node.name,\n slug: slugByOrgId.get(recordId) ?? null,\n logoUrl: logoUrlByOrgId.get(recordId) ?? null,\n updatedAt: updatedAtByOrgId.get(recordId) ?? null,\n tenantId: node.tenantId,\n tenantName,\n parentId: node.parentId,\n parentName,\n depth: node.depth,\n rootId: node.rootId,\n treePath: node.treePath,\n pathLabel,\n ancestorIds: node.ancestorIds,\n childIds: node.childIds,\n descendantIds: node.descendantIds,\n childrenCount: node.childIds.length,\n descendantsCount: node.descendantIds.length,\n isActive: node.isActive,\n ...(cfByOrg[recordId] ?? {}),\n }\n })\n const totalPages = Math.max(1, Math.ceil(total / pageSize))\n await logCrudAccess({\n container,\n auth,\n request: req,\n items,\n idField: 'id',\n resourceKind: 'directory.organization',\n organizationId: null,\n tenantId,\n query,\n accessType: ids && ids.length === 1 ? 'read:item' : undefined,\n })\n return NextResponse.json({ items, total, page, pageSize, totalPages, isSuperAdmin })\n}\n\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n\nconst organizationCreateResponseSchema = z.object({\n id: z.string().uuid(),\n})\n\nconst organizationDeleteRequestSchema = z.object({\n id: z.string().uuid(),\n})\n\nconst organizationsGetDoc: OpenApiMethodDoc = {\n summary: 'List organizations',\n description: 'Returns organizations using options, tree, or paginated manage view depending on the `view` parameter.',\n tags: [directoryTag],\n query: viewSchema,\n responses: [\n { status: 200, description: 'Organization data for the requested view.', schema: organizationListResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid query or tenant scope', schema: directoryErrorSchema },\n { status: 401, description: 'Authentication required', schema: directoryErrorSchema },\n ],\n}\n\nconst organizationsPostDoc: OpenApiMethodDoc = {\n summary: 'Create organization',\n description: 'Creates a new organization within a tenant and optionally assigns hierarchy relationships.',\n tags: [directoryTag],\n requestBody: {\n contentType: 'application/json',\n schema: organizationCreateSchema,\n description: 'Organization attributes and optional hierarchy configuration.',\n },\n responses: [\n { status: 201, description: 'Organization created.', schema: organizationCreateResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: directoryErrorSchema },\n { status: 401, description: 'Authentication required', schema: directoryErrorSchema },\n { status: 403, description: 'Missing directory.organizations.manage feature', schema: directoryErrorSchema },\n ],\n}\n\nconst organizationsPutDoc: OpenApiMethodDoc = {\n summary: 'Update organization',\n description: 'Updates organization details and hierarchy assignments.',\n tags: [directoryTag],\n requestBody: {\n contentType: 'application/json',\n schema: organizationUpdateSchema,\n description: 'Organization identifier followed by fields to update.',\n },\n responses: [\n { status: 200, description: 'Organization updated.', schema: directoryOkSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: directoryErrorSchema },\n { status: 401, description: 'Authentication required', schema: directoryErrorSchema },\n { status: 403, description: 'Missing directory.organizations.manage feature', schema: directoryErrorSchema },\n ],\n}\n\nconst organizationsDeleteDoc: OpenApiMethodDoc = {\n summary: 'Delete organization',\n description: 'Soft deletes an organization identified by id.',\n tags: [directoryTag],\n requestBody: {\n contentType: 'application/json',\n schema: organizationDeleteRequestSchema,\n description: 'Identifier of the organization to delete.',\n },\n responses: [\n { status: 200, description: 'Organization deleted.', schema: directoryOkSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: directoryErrorSchema },\n { status: 401, description: 'Authentication required', schema: directoryErrorSchema },\n { status: 403, description: 'Missing directory.organizations.manage feature', schema: directoryErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: directoryTag,\n summary: 'Manage organizations',\n methods: {\n GET: organizationsGetDoc,\n POST: organizationsPostDoc,\n PUT: organizationsPutDoc,\n DELETE: organizationsDeleteDoc,\n },\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["orgs", "items", "orgListFilter", "hierarchy", "search", "slugByOrgId", "logoUrlByOrgId", "rows", "total", "pageSize", "page", "start", "paged", "recordIds", "tenantIdByRecord", "organizationIdByRecord", "cfByOrg", "totalPages"]
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { logCrudAccess, makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { Organization, Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { organizationCreateSchema, organizationUpdateSchema } from '@open-mercato/core/modules/directory/data/validators'\nimport {\n computeHierarchyForOrganizations,\n type ComputedHierarchy,\n type ComputedOrganizationNode,\n} from '@open-mercato/core/modules/directory/lib/hierarchy'\nimport { isAllOrganizationsSelection } from '@open-mercato/core/modules/directory/constants'\nimport {\n getSelectedOrganizationFromRequest,\n resolveOrganizationScopeForRequest,\n} from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { E } from '#generated/entities.ids.generated'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport { organizationCrudEvents, organizationCrudIndexer } from '@open-mercato/core/modules/directory/commands/organizations'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport {\n directoryTag,\n directoryErrorSchema,\n directoryOkSchema,\n organizationListResponseSchema,\n} from '../openapi'\nimport { resolveIsSuperAdmin, enforceTenantSelection } from '@open-mercato/core/modules/auth/lib/tenantAccess'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\ntype CrudInput = Record<string, unknown>\nconst rawBodySchema = z.object({}).passthrough()\n\ntype TreeNode = {\n id: string\n name: string\n parentId: string | null\n tenantId: string | null\n depth: number\n ancestorIds: string[]\n childIds: string[]\n descendantIds: string[]\n isActive: boolean\n treePath: string | null\n pathLabel: string\n children: TreeNode[]\n}\n\nconst viewSchema = z.object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(200).default(50),\n search: z.string().optional(),\n view: z.enum(['options', 'manage', 'tree']).default('options'),\n ids: z.string().optional(),\n tenantId: z.string().uuid().optional(),\n includeInactive: z.enum(['true', 'false']).optional(),\n status: z.enum(['all', 'active', 'inactive']).optional(),\n})\n\nfunction parseIds(raw: string | null): string[] | null {\n if (!raw) return null\n const ids = raw.split(',').map((s) => s.trim()).filter(Boolean)\n return ids.length ? Array.from(new Set(ids)) : null\n}\n\nfunction stringId(value: unknown): string {\n return String(value)\n}\n\nfunction enforceTenantScope(\n authTenantId: string | null,\n requestedTenantId: string | null,\n isSuperAdmin: boolean,\n): string | null {\n if (isSuperAdmin) {\n return requestedTenantId || authTenantId || null\n }\n if (authTenantId && requestedTenantId && requestedTenantId !== authTenantId) {\n return null\n }\n return requestedTenantId || authTenantId\n}\n\nconst crud = makeCrudRoute<CrudInput, CrudInput, Record<string, unknown>>({\n metadata: {\n GET: { requireAuth: true, requireFeatures: ['directory.organizations.view'] },\n POST: { requireAuth: true, requireFeatures: ['directory.organizations.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['directory.organizations.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['directory.organizations.manage'] },\n },\n orm: {\n entity: Organization,\n idField: 'id',\n orgField: null,\n tenantField: null,\n softDeleteField: 'deletedAt',\n },\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n actions: {\n create: {\n commandId: 'directory.organizations.create',\n schema: rawBodySchema,\n mapInput: ({ parsed }) => parsed,\n response: ({ result }) => ({ id: String(result.id) }),\n status: 201,\n },\n update: {\n commandId: 'directory.organizations.update',\n schema: rawBodySchema,\n mapInput: ({ parsed }) => parsed,\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'directory.organizations.delete',\n response: () => ({ ok: true }),\n },\n },\n})\n\nexport const metadata = crud.metadata\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ items: [] }, { status: 401 })\n\n const url = new URL(req.url)\n const parsed = viewSchema.safeParse({\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n search: url.searchParams.get('search') ?? undefined,\n view: url.searchParams.get('view') ?? undefined,\n ids: url.searchParams.get('ids') ?? undefined,\n tenantId: url.searchParams.get('tenantId') ?? undefined,\n includeInactive: url.searchParams.get('includeInactive') ?? undefined,\n status: url.searchParams.get('status') ?? undefined,\n })\n if (!parsed.success) return NextResponse.json({ items: [] }, { status: 400 })\n\n const query = parsed.data\n const ids = parseIds(query.ids ?? null)\n const requestedTenantRaw = query.tenantId ?? null\n const normalizedRequestedTenantId = requestedTenantRaw && requestedTenantRaw.toLowerCase() === 'all' ? null : requestedTenantRaw\n const authTenantId = auth.tenantId ?? null\n const container = await createRequestContainer()\n const isSuperAdmin = await resolveIsSuperAdmin({ auth, container })\n const em = (container.resolve('em') as EntityManager)\n const allowAllTenants = isSuperAdmin && !normalizedRequestedTenantId && query.view === 'manage'\n let tenantId = allowAllTenants ? null : enforceTenantScope(authTenantId, normalizedRequestedTenantId, isSuperAdmin)\n const status = query.status ?? 'all'\n const includeInactive = parseBooleanToken(query.includeInactive) === true || status !== 'active'\n\n if (!allowAllTenants && !tenantId && !authTenantId && ids?.length) {\n let scopedIds = ids\n if (!isSuperAdmin) {\n let allowedIds: string[] | null = null\n try {\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n allowedIds = Array.isArray(scope.allowedIds) ? scope.allowedIds : null\n } catch {}\n const allowedSet = allowedIds ? new Set(allowedIds) : new Set<string>()\n scopedIds = ids.filter((id) => allowedSet.has(id))\n }\n if (scopedIds.length) {\n const scopedOrgs: Organization[] = await findWithDecryption(\n em,\n Organization,\n { id: { $in: scopedIds }, deletedAt: null },\n { populate: ['tenant'] },\n { tenantId: authTenantId, organizationId: null },\n )\n const tenantCandidates = new Set<string>()\n for (const org of scopedOrgs) {\n const orgTenantId = org.tenant?.id ? stringId(org.tenant.id) : ''\n if (orgTenantId) tenantCandidates.add(orgTenantId)\n }\n if (tenantCandidates.size === 1) {\n const candidateTenantId = Array.from(tenantCandidates)[0] ?? null\n try {\n tenantId = await enforceTenantSelection({ auth, container }, candidateTenantId)\n } catch (error) {\n if (isCrudHttpError(error)) {\n return NextResponse.json(error.body, { status: error.status })\n }\n throw error\n }\n } else if (tenantCandidates.size > 1) {\n return NextResponse.json({ items: [], error: 'Tenant scope required' }, { status: 400 })\n }\n }\n }\n\n if (!allowAllTenants && !tenantId) {\n const candidateOrgIds = new Set<string>()\n const cookieOrgId = getSelectedOrganizationFromRequest(req)\n const effectiveCookieOrgId = cookieOrgId && !isAllOrganizationsSelection(cookieOrgId) ? cookieOrgId : null\n if (effectiveCookieOrgId) candidateOrgIds.add(effectiveCookieOrgId)\n if (auth.orgId) candidateOrgIds.add(auth.orgId)\n\n try {\n const scope = await resolveOrganizationScopeForRequest({\n container,\n auth,\n request: req,\n selectedId: effectiveCookieOrgId ?? undefined,\n })\n if (scope.selectedId) candidateOrgIds.add(scope.selectedId)\n if (Array.isArray(scope.filterIds) && scope.filterIds.length) {\n candidateOrgIds.add(scope.filterIds[0]!)\n }\n if (Array.isArray(scope.allowedIds) && scope.allowedIds.length) {\n candidateOrgIds.add(scope.allowedIds[0]!)\n }\n } catch {}\n\n for (const orgId of candidateOrgIds) {\n if (!orgId) continue\n const org = await findOneWithDecryption(\n em,\n Organization,\n { id: orgId, deletedAt: null },\n { populate: ['tenant'] },\n { tenantId: authTenantId, organizationId: orgId },\n )\n if (org?.tenant && org.tenant.id) {\n tenantId = stringId(org.tenant.id)\n break\n }\n }\n }\n\n if (!allowAllTenants && !tenantId) {\n return NextResponse.json({ items: [], error: 'Tenant scope required' }, { status: 400 })\n }\n\n if (query.view === 'options') {\n if (!tenantId) {\n return NextResponse.json({ items: [], error: 'Tenant scope required' }, { status: 400 })\n }\n const where: FilterQuery<Organization> = { tenant: tenantId, deletedAt: null }\n if (status === 'active') where.isActive = true\n if (status === 'inactive') where.isActive = false\n if (status === 'all' && !includeInactive) where.isActive = true\n if (ids) where.id = { $in: ids }\n const orgs = await em.find(Organization, where, { orderBy: { name: 'ASC' } })\n const items = orgs.map((org) => ({\n id: stringId(org.id),\n name: org.name,\n logoUrl: org.logoUrl ?? null,\n parentId: org.parentId ?? null,\n tenantId: tenantId,\n isActive: !!org.isActive,\n depth: org.depth ?? 0,\n treePath: org.treePath ?? stringId(org.id),\n }))\n await logCrudAccess({\n container,\n auth,\n request: req,\n items,\n idField: 'id',\n resourceKind: 'directory.organization',\n organizationId: null,\n tenantId,\n query,\n accessType: ids && ids.length === 1 ? 'read:item' : undefined,\n })\n return NextResponse.json({ items })\n }\n\n if (query.view === 'tree') {\n if (!tenantId) {\n return NextResponse.json({ items: [], error: 'Tenant scope required' }, { status: 400 })\n }\n const orgListFilter: FilterQuery<Organization> = { tenant: tenantId, deletedAt: null }\n const orgs = await em.find(Organization, orgListFilter, { orderBy: { name: 'ASC' } })\n const hierarchy = computeHierarchyForOrganizations(orgs, tenantId)\n const nodeMap = new Map<string, { node: ComputedOrganizationNode; children: TreeNode[] }>()\n const roots: TreeNode[] = []\n for (const node of hierarchy.ordered) {\n const treeNode: TreeNode = {\n id: node.id,\n name: node.name,\n parentId: node.parentId,\n tenantId: node.tenantId,\n depth: node.depth,\n ancestorIds: node.ancestorIds,\n childIds: node.childIds,\n descendantIds: node.descendantIds,\n isActive: node.isActive,\n treePath: node.treePath,\n pathLabel: node.pathLabel,\n children: [],\n }\n nodeMap.set(node.id, { node, children: treeNode.children })\n if (node.parentId && nodeMap.has(node.parentId)) {\n const parentEntry = nodeMap.get(node.parentId)!\n parentEntry.children.push(treeNode)\n } else {\n roots.push(treeNode)\n }\n }\n await logCrudAccess({\n container,\n auth,\n request: req,\n items: roots,\n idField: 'id',\n resourceKind: 'directory.organization',\n organizationId: null,\n tenantId,\n query,\n })\n return NextResponse.json({ items: roots })\n }\n\n if (query.view !== 'manage') {\n return NextResponse.json({ items: [] }, { status: 400 })\n }\n\n if (allowAllTenants) {\n // Multi-tenant aggregate view for super administrators\n const search = (query.search || '').trim().toLowerCase()\n const allOrgs = await findWithDecryption(\n em,\n Organization,\n { deletedAt: null },\n { orderBy: { name: 'ASC' }, populate: ['tenant'] },\n { tenantId: null, organizationId: null },\n )\n const byTenant = new Map<string, Organization[]>()\n for (const org of allOrgs) {\n const tenantEntity = org.tenant\n const tid = tenantEntity ? stringId(tenantEntity.id) : null\n if (!tid) continue\n if (!byTenant.has(tid)) byTenant.set(tid, [])\n byTenant.get(tid)!.push(org)\n }\n\n const slugByOrgId = new Map<string, string | null>()\n const updatedAtByOrgId = new Map<string, string | null>()\n const logoUrlByOrgId = new Map<string, string | null>()\n for (const org of allOrgs) {\n slugByOrgId.set(String(org.id), org.slug ?? null)\n updatedAtByOrgId.set(String(org.id), org.updatedAt instanceof Date ? org.updatedAt.toISOString() : null)\n logoUrlByOrgId.set(String(org.id), org.logoUrl ?? null)\n }\n\n const tenantIds = Array.from(byTenant.keys())\n const tenants = tenantIds.length\n ? await em.find(Tenant, { id: { $in: tenantIds as unknown as string[] } })\n : []\n const tenantNameMap = tenants.reduce<Record<string, string>>((acc, tenant) => {\n const tid = stringId(tenant.id)\n const name = typeof tenant.name === 'string' && tenant.name.length > 0 ? tenant.name : tid\n acc[tid] = name\n return acc\n }, {})\n\n const hierarchies = new Map<string, ComputedHierarchy>()\n const orderedNodes: Array<{ tenantId: string; node: ComputedOrganizationNode }> = []\n for (const [tid, orgs] of byTenant.entries()) {\n const hierarchy = computeHierarchyForOrganizations(orgs, tid)\n hierarchies.set(tid, hierarchy)\n for (const node of hierarchy.ordered) {\n orderedNodes.push({ tenantId: tid, node })\n }\n }\n\n let rows = orderedNodes\n if (query.status === 'active') {\n rows = rows.filter((entry) => entry.node.isActive)\n } else if (query.status === 'inactive') {\n rows = rows.filter((entry) => !entry.node.isActive)\n }\n\n if (search) {\n rows = rows.filter(({ node }) => {\n const pathLabel = (node.pathLabel || '').toLowerCase()\n return node.name.toLowerCase().includes(search) || pathLabel.includes(search)\n })\n }\n if (ids) {\n const idSet = new Set(ids)\n rows = rows.filter(({ node }) => idSet.has(node.id))\n }\n\n rows.sort((a, b) => {\n const nameA = tenantNameMap[a.tenantId] ?? a.tenantId\n const nameB = tenantNameMap[b.tenantId] ?? b.tenantId\n const tenantCompare = nameA.localeCompare(nameB)\n if (tenantCompare !== 0) return tenantCompare\n return a.node.pathLabel.localeCompare(b.node.pathLabel)\n })\n\n const total = rows.length\n const pageSize = query.pageSize\n const page = query.page\n const start = (page - 1) * pageSize\n const paged = rows.slice(start, start + pageSize)\n const recordIds: string[] = []\n const tenantIdByRecord: Record<string, string | null> = {}\n const organizationIdByRecord: Record<string, string | null> = {}\n for (const entry of paged) {\n const recordId = String(entry.node.id)\n recordIds.push(recordId)\n tenantIdByRecord[recordId] = entry.tenantId\n organizationIdByRecord[recordId] = recordId\n }\n const tenantFallbacks = Array.from(new Set(recordIds.map((id) => tenantIdByRecord[id]).filter((value): value is string => typeof value === 'string' && value.length > 0)))\n const cfByOrg = recordIds.length\n ? await loadCustomFieldValues({\n em,\n entityId: E.directory.organization,\n recordIds,\n tenantIdByRecord,\n organizationIdByRecord,\n tenantFallbacks,\n })\n : {}\n const items = paged.map(({ tenantId: tid, node }) => {\n const hierarchy = hierarchies.get(tid)\n const parentName = node.parentId && hierarchy ? hierarchy.map.get(node.parentId)?.name ?? null : null\n const pathLabel = node.pathLabel || node.name\n const recordId = String(node.id)\n return {\n id: node.id,\n name: node.name,\n slug: slugByOrgId.get(recordId) ?? null,\n updatedAt: updatedAtByOrgId.get(recordId) ?? null,\n logoUrl: logoUrlByOrgId.get(recordId) ?? null,\n tenantId: tid,\n tenantName: tenantNameMap[tid] ?? tid,\n parentId: node.parentId,\n parentName,\n depth: node.depth,\n rootId: node.rootId,\n treePath: node.treePath,\n pathLabel,\n ancestorIds: node.ancestorIds,\n childIds: node.childIds,\n descendantIds: node.descendantIds,\n childrenCount: node.childIds.length,\n descendantsCount: node.descendantIds.length,\n isActive: node.isActive,\n ...(cfByOrg[recordId] ?? {}),\n }\n })\n const totalPages = Math.max(1, Math.ceil(total / pageSize))\n await logCrudAccess({\n container,\n auth,\n request: req,\n items,\n idField: 'id',\n resourceKind: 'directory.organization',\n organizationId: null,\n tenantId: null,\n query,\n accessType: ids && ids.length === 1 ? 'read:item' : undefined,\n })\n return NextResponse.json({ items, total, page, pageSize, totalPages, isSuperAdmin })\n }\n\n if (!tenantId) {\n return NextResponse.json({ items: [], error: 'Tenant scope required' }, { status: 400 })\n }\n\n const orgListFilter: FilterQuery<Organization> = { tenant: tenantId, deletedAt: null }\n const orgs = await em.find(Organization, orgListFilter, { orderBy: { name: 'ASC' } })\n const hierarchy = computeHierarchyForOrganizations(orgs, tenantId)\n const slugByOrgId = new Map<string, string | null>()\n const logoUrlByOrgId = new Map<string, string | null>()\n const updatedAtByOrgId = new Map<string, string | null>()\n for (const org of orgs) {\n slugByOrgId.set(String(org.id), org.slug ?? null)\n logoUrlByOrgId.set(String(org.id), org.logoUrl ?? null)\n updatedAtByOrgId.set(String(org.id), org.updatedAt instanceof Date ? org.updatedAt.toISOString() : null)\n }\n\n // Manage view: paginated flat list for a single tenant\n const search = (query.search || '').trim().toLowerCase()\n let rows = hierarchy.ordered\n if (status === 'active') {\n rows = rows.filter((node) => node.isActive)\n } else if (status === 'inactive') {\n rows = rows.filter((node) => !node.isActive)\n }\n\n if (search) {\n rows = rows.filter((node) => {\n const pathLabel = (node.pathLabel || '').toLowerCase()\n return node.name.toLowerCase().includes(search) || pathLabel.includes(search)\n })\n }\n if (ids) {\n const idSet = new Set(ids)\n rows = rows.filter((node) => idSet.has(node.id))\n }\n\n const total = rows.length\n const pageSize = query.pageSize\n const page = query.page\n const start = (page - 1) * pageSize\n const paged = rows.slice(start, start + pageSize)\n const recordIds: string[] = []\n const tenantIdByRecord: Record<string, string | null> = {}\n const organizationIdByRecord: Record<string, string | null> = {}\n for (const node of paged) {\n const recordId = String(node.id)\n recordIds.push(recordId)\n tenantIdByRecord[recordId] = node.tenantId ? String(node.tenantId) : null\n organizationIdByRecord[recordId] = recordId\n }\n const cfByOrg = recordIds.length\n ? await loadCustomFieldValues({\n em,\n entityId: E.directory.organization,\n recordIds,\n tenantIdByRecord,\n organizationIdByRecord,\n tenantFallbacks: tenantId ? [tenantId] : [],\n })\n : {}\n let tenantName: string | null = null\n if (isSuperAdmin && tenantId) {\n const tenant = await em.findOne(Tenant, { id: tenantId })\n if (tenant) {\n const display = typeof tenant.name === 'string' && tenant.name.length > 0 ? tenant.name : stringId(tenant.id)\n tenantName = display\n }\n }\n const items = paged.map((node) => {\n const parentName = node.parentId ? hierarchy.map.get(node.parentId)?.name ?? null : null\n const pathLabel = node.pathLabel || node.name\n const recordId = String(node.id)\n return {\n id: node.id,\n name: node.name,\n slug: slugByOrgId.get(recordId) ?? null,\n logoUrl: logoUrlByOrgId.get(recordId) ?? null,\n updatedAt: updatedAtByOrgId.get(recordId) ?? null,\n tenantId: node.tenantId,\n tenantName,\n parentId: node.parentId,\n parentName,\n depth: node.depth,\n rootId: node.rootId,\n treePath: node.treePath,\n pathLabel,\n ancestorIds: node.ancestorIds,\n childIds: node.childIds,\n descendantIds: node.descendantIds,\n childrenCount: node.childIds.length,\n descendantsCount: node.descendantIds.length,\n isActive: node.isActive,\n ...(cfByOrg[recordId] ?? {}),\n }\n })\n const totalPages = Math.max(1, Math.ceil(total / pageSize))\n await logCrudAccess({\n container,\n auth,\n request: req,\n items,\n idField: 'id',\n resourceKind: 'directory.organization',\n organizationId: null,\n tenantId,\n query,\n accessType: ids && ids.length === 1 ? 'read:item' : undefined,\n })\n return NextResponse.json({ items, total, page, pageSize, totalPages, isSuperAdmin })\n}\n\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n\nconst organizationCreateResponseSchema = z.object({\n id: z.string().uuid(),\n})\n\nconst organizationDeleteRequestSchema = z.object({\n id: z.string().uuid(),\n})\n\nconst organizationsGetDoc: OpenApiMethodDoc = {\n summary: 'List organizations',\n description: 'Returns organizations using options, tree, or paginated manage view depending on the `view` parameter.',\n tags: [directoryTag],\n query: viewSchema,\n responses: [\n { status: 200, description: 'Organization data for the requested view.', schema: organizationListResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid query or tenant scope', schema: directoryErrorSchema },\n { status: 401, description: 'Authentication required', schema: directoryErrorSchema },\n ],\n}\n\nconst organizationsPostDoc: OpenApiMethodDoc = {\n summary: 'Create organization',\n description: 'Creates a new organization within a tenant and optionally assigns hierarchy relationships.',\n tags: [directoryTag],\n requestBody: {\n contentType: 'application/json',\n schema: organizationCreateSchema,\n description: 'Organization attributes and optional hierarchy configuration.',\n },\n responses: [\n { status: 201, description: 'Organization created.', schema: organizationCreateResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: directoryErrorSchema },\n { status: 401, description: 'Authentication required', schema: directoryErrorSchema },\n { status: 403, description: 'Missing directory.organizations.manage feature', schema: directoryErrorSchema },\n ],\n}\n\nconst organizationsPutDoc: OpenApiMethodDoc = {\n summary: 'Update organization',\n description: 'Updates organization details and hierarchy assignments.',\n tags: [directoryTag],\n requestBody: {\n contentType: 'application/json',\n schema: organizationUpdateSchema,\n description: 'Organization identifier followed by fields to update.',\n },\n responses: [\n { status: 200, description: 'Organization updated.', schema: directoryOkSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: directoryErrorSchema },\n { status: 401, description: 'Authentication required', schema: directoryErrorSchema },\n { status: 403, description: 'Missing directory.organizations.manage feature', schema: directoryErrorSchema },\n ],\n}\n\nconst organizationsDeleteDoc: OpenApiMethodDoc = {\n summary: 'Delete organization',\n description: 'Soft deletes an organization identified by id.',\n tags: [directoryTag],\n requestBody: {\n contentType: 'application/json',\n schema: organizationDeleteRequestSchema,\n description: 'Identifier of the organization to delete.',\n },\n responses: [\n { status: 200, description: 'Organization deleted.', schema: directoryOkSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed', schema: directoryErrorSchema },\n { status: 401, description: 'Authentication required', schema: directoryErrorSchema },\n { status: 403, description: 'Missing directory.organizations.manage feature', schema: directoryErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: directoryTag,\n summary: 'Manage organizations',\n methods: {\n GET: organizationsGetDoc,\n POST: organizationsPostDoc,\n PUT: organizationsPutDoc,\n DELETE: organizationsDeleteDoc,\n },\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,eAAe,qBAAqB;AAC7C,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,cAAc,cAAc;AACrC,SAAS,0BAA0B,gCAAgC;AACnE;AAAA,EACE;AAAA,OAGK;AACP,SAAS,mCAAmC;AAC5C;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,6BAA6B;AACtC,SAAS,SAAS;AAGlB,SAAS,wBAAwB,+BAA+B;AAEhE,SAAS,yBAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,qBAAqB,8BAA8B;AAC5D,SAAS,uBAAuB;AAChC,SAAS,oBAAoB,6BAA6B;AAG1D,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAiB/C,MAAM,aAAa,EAAE,OAAO;AAAA,EAC1B,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EACxC,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EACtD,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,MAAM,EAAE,KAAK,CAAC,WAAW,UAAU,MAAM,CAAC,EAAE,QAAQ,SAAS;AAAA,EAC7D,KAAK,EAAE,OAAO,EAAE,SAAS;AAAA,EACzB,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACrC,iBAAiB,EAAE,KAAK,CAAC,QAAQ,OAAO,CAAC,EAAE,SAAS;AAAA,EACpD,QAAQ,EAAE,KAAK,CAAC,OAAO,UAAU,UAAU,CAAC,EAAE,SAAS;AACzD,CAAC;AAED,SAAS,SAAS,KAAqC;AACrD,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,MAAM,IAAI,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AAC9D,SAAO,IAAI,SAAS,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,IAAI;AACjD;AAEA,SAAS,SAAS,OAAwB;AACxC,SAAO,OAAO,KAAK;AACrB;AAEA,SAAS,mBACP,cACA,mBACA,cACe;AACf,MAAI,cAAc;AAChB,WAAO,qBAAqB,gBAAgB;AAAA,EAC9C;AACA,MAAI,gBAAgB,qBAAqB,sBAAsB,cAAc;AAC3E,WAAO;AAAA,EACT;AACA,SAAO,qBAAqB;AAC9B;AAEA,MAAM,OAAO,cAA6D;AAAA,EACxE,UAAU;AAAA,IACR,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,8BAA8B,EAAE;AAAA,IAC5E,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,gCAAgC,EAAE;AAAA,IAC/E,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,gCAAgC,EAAE;AAAA,IAC9E,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,gCAAgC,EAAE;AAAA,EACnF;AAAA,EACA,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,CAAC,EAAE,OAAO,MAAM;AAAA,MAC1B,UAAU,CAAC,EAAE,OAAO,OAAO,EAAE,IAAI,OAAO,OAAO,EAAE,EAAE;AAAA,MACnD,QAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,CAAC,EAAE,OAAO,MAAM;AAAA,MAC1B,UAAU,OAAO,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,UAAU,OAAO,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,EACF;AACF,CAAC;AAEM,MAAM,WAAW,KAAK;AAE7B,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElE,QAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,QAAM,SAAS,WAAW,UAAU;AAAA,IAClC,MAAM,IAAI,aAAa,IAAI,MAAM,KAAK;AAAA,IACtC,UAAU,IAAI,aAAa,IAAI,UAAU,KAAK;AAAA,IAC9C,QAAQ,IAAI,aAAa,IAAI,QAAQ,KAAK;AAAA,IAC1C,MAAM,IAAI,aAAa,IAAI,MAAM,KAAK;AAAA,IACtC,KAAK,IAAI,aAAa,IAAI,KAAK,KAAK;AAAA,IACpC,UAAU,IAAI,aAAa,IAAI,UAAU,KAAK;AAAA,IAC9C,iBAAiB,IAAI,aAAa,IAAI,iBAAiB,KAAK;AAAA,IAC5D,QAAQ,IAAI,aAAa,IAAI,QAAQ,KAAK;AAAA,EAC5C,CAAC;AACD,MAAI,CAAC,OAAO,QAAS,QAAO,aAAa,KAAK,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAE5E,QAAM,QAAQ,OAAO;AACrB,QAAM,MAAM,SAAS,MAAM,OAAO,IAAI;AACtC,QAAM,qBAAqB,MAAM,YAAY;AAC7C,QAAM,8BAA8B,sBAAsB,mBAAmB,YAAY,MAAM,QAAQ,OAAO;AAC9G,QAAM,eAAe,KAAK,YAAY;AACtC,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,eAAe,MAAM,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAClE,QAAM,KAAM,UAAU,QAAQ,IAAI;AAClC,QAAM,kBAAkB,gBAAgB,CAAC,+BAA+B,MAAM,SAAS;AACvF,MAAI,WAAW,kBAAkB,OAAO,mBAAmB,cAAc,6BAA6B,YAAY;AAClH,QAAM,SAAS,MAAM,UAAU;AAC/B,QAAM,kBAAkB,kBAAkB,MAAM,eAAe,MAAM,QAAQ,WAAW;AAExF,MAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,gBAAgB,KAAK,QAAQ;AACjE,QAAI,YAAY;AAChB,QAAI,CAAC,cAAc;AACjB,UAAI,aAA8B;AAClC,UAAI;AACF,cAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AACxF,qBAAa,MAAM,QAAQ,MAAM,UAAU,IAAI,MAAM,aAAa;AAAA,MACpE,QAAQ;AAAA,MAAC;AACT,YAAM,aAAa,aAAa,IAAI,IAAI,UAAU,IAAI,oBAAI,IAAY;AACtE,kBAAY,IAAI,OAAO,CAAC,OAAO,WAAW,IAAI,EAAE,CAAC;AAAA,IACnD;AACA,QAAI,UAAU,QAAQ;AACpB,YAAM,aAA6B,MAAM;AAAA,QACvC;AAAA,QACA;AAAA,QACA,EAAE,IAAI,EAAE,KAAK,UAAU,GAAG,WAAW,KAAK;AAAA,QAC1C,EAAE,UAAU,CAAC,QAAQ,EAAE;AAAA,QACvB,EAAE,UAAU,cAAc,gBAAgB,KAAK;AAAA,MACjD;AACA,YAAM,mBAAmB,oBAAI,IAAY;AACzC,iBAAW,OAAO,YAAY;AAC5B,cAAM,cAAc,IAAI,QAAQ,KAAK,SAAS,IAAI,OAAO,EAAE,IAAI;AAC/D,YAAI,YAAa,kBAAiB,IAAI,WAAW;AAAA,MACnD;AACA,UAAI,iBAAiB,SAAS,GAAG;AAC/B,cAAM,oBAAoB,MAAM,KAAK,gBAAgB,EAAE,CAAC,KAAK;AAC7D,YAAI;AACF,qBAAW,MAAM,uBAAuB,EAAE,MAAM,UAAU,GAAG,iBAAiB;AAAA,QAChF,SAAS,OAAO;AACd,cAAI,gBAAgB,KAAK,GAAG;AAC1B,mBAAO,aAAa,KAAK,MAAM,MAAM,EAAE,QAAQ,MAAM,OAAO,CAAC;AAAA,UAC/D;AACA,gBAAM;AAAA,QACR;AAAA,MACF,WAAW,iBAAiB,OAAO,GAAG;AACpC,eAAO,aAAa,KAAK,EAAE,OAAO,CAAC,GAAG,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MACzF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,mBAAmB,CAAC,UAAU;AACjC,UAAM,kBAAkB,oBAAI,IAAY;AACxC,UAAM,cAAc,mCAAmC,GAAG;AAC1D,UAAM,uBAAuB,eAAe,CAAC,4BAA4B,WAAW,IAAI,cAAc;AACtG,QAAI,qBAAsB,iBAAgB,IAAI,oBAAoB;AAClE,QAAI,KAAK,MAAO,iBAAgB,IAAI,KAAK,KAAK;AAE9C,QAAI;AACF,YAAM,QAAQ,MAAM,mCAAmC;AAAA,QACrD;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT,YAAY,wBAAwB;AAAA,MACtC,CAAC;AACD,UAAI,MAAM,WAAY,iBAAgB,IAAI,MAAM,UAAU;AAC1D,UAAI,MAAM,QAAQ,MAAM,SAAS,KAAK,MAAM,UAAU,QAAQ;AAC5D,wBAAgB,IAAI,MAAM,UAAU,CAAC,CAAE;AAAA,MACzC;AACA,UAAI,MAAM,QAAQ,MAAM,UAAU,KAAK,MAAM,WAAW,QAAQ;AAC9D,wBAAgB,IAAI,MAAM,WAAW,CAAC,CAAE;AAAA,MAC1C;AAAA,IACF,QAAQ;AAAA,IAAC;AAET,eAAW,SAAS,iBAAiB;AACnC,UAAI,CAAC,MAAO;AACZ,YAAM,MAAM,MAAM;AAAA,QAChB;AAAA,QACA;AAAA,QACA,EAAE,IAAI,OAAO,WAAW,KAAK;AAAA,QAC7B,EAAE,UAAU,CAAC,QAAQ,EAAE;AAAA,QACvB,EAAE,UAAU,cAAc,gBAAgB,MAAM;AAAA,MAClD;AACA,UAAI,KAAK,UAAU,IAAI,OAAO,IAAI;AAChC,mBAAW,SAAS,IAAI,OAAO,EAAE;AACjC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,mBAAmB,CAAC,UAAU;AACjC,WAAO,aAAa,KAAK,EAAE,OAAO,CAAC,GAAG,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzF;AAEA,MAAI,MAAM,SAAS,WAAW;AAC5B,QAAI,CAAC,UAAU;AACb,aAAO,aAAa,KAAK,EAAE,OAAO,CAAC,GAAG,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACzF;AACA,UAAM,QAAmC,EAAE,QAAQ,UAAU,WAAW,KAAK;AAC7E,QAAI,WAAW,SAAU,OAAM,WAAW;AAC1C,QAAI,WAAW,WAAY,OAAM,WAAW;AAC5C,QAAI,WAAW,SAAS,CAAC,gBAAiB,OAAM,WAAW;AAC3D,QAAI,IAAK,OAAM,KAAK,EAAE,KAAK,IAAI;AAC/B,UAAMA,QAAO,MAAM,GAAG,KAAK,cAAc,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,EAAE,CAAC;AAC5E,UAAMC,SAAQD,MAAK,IAAI,CAAC,SAAS;AAAA,MAC/B,IAAI,SAAS,IAAI,EAAE;AAAA,MACnB,MAAM,IAAI;AAAA,MACV,SAAS,IAAI,WAAW;AAAA,MACxB,UAAU,IAAI,YAAY;AAAA,MAC1B;AAAA,MACA,UAAU,CAAC,CAAC,IAAI;AAAA,MAChB,OAAO,IAAI,SAAS;AAAA,MACpB,UAAU,IAAI,YAAY,SAAS,IAAI,EAAE;AAAA,IAC3C,EAAE;AACF,UAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,OAAAC;AAAA,MACA,SAAS;AAAA,MACT,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,MACA,YAAY,OAAO,IAAI,WAAW,IAAI,cAAc;AAAA,IACtD,CAAC;AACD,WAAO,aAAa,KAAK,EAAE,OAAAA,OAAM,CAAC;AAAA,EACpC;AAEA,MAAI,MAAM,SAAS,QAAQ;AACzB,QAAI,CAAC,UAAU;AACb,aAAO,aAAa,KAAK,EAAE,OAAO,CAAC,GAAG,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACzF;AACA,UAAMC,iBAA2C,EAAE,QAAQ,UAAU,WAAW,KAAK;AACrF,UAAMF,QAAO,MAAM,GAAG,KAAK,cAAcE,gBAAe,EAAE,SAAS,EAAE,MAAM,MAAM,EAAE,CAAC;AACpF,UAAMC,aAAY,iCAAiCH,OAAM,QAAQ;AACjE,UAAM,UAAU,oBAAI,IAAsE;AAC1F,UAAM,QAAoB,CAAC;AAC3B,eAAW,QAAQG,WAAU,SAAS;AACpC,YAAM,WAAqB;AAAA,QACzB,IAAI,KAAK;AAAA,QACT,MAAM,KAAK;AAAA,QACX,UAAU,KAAK;AAAA,QACf,UAAU,KAAK;AAAA,QACf,OAAO,KAAK;AAAA,QACZ,aAAa,KAAK;AAAA,QAClB,UAAU,KAAK;AAAA,QACf,eAAe,KAAK;AAAA,QACpB,UAAU,KAAK;AAAA,QACf,UAAU,KAAK;AAAA,QACf,WAAW,KAAK;AAAA,QAChB,UAAU,CAAC;AAAA,MACb;AACA,cAAQ,IAAI,KAAK,IAAI,EAAE,MAAM,UAAU,SAAS,SAAS,CAAC;AAC1D,UAAI,KAAK,YAAY,QAAQ,IAAI,KAAK,QAAQ,GAAG;AAC/C,cAAM,cAAc,QAAQ,IAAI,KAAK,QAAQ;AAC7C,oBAAY,SAAS,KAAK,QAAQ;AAAA,MACpC,OAAO;AACL,cAAM,KAAK,QAAQ;AAAA,MACrB;AAAA,IACF;AACA,UAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,OAAO;AAAA,MACP,SAAS;AAAA,MACT,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO,aAAa,KAAK,EAAE,OAAO,MAAM,CAAC;AAAA,EAC3C;AAEA,MAAI,MAAM,SAAS,UAAU;AAC3B,WAAO,aAAa,KAAK,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzD;AAEA,MAAI,iBAAiB;AAEnB,UAAMC,WAAU,MAAM,UAAU,IAAI,KAAK,EAAE,YAAY;AACvD,UAAM,UAAU,MAAM;AAAA,MACpB;AAAA,MACA;AAAA,MACA,EAAE,WAAW,KAAK;AAAA,MAClB,EAAE,SAAS,EAAE,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,EAAE;AAAA,MACjD,EAAE,UAAU,MAAM,gBAAgB,KAAK;AAAA,IACzC;AACA,UAAM,WAAW,oBAAI,IAA4B;AACjD,eAAW,OAAO,SAAS;AACzB,YAAM,eAAe,IAAI;AACzB,YAAM,MAAM,eAAe,SAAS,aAAa,EAAE,IAAI;AACvD,UAAI,CAAC,IAAK;AACV,UAAI,CAAC,SAAS,IAAI,GAAG,EAAG,UAAS,IAAI,KAAK,CAAC,CAAC;AAC5C,eAAS,IAAI,GAAG,EAAG,KAAK,GAAG;AAAA,IAC7B;AAEA,UAAMC,eAAc,oBAAI,IAA2B;AACnD,UAAMC,oBAAmB,oBAAI,IAA2B;AACxD,UAAMC,kBAAiB,oBAAI,IAA2B;AACtD,eAAW,OAAO,SAAS;AACzB,MAAAF,aAAY,IAAI,OAAO,IAAI,EAAE,GAAG,IAAI,QAAQ,IAAI;AAChD,MAAAC,kBAAiB,IAAI,OAAO,IAAI,EAAE,GAAG,IAAI,qBAAqB,OAAO,IAAI,UAAU,YAAY,IAAI,IAAI;AACvG,MAAAC,gBAAe,IAAI,OAAO,IAAI,EAAE,GAAG,IAAI,WAAW,IAAI;AAAA,IACxD;AAEA,UAAM,YAAY,MAAM,KAAK,SAAS,KAAK,CAAC;AAC5C,UAAM,UAAU,UAAU,SACtB,MAAM,GAAG,KAAK,QAAQ,EAAE,IAAI,EAAE,KAAK,UAAiC,EAAE,CAAC,IACvE,CAAC;AACL,UAAM,gBAAgB,QAAQ,OAA+B,CAAC,KAAK,WAAW;AAC5E,YAAM,MAAM,SAAS,OAAO,EAAE;AAC9B,YAAM,OAAO,OAAO,OAAO,SAAS,YAAY,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO;AACvF,UAAI,GAAG,IAAI;AACX,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAEL,UAAM,cAAc,oBAAI,IAA+B;AACvD,UAAM,eAA4E,CAAC;AACnF,eAAW,CAAC,KAAKP,KAAI,KAAK,SAAS,QAAQ,GAAG;AAC5C,YAAMG,aAAY,iCAAiCH,OAAM,GAAG;AAC5D,kBAAY,IAAI,KAAKG,UAAS;AAC9B,iBAAW,QAAQA,WAAU,SAAS;AACpC,qBAAa,KAAK,EAAE,UAAU,KAAK,KAAK,CAAC;AAAA,MAC3C;AAAA,IACF;AAEA,QAAIK,QAAO;AACX,QAAI,MAAM,WAAW,UAAU;AAC7B,MAAAA,QAAOA,MAAK,OAAO,CAAC,UAAU,MAAM,KAAK,QAAQ;AAAA,IACnD,WAAW,MAAM,WAAW,YAAY;AACtC,MAAAA,QAAOA,MAAK,OAAO,CAAC,UAAU,CAAC,MAAM,KAAK,QAAQ;AAAA,IACpD;AAEA,QAAIJ,SAAQ;AACV,MAAAI,QAAOA,MAAK,OAAO,CAAC,EAAE,KAAK,MAAM;AAC/B,cAAM,aAAa,KAAK,aAAa,IAAI,YAAY;AACrD,eAAO,KAAK,KAAK,YAAY,EAAE,SAASJ,OAAM,KAAK,UAAU,SAASA,OAAM;AAAA,MAC9E,CAAC;AAAA,IACH;AACA,QAAI,KAAK;AACP,YAAM,QAAQ,IAAI,IAAI,GAAG;AACzB,MAAAI,QAAOA,MAAK,OAAO,CAAC,EAAE,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;AAAA,IACrD;AAEA,IAAAA,MAAK,KAAK,CAAC,GAAG,MAAM;AAClB,YAAM,QAAQ,cAAc,EAAE,QAAQ,KAAK,EAAE;AAC7C,YAAM,QAAQ,cAAc,EAAE,QAAQ,KAAK,EAAE;AAC7C,YAAM,gBAAgB,MAAM,cAAc,KAAK;AAC/C,UAAI,kBAAkB,EAAG,QAAO;AAChC,aAAO,EAAE,KAAK,UAAU,cAAc,EAAE,KAAK,SAAS;AAAA,IACxD,CAAC;AAED,UAAMC,SAAQD,MAAK;AACnB,UAAME,YAAW,MAAM;AACvB,UAAMC,QAAO,MAAM;AACnB,UAAMC,UAASD,QAAO,KAAKD;AAC3B,UAAMG,SAAQL,MAAK,MAAMI,QAAOA,SAAQF,SAAQ;AAChD,UAAMI,aAAsB,CAAC;AAC7B,UAAMC,oBAAkD,CAAC;AACzD,UAAMC,0BAAwD,CAAC;AAC/D,eAAW,SAASH,QAAO;AACzB,YAAM,WAAW,OAAO,MAAM,KAAK,EAAE;AACrC,MAAAC,WAAU,KAAK,QAAQ;AACvB,MAAAC,kBAAiB,QAAQ,IAAI,MAAM;AACnC,MAAAC,wBAAuB,QAAQ,IAAI;AAAA,IACrC;AACA,UAAM,kBAAkB,MAAM,KAAK,IAAI,IAAIF,WAAU,IAAI,CAAC,OAAOC,kBAAiB,EAAE,CAAC,EAAE,OAAO,CAAC,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC,CAAC,CAAC;AACzK,UAAME,WAAUH,WAAU,SACtB,MAAM,sBAAsB;AAAA,MAC1B;AAAA,MACA,UAAU,EAAE,UAAU;AAAA,MACtB,WAAAA;AAAA,MACA,kBAAAC;AAAA,MACA,wBAAAC;AAAA,MACA;AAAA,IACF,CAAC,IACD,CAAC;AACL,UAAMf,SAAQY,OAAM,IAAI,CAAC,EAAE,UAAU,KAAK,KAAK,MAAM;AACnD,YAAMV,aAAY,YAAY,IAAI,GAAG;AACrC,YAAM,aAAa,KAAK,YAAYA,aAAYA,WAAU,IAAI,IAAI,KAAK,QAAQ,GAAG,QAAQ,OAAO;AACjG,YAAM,YAAY,KAAK,aAAa,KAAK;AACzC,YAAM,WAAW,OAAO,KAAK,EAAE;AAC/B,aAAO;AAAA,QACL,IAAI,KAAK;AAAA,QACT,MAAM,KAAK;AAAA,QACX,MAAME,aAAY,IAAI,QAAQ,KAAK;AAAA,QACnC,WAAWC,kBAAiB,IAAI,QAAQ,KAAK;AAAA,QAC7C,SAASC,gBAAe,IAAI,QAAQ,KAAK;AAAA,QACzC,UAAU;AAAA,QACV,YAAY,cAAc,GAAG,KAAK;AAAA,QAClC,UAAU,KAAK;AAAA,QACf;AAAA,QACA,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,QACb,UAAU,KAAK;AAAA,QACf;AAAA,QACA,aAAa,KAAK;AAAA,QAClB,UAAU,KAAK;AAAA,QACf,eAAe,KAAK;AAAA,QACpB,eAAe,KAAK,SAAS;AAAA,QAC7B,kBAAkB,KAAK,cAAc;AAAA,QACrC,UAAU,KAAK;AAAA,QACf,GAAIU,SAAQ,QAAQ,KAAK,CAAC;AAAA,MAC5B;AAAA,IACF,CAAC;AACD,UAAMC,cAAa,KAAK,IAAI,GAAG,KAAK,KAAKT,SAAQC,SAAQ,CAAC;AAC1D,UAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,OAAAT;AAAA,MACA,SAAS;AAAA,MACT,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,UAAU;AAAA,MACV;AAAA,MACA,YAAY,OAAO,IAAI,WAAW,IAAI,cAAc;AAAA,IACtD,CAAC;AACD,WAAO,aAAa,KAAK,EAAE,OAAAA,QAAO,OAAAQ,QAAO,MAAAE,OAAM,UAAAD,WAAU,YAAAQ,aAAY,aAAa,CAAC;AAAA,EACrF;AAEA,MAAI,CAAC,UAAU;AACb,WAAO,aAAa,KAAK,EAAE,OAAO,CAAC,GAAG,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzF;AAEA,QAAM,gBAA2C,EAAE,QAAQ,UAAU,WAAW,KAAK;AACrF,QAAM,OAAO,MAAM,GAAG,KAAK,cAAc,eAAe,EAAE,SAAS,EAAE,MAAM,MAAM,EAAE,CAAC;AACpF,QAAM,YAAY,iCAAiC,MAAM,QAAQ;AACjE,QAAM,cAAc,oBAAI,IAA2B;AACnD,QAAM,iBAAiB,oBAAI,IAA2B;AACtD,QAAM,mBAAmB,oBAAI,IAA2B;AACxD,aAAW,OAAO,MAAM;AACtB,gBAAY,IAAI,OAAO,IAAI,EAAE,GAAG,IAAI,QAAQ,IAAI;AAChD,mBAAe,IAAI,OAAO,IAAI,EAAE,GAAG,IAAI,WAAW,IAAI;AACtD,qBAAiB,IAAI,OAAO,IAAI,EAAE,GAAG,IAAI,qBAAqB,OAAO,IAAI,UAAU,YAAY,IAAI,IAAI;AAAA,EACzG;AAGA,QAAM,UAAU,MAAM,UAAU,IAAI,KAAK,EAAE,YAAY;AACvD,MAAI,OAAO,UAAU;AACrB,MAAI,WAAW,UAAU;AACvB,WAAO,KAAK,OAAO,CAAC,SAAS,KAAK,QAAQ;AAAA,EAC5C,WAAW,WAAW,YAAY;AAChC,WAAO,KAAK,OAAO,CAAC,SAAS,CAAC,KAAK,QAAQ;AAAA,EAC7C;AAEA,MAAI,QAAQ;AACV,WAAO,KAAK,OAAO,CAAC,SAAS;AAC3B,YAAM,aAAa,KAAK,aAAa,IAAI,YAAY;AACrD,aAAO,KAAK,KAAK,YAAY,EAAE,SAAS,MAAM,KAAK,UAAU,SAAS,MAAM;AAAA,IAC9E,CAAC;AAAA,EACH;AACA,MAAI,KAAK;AACP,UAAM,QAAQ,IAAI,IAAI,GAAG;AACzB,WAAO,KAAK,OAAO,CAAC,SAAS,MAAM,IAAI,KAAK,EAAE,CAAC;AAAA,EACjD;AAEA,QAAM,QAAQ,KAAK;AACnB,QAAM,WAAW,MAAM;AACvB,QAAM,OAAO,MAAM;AACnB,QAAM,SAAS,OAAO,KAAK;AAC3B,QAAM,QAAQ,KAAK,MAAM,OAAO,QAAQ,QAAQ;AAChD,QAAM,YAAsB,CAAC;AAC7B,QAAM,mBAAkD,CAAC;AACzD,QAAM,yBAAwD,CAAC;AAC/D,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAW,OAAO,KAAK,EAAE;AAC/B,cAAU,KAAK,QAAQ;AACvB,qBAAiB,QAAQ,IAAI,KAAK,WAAW,OAAO,KAAK,QAAQ,IAAI;AACrE,2BAAuB,QAAQ,IAAI;AAAA,EACrC;AACA,QAAM,UAAU,UAAU,SACtB,MAAM,sBAAsB;AAAA,IAC1B;AAAA,IACA,UAAU,EAAE,UAAU;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB,WAAW,CAAC,QAAQ,IAAI,CAAC;AAAA,EAC5C,CAAC,IACD,CAAC;AACL,MAAI,aAA4B;AAChC,MAAI,gBAAgB,UAAU;AAC5B,UAAM,SAAS,MAAM,GAAG,QAAQ,QAAQ,EAAE,IAAI,SAAS,CAAC;AACxD,QAAI,QAAQ;AACV,YAAM,UAAU,OAAO,OAAO,SAAS,YAAY,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO,SAAS,OAAO,EAAE;AAC5G,mBAAa;AAAA,IACf;AAAA,EACF;AACA,QAAM,QAAQ,MAAM,IAAI,CAAC,SAAS;AAChC,UAAM,aAAa,KAAK,WAAW,UAAU,IAAI,IAAI,KAAK,QAAQ,GAAG,QAAQ,OAAO;AACpF,UAAM,YAAY,KAAK,aAAa,KAAK;AACzC,UAAM,WAAW,OAAO,KAAK,EAAE;AAC/B,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX,MAAM,YAAY,IAAI,QAAQ,KAAK;AAAA,MACnC,SAAS,eAAe,IAAI,QAAQ,KAAK;AAAA,MACzC,WAAW,iBAAiB,IAAI,QAAQ,KAAK;AAAA,MAC7C,UAAU,KAAK;AAAA,MACf;AAAA,MACA,UAAU,KAAK;AAAA,MACf;AAAA,MACA,OAAO,KAAK;AAAA,MACZ,QAAQ,KAAK;AAAA,MACb,UAAU,KAAK;AAAA,MACf;AAAA,MACA,aAAa,KAAK;AAAA,MAClB,UAAU,KAAK;AAAA,MACf,eAAe,KAAK;AAAA,MACpB,eAAe,KAAK,SAAS;AAAA,MAC7B,kBAAkB,KAAK,cAAc;AAAA,MACrC,UAAU,KAAK;AAAA,MACf,GAAI,QAAQ,QAAQ,KAAK,CAAC;AAAA,IAC5B;AAAA,EACF,CAAC;AACD,QAAM,aAAa,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,QAAQ,CAAC;AAC1D,QAAM,cAAc;AAAA,IAClB;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT;AAAA,IACA,SAAS;AAAA,IACT,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB;AAAA,IACA;AAAA,IACA,YAAY,OAAO,IAAI,WAAW,IAAI,cAAc;AAAA,EACtD,CAAC;AACD,SAAO,aAAa,KAAK,EAAE,OAAO,OAAO,MAAM,UAAU,YAAY,aAAa,CAAC;AACrF;AAEO,MAAM,OAAO,KAAK;AAClB,MAAM,MAAM,KAAK;AACjB,MAAM,SAAS,KAAK;AAE3B,MAAM,mCAAmC,EAAE,OAAO;AAAA,EAChD,IAAI,EAAE,OAAO,EAAE,KAAK;AACtB,CAAC;AAED,MAAM,kCAAkC,EAAE,OAAO;AAAA,EAC/C,IAAI,EAAE,OAAO,EAAE,KAAK;AACtB,CAAC;AAED,MAAM,sBAAwC;AAAA,EAC5C,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,YAAY;AAAA,EACnB,OAAO;AAAA,EACP,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,6CAA6C,QAAQ,+BAA+B;AAAA,EAClH;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,iCAAiC,QAAQ,qBAAqB;AAAA,IAC1F,EAAE,QAAQ,KAAK,aAAa,2BAA2B,QAAQ,qBAAqB;AAAA,EACtF;AACF;AAEA,MAAM,uBAAyC;AAAA,EAC7C,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,YAAY;AAAA,EACnB,aAAa;AAAA,IACX,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,aAAa;AAAA,EACf;AAAA,EACA,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,yBAAyB,QAAQ,iCAAiC;AAAA,EAChG;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,qBAAqB;AAAA,IAC9E,EAAE,QAAQ,KAAK,aAAa,2BAA2B,QAAQ,qBAAqB;AAAA,IACpF,EAAE,QAAQ,KAAK,aAAa,kDAAkD,QAAQ,qBAAqB;AAAA,EAC7G;AACF;AAEA,MAAM,sBAAwC;AAAA,EAC5C,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,YAAY;AAAA,EACnB,aAAa;AAAA,IACX,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,aAAa;AAAA,EACf;AAAA,EACA,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,yBAAyB,QAAQ,kBAAkB;AAAA,EACjF;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,qBAAqB;AAAA,IAC9E,EAAE,QAAQ,KAAK,aAAa,2BAA2B,QAAQ,qBAAqB;AAAA,IACpF,EAAE,QAAQ,KAAK,aAAa,kDAAkD,QAAQ,qBAAqB;AAAA,EAC7G;AACF;AAEA,MAAM,yBAA2C;AAAA,EAC/C,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,YAAY;AAAA,EACnB,aAAa;AAAA,IACX,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,aAAa;AAAA,EACf;AAAA,EACA,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,yBAAyB,QAAQ,kBAAkB;AAAA,EACjF;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,qBAAqB;AAAA,IAC9E,EAAE,QAAQ,KAAK,aAAa,2BAA2B,QAAQ,qBAAqB;AAAA,IACpF,EAAE,QAAQ,KAAK,aAAa,kDAAkD,QAAQ,qBAAqB;AAAA,EAC7G;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,IACN,KAAK;AAAA,IACL,QAAQ;AAAA,EACV;AACF;",
|
|
6
|
+
"names": ["orgs", "items", "orgListFilter", "hierarchy", "search", "slugByOrgId", "updatedAtByOrgId", "logoUrlByOrgId", "rows", "total", "pageSize", "page", "start", "paged", "recordIds", "tenantIdByRecord", "organizationIdByRecord", "cfByOrg", "totalPages"]
|
|
7
7
|
}
|
|
@@ -7,6 +7,9 @@ import { getEntityIds } from "@open-mercato/shared/lib/encryption/entityIds";
|
|
|
7
7
|
import { upsertCustomEntitySchema } from "@open-mercato/core/modules/entities/data/validators";
|
|
8
8
|
import { isSystemEntitySelectable } from "@open-mercato/shared/lib/entities/system-entities";
|
|
9
9
|
import { SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, isOrmBackedSystemEntityId } from "@open-mercato/shared/lib/data/engine";
|
|
10
|
+
import { enforceCommandOptimisticLock } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
|
|
11
|
+
import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
12
|
+
const CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND = "entities.entity";
|
|
10
13
|
const metadata = {
|
|
11
14
|
GET: { requireAuth: true },
|
|
12
15
|
POST: { requireAuth: true, requireFeatures: ["entities.definitions.manage"] },
|
|
@@ -47,7 +50,8 @@ async function GET(req) {
|
|
|
47
50
|
description: c.description ?? void 0,
|
|
48
51
|
labelField: c.labelField ?? void 0,
|
|
49
52
|
defaultEditor: c.defaultEditor ?? void 0,
|
|
50
|
-
showInSidebar: c.showInSidebar ?? false
|
|
53
|
+
showInSidebar: c.showInSidebar ?? false,
|
|
54
|
+
updatedAt: c.updatedAt instanceof Date ? c.updatedAt.toISOString() : c.updatedAt ?? void 0
|
|
51
55
|
}));
|
|
52
56
|
const byId = /* @__PURE__ */ new Map();
|
|
53
57
|
for (const g of generated) byId.set(g.entityId, g);
|
|
@@ -99,6 +103,21 @@ async function POST(req) {
|
|
|
99
103
|
}
|
|
100
104
|
const where = { entityId: input.entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null };
|
|
101
105
|
let ent = await em.findOne(CustomEntity, where);
|
|
106
|
+
if (ent) {
|
|
107
|
+
try {
|
|
108
|
+
enforceCommandOptimisticLock({
|
|
109
|
+
resourceKind: CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND,
|
|
110
|
+
resourceId: ent.id,
|
|
111
|
+
current: ent.updatedAt ?? null,
|
|
112
|
+
request: req
|
|
113
|
+
});
|
|
114
|
+
} catch (lockError) {
|
|
115
|
+
if (isCrudHttpError(lockError)) {
|
|
116
|
+
return NextResponse.json(lockError.body, { status: lockError.status });
|
|
117
|
+
}
|
|
118
|
+
throw lockError;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
102
121
|
if (!ent) ent = em.create(CustomEntity, { ...where, createdAt: /* @__PURE__ */ new Date() });
|
|
103
122
|
ent.label = input.label;
|
|
104
123
|
ent.description = input.description ?? null;
|
|
@@ -156,6 +175,7 @@ const entitySummarySchema = z.object({
|
|
|
156
175
|
labelField: z.string().optional(),
|
|
157
176
|
defaultEditor: z.string().optional(),
|
|
158
177
|
showInSidebar: z.boolean().optional(),
|
|
178
|
+
updatedAt: z.string().optional(),
|
|
159
179
|
count: z.number()
|
|
160
180
|
});
|
|
161
181
|
const entityListResponseSchema = z.object({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/entities/api/entities.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { CustomEntity, CustomFieldDef } from '@open-mercato/core/modules/entities/data/entities'\nimport { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { upsertCustomEntitySchema } from '@open-mercato/core/modules/entities/data/validators'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { isSystemEntitySelectable } from '@open-mercato/shared/lib/entities/system-entities'\nimport { SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, isOrmBackedSystemEntityId } from '@open-mercato/shared/lib/data/engine'\n\nexport const metadata = {\n GET: { requireAuth: true },\n POST: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || (!auth.orgId && !auth.isSuperAdmin)) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n // Generated entities from code\n const AllEntities = getEntityIds()\n const generated: { entityId: string; source: 'code'; label: string }[] = []\n for (const modId of Object.keys(AllEntities)) {\n const entities = (AllEntities as any)[modId] as Record<string, string>\n for (const k of Object.keys(entities)) {\n const id = entities[k]\n if (!isSystemEntitySelectable(id)) continue\n generated.push({ entityId: id, source: 'code', label: id })\n }\n }\n\n // Custom user-defined entities (global/org/tenant scoped)\n const where: any = { isActive: true }\n where.$and = [\n { $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] },\n { $or: [ { tenantId: auth.tenantId ?? undefined as any }, { tenantId: null } ] },\n ]\n const customs = await em.find(CustomEntity as any, where as any, { orderBy: { entityId: 'asc' } as any })\n // Resolve overlay precedence: prefer organization/tenant-specific over global\n const customByEntityId = new Map<string, any>()\n for (const c of customs as any[]) {\n const specificity = (c.organizationId ? 2 : 0) + (c.tenantId ? 1 : 0)\n const prev = customByEntityId.get(c.entityId)\n const prevSpec = prev ? ((prev.organizationId ? 2 : 0) + (prev.tenantId ? 1 : 0)) : -1\n if (!prev || specificity > prevSpec) customByEntityId.set(c.entityId, c)\n }\n\n const custom = Array.from(customByEntityId.values())\n .filter((c) => isSystemEntitySelectable(c.entityId))\n .map((c) => ({\n entityId: c.entityId,\n source: 'custom' as const,\n label: c.label,\n description: c.description ?? undefined,\n labelField: (c as any).labelField ?? undefined,\n defaultEditor: (c as any).defaultEditor ?? undefined,\n showInSidebar: (c as any).showInSidebar ?? false,\n }))\n\n const byId = new Map<string, any>()\n for (const g of generated) byId.set(g.entityId, g)\n for (const cu of custom) {\n const existing = byId.get(cu.entityId)\n byId.set(cu.entityId, { ...existing, ...cu, source: existing?.source ?? cu.source })\n }\n\n // Count field definitions scoped to current tenant/org (same scoping as custom entities)\n const defsWhere: any = { isActive: true }\n defsWhere.$and = [\n //{ $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] }, // the entities and custom fields are defined per tenant\n { tenantId: auth.tenantId ?? undefined as any },\n ]\n const defs = await em.find(CustomFieldDef as any, defsWhere as any)\n // Count distinct field names (keys) per entityId\n const keySets = new Map<string, Set<string>>()\n for (const d of defs as any[]) {\n const eid = String(d.entityId)\n const k = String(d.key)\n if (!isSystemEntitySelectable(eid)) continue\n const set = keySets.get(eid) || new Set<string>()\n set.add(k)\n keySets.set(eid, set)\n }\n const counts: Record<string, number> = {}\n for (const [eid, set] of keySets.entries()) counts[eid] = set.size\n\n const items = Array.from(byId.values()).map((it: any) => ({ ...it, count: counts[it.entityId] || 0 }))\n return NextResponse.json({ items })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const parsed = upsertCustomEntitySchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Validation failed', details: parsed.error.flatten() }, { status: 400 })\n }\n const input = parsed.data\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n // A registration for a module-declared, table-backed system entity would flip\n // query-engine classification to doc storage for the whole entity type (#2939's\n // failure mode via another door) \u2014 refuse to create one.\n if (isOrmBackedSystemEntityId(em, input.entityId)) {\n return NextResponse.json(\n { error: 'System entities cannot be registered as custom entities', code: SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, entityId: input.entityId },\n { status: 400 },\n )\n }\n\n const where: any = { entityId: input.entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n let ent = await em.findOne(CustomEntity, where)\n if (!ent) ent = em.create(CustomEntity, { ...where, createdAt: new Date() })\n ent.label = input.label\n ent.description = input.description ?? null\n ent.isActive = input.isActive ?? true\n ent.labelField = input.labelField ?? ent.labelField ?? null\n ent.defaultEditor = input.defaultEditor ?? ent.defaultEditor ?? null\n ent.showInSidebar = input.showInSidebar ?? ent.showInSidebar ?? false\n ent.updatedAt = new Date()\n em.persist(ent)\n await em.flush()\n // Invalidate sidebar/nav cache for tenant scope (also when tenantId is null)\n try {\n const cache = (await createRequestContainer()).resolve('cache') as any\n if (cache) {\n await cache.deleteByTags([`nav:entities:${auth.tenantId || 'null'}`])\n }\n } catch {}\n return NextResponse.json({ ok: true, item: { id: ent.id, entityId: ent.entityId, label: ent.label, description: ent.description ?? undefined } })\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const entityId = body?.entityId\n if (!entityId) return NextResponse.json({ error: 'entityId is required' }, { status: 400 })\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n const where: any = { entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n const ent = await em.findOne(CustomEntity, where)\n if (!ent) return NextResponse.json({ error: 'Not found' }, { status: 404 })\n ent.isActive = false\n ent.updatedAt = new Date()\n ent.deletedAt = ent.deletedAt ?? new Date()\n em.persist(ent)\n await em.flush()\n // Invalidate sidebar/nav cache for tenant scope (also when tenantId is null)\n try {\n const cache = (await createRequestContainer()).resolve('cache') as any\n if (cache) {\n await cache.deleteByTags([`nav:entities:${auth.tenantId || 'null'}`])\n }\n } catch {}\n return NextResponse.json({ ok: true })\n}\n\nconst entitySummarySchema = z.object({\n entityId: z.string(),\n source: z.enum(['code', 'custom']),\n label: z.string(),\n description: z.string().optional(),\n labelField: z.string().optional(),\n defaultEditor: z.string().optional(),\n showInSidebar: z.boolean().optional(),\n count: z.number(),\n})\n\nconst entityListResponseSchema = z.object({\n items: z.array(entitySummarySchema),\n})\n\nconst deleteEntityRequestSchema = z.object({\n entityId: z.string(),\n})\n\nconst upsertCustomEntityResponseSchema = z.object({\n ok: z.literal(true),\n item: z.object({\n id: z.string().uuid(),\n entityId: z.string(),\n label: z.string(),\n description: z.string().optional(),\n }),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Entities',\n summary: 'Manage custom entities',\n methods: {\n GET: {\n summary: 'List available entities',\n description: 'Returns generated and custom entities scoped to the caller with field counts per entity.',\n responses: [\n {\n status: 200,\n description: 'List of entities',\n schema: entityListResponseSchema,\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n POST: {\n summary: 'Upsert custom entity',\n description: 'Creates or updates a tenant/org scoped custom entity definition.',\n requestBody: {\n contentType: 'application/json',\n schema: upsertCustomEntitySchema,\n },\n responses: [\n {\n status: 200,\n description: 'Entity saved',\n schema: upsertCustomEntityResponseSchema,\n },\n {\n status: 400,\n description: 'Validation error',\n schema: z.object({\n error: z.string(),\n details: z.any().optional(),\n }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n DELETE: {\n summary: 'Soft delete custom entity',\n description: 'Marks the specified custom entity inactive within the current scope.',\n requestBody: {\n contentType: 'application/json',\n schema: deleteEntityRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Entity deleted',\n schema: z.object({ ok: z.boolean() }),\n },\n {\n status: 400,\n description: 'Missing entity id',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 404,\n description: 'Entity not found in scope',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,cAAc,sBAAsB;AAC7C,SAAS,oBAAoB;AAC7B,SAAS,gCAAgC;AAEzC,SAAS,gCAAgC;AACzC,SAAS,oCAAoC,iCAAiC;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { CustomEntity, CustomFieldDef } from '@open-mercato/core/modules/entities/data/entities'\nimport { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { upsertCustomEntitySchema } from '@open-mercato/core/modules/entities/data/validators'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { isSystemEntitySelectable } from '@open-mercato/shared/lib/entities/system-entities'\nimport { SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, isOrmBackedSystemEntityId } from '@open-mercato/shared/lib/data/engine'\nimport { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\n\nconst CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND = 'entities.entity'\n\nexport const metadata = {\n GET: { requireAuth: true },\n POST: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || (!auth.orgId && !auth.isSuperAdmin)) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n // Generated entities from code\n const AllEntities = getEntityIds()\n const generated: { entityId: string; source: 'code'; label: string }[] = []\n for (const modId of Object.keys(AllEntities)) {\n const entities = (AllEntities as any)[modId] as Record<string, string>\n for (const k of Object.keys(entities)) {\n const id = entities[k]\n if (!isSystemEntitySelectable(id)) continue\n generated.push({ entityId: id, source: 'code', label: id })\n }\n }\n\n // Custom user-defined entities (global/org/tenant scoped)\n const where: any = { isActive: true }\n where.$and = [\n { $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] },\n { $or: [ { tenantId: auth.tenantId ?? undefined as any }, { tenantId: null } ] },\n ]\n const customs = await em.find(CustomEntity as any, where as any, { orderBy: { entityId: 'asc' } as any })\n // Resolve overlay precedence: prefer organization/tenant-specific over global\n const customByEntityId = new Map<string, any>()\n for (const c of customs as any[]) {\n const specificity = (c.organizationId ? 2 : 0) + (c.tenantId ? 1 : 0)\n const prev = customByEntityId.get(c.entityId)\n const prevSpec = prev ? ((prev.organizationId ? 2 : 0) + (prev.tenantId ? 1 : 0)) : -1\n if (!prev || specificity > prevSpec) customByEntityId.set(c.entityId, c)\n }\n\n const custom = Array.from(customByEntityId.values())\n .filter((c) => isSystemEntitySelectable(c.entityId))\n .map((c) => ({\n entityId: c.entityId,\n source: 'custom' as const,\n label: c.label,\n description: c.description ?? undefined,\n labelField: (c as any).labelField ?? undefined,\n defaultEditor: (c as any).defaultEditor ?? undefined,\n showInSidebar: (c as any).showInSidebar ?? false,\n updatedAt: c.updatedAt instanceof Date ? c.updatedAt.toISOString() : (c.updatedAt ?? undefined),\n }))\n\n const byId = new Map<string, any>()\n for (const g of generated) byId.set(g.entityId, g)\n for (const cu of custom) {\n const existing = byId.get(cu.entityId)\n byId.set(cu.entityId, { ...existing, ...cu, source: existing?.source ?? cu.source })\n }\n\n // Count field definitions scoped to current tenant/org (same scoping as custom entities)\n const defsWhere: any = { isActive: true }\n defsWhere.$and = [\n //{ $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] }, // the entities and custom fields are defined per tenant\n { tenantId: auth.tenantId ?? undefined as any },\n ]\n const defs = await em.find(CustomFieldDef as any, defsWhere as any)\n // Count distinct field names (keys) per entityId\n const keySets = new Map<string, Set<string>>()\n for (const d of defs as any[]) {\n const eid = String(d.entityId)\n const k = String(d.key)\n if (!isSystemEntitySelectable(eid)) continue\n const set = keySets.get(eid) || new Set<string>()\n set.add(k)\n keySets.set(eid, set)\n }\n const counts: Record<string, number> = {}\n for (const [eid, set] of keySets.entries()) counts[eid] = set.size\n\n const items = Array.from(byId.values()).map((it: any) => ({ ...it, count: counts[it.entityId] || 0 }))\n return NextResponse.json({ items })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const parsed = upsertCustomEntitySchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Validation failed', details: parsed.error.flatten() }, { status: 400 })\n }\n const input = parsed.data\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n // A registration for a module-declared, table-backed system entity would flip\n // query-engine classification to doc storage for the whole entity type (#2939's\n // failure mode via another door) \u2014 refuse to create one.\n if (isOrmBackedSystemEntityId(em, input.entityId)) {\n return NextResponse.json(\n { error: 'System entities cannot be registered as custom entities', code: SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, entityId: input.entityId },\n { status: 400 },\n )\n }\n\n const where: any = { entityId: input.entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n let ent = await em.findOne(CustomEntity, where)\n if (ent) {\n try {\n enforceCommandOptimisticLock({\n resourceKind: CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND,\n resourceId: ent.id,\n current: ent.updatedAt ?? null,\n request: req,\n })\n } catch (lockError) {\n if (isCrudHttpError(lockError)) {\n return NextResponse.json(lockError.body, { status: lockError.status })\n }\n throw lockError\n }\n }\n if (!ent) ent = em.create(CustomEntity, { ...where, createdAt: new Date() })\n ent.label = input.label\n ent.description = input.description ?? null\n ent.isActive = input.isActive ?? true\n ent.labelField = input.labelField ?? ent.labelField ?? null\n ent.defaultEditor = input.defaultEditor ?? ent.defaultEditor ?? null\n ent.showInSidebar = input.showInSidebar ?? ent.showInSidebar ?? false\n ent.updatedAt = new Date()\n em.persist(ent)\n await em.flush()\n // Invalidate sidebar/nav cache for tenant scope (also when tenantId is null)\n try {\n const cache = (await createRequestContainer()).resolve('cache') as any\n if (cache) {\n await cache.deleteByTags([`nav:entities:${auth.tenantId || 'null'}`])\n }\n } catch {}\n return NextResponse.json({ ok: true, item: { id: ent.id, entityId: ent.entityId, label: ent.label, description: ent.description ?? undefined } })\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const entityId = body?.entityId\n if (!entityId) return NextResponse.json({ error: 'entityId is required' }, { status: 400 })\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n const where: any = { entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n const ent = await em.findOne(CustomEntity, where)\n if (!ent) return NextResponse.json({ error: 'Not found' }, { status: 404 })\n ent.isActive = false\n ent.updatedAt = new Date()\n ent.deletedAt = ent.deletedAt ?? new Date()\n em.persist(ent)\n await em.flush()\n // Invalidate sidebar/nav cache for tenant scope (also when tenantId is null)\n try {\n const cache = (await createRequestContainer()).resolve('cache') as any\n if (cache) {\n await cache.deleteByTags([`nav:entities:${auth.tenantId || 'null'}`])\n }\n } catch {}\n return NextResponse.json({ ok: true })\n}\n\nconst entitySummarySchema = z.object({\n entityId: z.string(),\n source: z.enum(['code', 'custom']),\n label: z.string(),\n description: z.string().optional(),\n labelField: z.string().optional(),\n defaultEditor: z.string().optional(),\n showInSidebar: z.boolean().optional(),\n updatedAt: z.string().optional(),\n count: z.number(),\n})\n\nconst entityListResponseSchema = z.object({\n items: z.array(entitySummarySchema),\n})\n\nconst deleteEntityRequestSchema = z.object({\n entityId: z.string(),\n})\n\nconst upsertCustomEntityResponseSchema = z.object({\n ok: z.literal(true),\n item: z.object({\n id: z.string().uuid(),\n entityId: z.string(),\n label: z.string(),\n description: z.string().optional(),\n }),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Entities',\n summary: 'Manage custom entities',\n methods: {\n GET: {\n summary: 'List available entities',\n description: 'Returns generated and custom entities scoped to the caller with field counts per entity.',\n responses: [\n {\n status: 200,\n description: 'List of entities',\n schema: entityListResponseSchema,\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n POST: {\n summary: 'Upsert custom entity',\n description: 'Creates or updates a tenant/org scoped custom entity definition.',\n requestBody: {\n contentType: 'application/json',\n schema: upsertCustomEntitySchema,\n },\n responses: [\n {\n status: 200,\n description: 'Entity saved',\n schema: upsertCustomEntityResponseSchema,\n },\n {\n status: 400,\n description: 'Validation error',\n schema: z.object({\n error: z.string(),\n details: z.any().optional(),\n }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n DELETE: {\n summary: 'Soft delete custom entity',\n description: 'Marks the specified custom entity inactive within the current scope.',\n requestBody: {\n contentType: 'application/json',\n schema: deleteEntityRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Entity deleted',\n schema: z.object({ ok: z.boolean() }),\n },\n {\n status: 400,\n description: 'Missing entity id',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 404,\n description: 'Entity not found in scope',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -153,6 +153,7 @@ function EditDefinitionsPage({ params }) {
|
|
|
153
153
|
const labelFieldValue = typeof record?.labelField === "string" && record.labelField.length > 0 ? record.labelField : "name";
|
|
154
154
|
const defaultEditorValue = typeof record?.defaultEditor === "string" ? record.defaultEditor : "";
|
|
155
155
|
const showInSidebarValue = record?.showInSidebar === true;
|
|
156
|
+
const updatedAtValue = typeof record?.updatedAt === "string" && record.updatedAt.length > 0 ? record.updatedAt : void 0;
|
|
156
157
|
setLabel(labelValue);
|
|
157
158
|
if (record?.source === "code" || record?.source === "custom") setEntitySource(record.source);
|
|
158
159
|
setEntityInitial({
|
|
@@ -160,7 +161,8 @@ function EditDefinitionsPage({ params }) {
|
|
|
160
161
|
description: descriptionValue,
|
|
161
162
|
labelField: labelFieldValue,
|
|
162
163
|
defaultEditor: defaultEditorValue,
|
|
163
|
-
showInSidebar: showInSidebarValue
|
|
164
|
+
showInSidebar: showInSidebarValue,
|
|
165
|
+
updatedAt: updatedAtValue
|
|
164
166
|
});
|
|
165
167
|
setEntityFormLoading(false);
|
|
166
168
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/entities/backend/entities/user/%5BentityId%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport React, { useEffect, useMemo, useState } from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { invalidateCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { upsertCustomEntitySchema, upsertCustomFieldDefSchema } from '@open-mercato/core/modules/entities/data/validators'\nimport { z } from 'zod'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'\nimport Link from 'next/link'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { loadGeneratedFieldRegistrations } from '@open-mercato/ui/backend/fields/registry'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { createCrudFormError, raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { FieldDefinitionsEditor, type FieldDefinition, type FieldDefinitionError } from '@open-mercato/ui/backend/custom-fields/FieldDefinitionsEditor'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { normalizeCustomFieldOptions } from '@open-mercato/shared/modules/entities/options'\nimport { TranslationManager } from '@open-mercato/core/modules/translations/components/TranslationManager'\n\ntype Def = FieldDefinition\ntype EntitiesListResponse = { items?: Array<Record<string, unknown>> }\ntype FieldsetGroup = { code: string; title?: string; hint?: string }\ntype FieldsetDefinition = { code: string; label: string; icon?: string; description?: string; groups?: FieldsetGroup[] }\ntype DefinitionsManageResponse = { items?: any[]; deletedKeys?: string[]; fieldsets?: FieldsetDefinition[]; settings?: { singleFieldsetPerRecord?: boolean } }\n\ntype DefErrors = FieldDefinitionError\n\n\nexport default function EditDefinitionsPage({ params }: { params?: { entityId?: string } }) {\n React.useEffect(() => { loadGeneratedFieldRegistrations().catch(() => {}) }, [])\n const router = useRouter()\n const searchParams = useSearchParams()\n const t = useT()\n const queryClient = useQueryClient()\n const entityId = useMemo(() => decodeURIComponent((params?.entityId as any) || ''), [params])\n const [label, setLabel] = useState('')\n const [entitySource, setEntitySource] = useState<'code'|'custom'>('custom')\n const [entityFormLoading, setEntityFormLoading] = useState(true)\n const [entityInitial, setEntityInitial] = useState<{ label?: string; description?: string; labelField?: string; defaultEditor?: string; showInSidebar?: boolean }>({})\n const [defs, setDefs] = useState<Def[]>([])\n const [orderDirty, setOrderDirty] = useState(false)\n const [orderSaving, setOrderSaving] = useState(false)\n const listRef = React.useRef<HTMLDivElement | null>(null)\n const [loading, setLoading] = useState(true)\n const [saving, setSaving] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [deletedKeys, setDeletedKeys] = useState<string[]>([])\n const [defErrors, setDefErrors] = useState<Record<number, DefErrors>>({})\n const [fieldsets, setFieldsets] = useState<FieldsetDefinition[]>([])\n const [activeFieldset, setActiveFieldset] = useState<string | null>(null)\n const [singleFieldsetPerRecord, setSingleFieldsetPerRecord] = useState(true)\n const [translateDef, setTranslateDef] = useState<{ def: Def; entityId: string } | null>(null)\n\n const translateFields = React.useMemo(() => {\n if (!translateDef) return undefined\n const { def } = translateDef\n const fields: string[] = ['label', 'description']\n const options = normalizeCustomFieldOptions(def.configJson?.options)\n for (const opt of options) {\n if (opt.value) fields.push(`options.${opt.value}.label`)\n }\n return fields\n }, [translateDef])\n\n const translateBaseValues = React.useMemo(() => {\n if (!translateDef) return undefined\n const { def } = translateDef\n const base: Record<string, string> = {}\n if (typeof def.configJson?.label === 'string') base.label = def.configJson.label\n if (typeof def.configJson?.description === 'string') base.description = def.configJson.description\n const options = normalizeCustomFieldOptions(def.configJson?.options)\n for (const opt of options) {\n if (opt.value && opt.label) base[`options.${opt.value}.label`] = opt.label\n }\n return base\n }, [translateDef])\n\n const requestedFieldset = React.useMemo(() => {\n const raw = searchParams?.get('fieldset')\n return raw && raw.trim().length ? raw.trim() : null\n }, [searchParams])\n const embedFieldsetView = React.useMemo(() => searchParams?.get('view') === 'fieldset', [searchParams])\n const normalizeGroupPayload = React.useCallback((value: unknown) => {\n if (!value) return null\n if (typeof value === 'string') {\n const code = value.trim()\n return code ? { code } : null\n }\n if (typeof value !== 'object') return null\n const entry = value as Record<string, unknown>\n const code = typeof entry.code === 'string' ? entry.code.trim() : ''\n if (!code) return null\n const group: FieldsetGroup = { code }\n if (typeof entry.title === 'string' && entry.title.trim()) group.title = entry.title.trim()\n if (typeof entry.hint === 'string' && entry.hint.trim()) group.hint = entry.hint.trim()\n return group\n }, [])\n\n const buildFieldsetPayload = React.useCallback(() => {\n const groupMap = new Map<string, FieldsetGroup[]>()\n defs.forEach((definition) => {\n const code = typeof definition.configJson?.fieldset === 'string' ? definition.configJson.fieldset : null\n if (!code) return\n const normalized = normalizeGroupPayload(definition.configJson?.group)\n if (!normalized) return\n const list = groupMap.get(code) ?? []\n if (!list.some((entry) => entry.code === normalized.code)) {\n list.push(normalized)\n groupMap.set(code, list)\n }\n })\n return fieldsets.map((fs) => ({\n ...fs,\n groups: groupMap.get(fs.code) ?? [],\n }))\n }, [defs, fieldsets, normalizeGroupPayload])\n\n const validateDef = React.useCallback((d: Def): DefErrors => {\n const parsed = upsertCustomFieldDefSchema.safeParse({ entityId, key: d.key, kind: d.kind, configJson: d.configJson, isActive: d.isActive })\n if (parsed.success) return {}\n const errs: DefErrors = {}\n for (const issue of parsed.error.issues) {\n if ((issue.path || []).includes('key')) errs.key = issue.message\n if ((issue.path || []).includes('kind')) errs.kind = issue.message\n }\n return errs\n }, [entityId, requestedFieldset])\n\n const validateAndSetErrorAt = (index: number, d: Def) => {\n const errs = validateDef(d)\n setDefErrors((prev) => ({ ...prev, [index]: errs }))\n return !errs.key && !errs.kind\n }\n\n const validateAll = () => {\n const nextErrors: Record<number, DefErrors> = {}\n defs.forEach((d, i) => {\n nextErrors[i] = validateDef(d)\n })\n setDefErrors(nextErrors)\n return Object.values(nextErrors).every(e => !e.key && !e.kind)\n }\n\n useEffect(() => {\n let mounted = true\n async function load() {\n setLoading(true)\n try {\n const entJson = await readApiResultOrThrow<EntitiesListResponse>(\n '/api/entities/entities',\n undefined,\n { errorMessage: 'Failed to load entity metadata', fallback: { items: [] } },\n )\n const ent = (entJson.items || []).find((x: any) => x.entityId === entityId)\n if (mounted) {\n const record = ent as Record<string, unknown> | undefined\n const labelValue =\n typeof record?.label === 'string' && record.label.trim().length > 0 ? record.label : entityId\n const descriptionValue = typeof record?.description === 'string' ? record.description : ''\n const labelFieldValue =\n typeof record?.labelField === 'string' && record.labelField.length > 0 ? record.labelField : 'name'\n const defaultEditorValue =\n typeof record?.defaultEditor === 'string' ? record.defaultEditor : ''\n const showInSidebarValue = record?.showInSidebar === true\n setLabel(labelValue)\n if (record?.source === 'code' || record?.source === 'custom') setEntitySource(record.source)\n setEntityInitial({\n label: labelValue,\n description: descriptionValue,\n labelField: labelFieldValue,\n defaultEditor: defaultEditorValue,\n showInSidebar: showInSidebarValue,\n })\n setEntityFormLoading(false)\n }\n const json = await readApiResultOrThrow<DefinitionsManageResponse>(\n `/api/entities/definitions.manage?entityId=${encodeURIComponent(entityId)}`,\n undefined,\n { errorMessage: 'Failed to load entity definitions', fallback: { items: [], deletedKeys: [] } },\n )\n if (mounted) {\n const loaded: Def[] = (json.items || []).map((d: any) => ({ key: d.key, kind: d.kind, configJson: d.configJson || {}, isActive: d.isActive !== false }))\n loaded.sort(\n (a, b) => Number(a.configJson?.priority ?? 0) - Number(b.configJson?.priority ?? 0)\n )\n setDefs(loaded)\n setDefErrors({})\n setDeletedKeys(Array.isArray(json.deletedKeys) ? json.deletedKeys : [])\n const loadedFieldsets = Array.isArray(json.fieldsets) ? json.fieldsets : []\n setFieldsets(loadedFieldsets)\n setActiveFieldset((prev) => {\n if (requestedFieldset && loadedFieldsets.some((fs) => fs.code === requestedFieldset)) {\n return requestedFieldset\n }\n if (prev && loadedFieldsets.some((fs) => fs.code === prev)) return prev\n return loadedFieldsets[0]?.code ?? null\n })\n setSingleFieldsetPerRecord(json.settings?.singleFieldsetPerRecord !== false)\n }\n } catch (e: any) {\n if (mounted) setError(e.message || 'Failed to load')\n } finally {\n if (mounted) setLoading(false)\n }\n }\n if (entityId) load()\n return () => { mounted = false }\n }, [entityId])\n\n function addField() {\n setDefs((arr) => [\n ...arr,\n {\n key: '',\n kind: 'text',\n configJson: activeFieldset ? { fieldset: activeFieldset } : {},\n isActive: true,\n },\n ])\n }\n\n async function restoreField(key: string) {\n try {\n const call = await apiCall('/api/entities/definitions.restore', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, 'Failed to restore field')\n }\n // Reload definitions & deleted keys\n const j2 = await readApiResultOrThrow<DefinitionsManageResponse>(\n `/api/entities/definitions.manage?entityId=${encodeURIComponent(entityId)}`,\n undefined,\n { errorMessage: 'Failed to reload field definitions', fallback: { items: [], deletedKeys: [] } },\n )\n const loaded: Def[] = (j2.items || []).map((d: any) => ({ key: d.key, kind: d.kind, configJson: d.configJson || {}, isActive: d.isActive !== false }))\n loaded.sort(\n (a, b) => Number(a.configJson?.priority ?? 0) - Number(b.configJson?.priority ?? 0)\n )\n setDefs(loaded)\n setDeletedKeys(Array.isArray(j2.deletedKeys) ? j2.deletedKeys : [])\n flash(`Restored ${key}`, 'success')\n await invalidateCustomFieldDefs(queryClient, entityId)\n } catch (e: any) {\n flash(e?.message || 'Failed to restore field', 'error')\n }\n }\n\n async function saveAll() {\n setSaving(true)\n setError(null)\n try {\n if (!validateAll()) {\n flash('Please fix validation errors in field definitions', 'error')\n throw new Error('Validation failed')\n }\n const payload = {\n entityId,\n definitions: defs.filter(d => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n }\n const call = await apiCall('/api/entities/definitions.batch', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, 'Failed to save definitions')\n }\n await invalidateCustomFieldDefs(queryClient, entityId)\n router.push(`/backend/entities/user?flash=Definitions%20saved&type=success`)\n } catch (e: any) {\n setError(e.message || 'Failed to save')\n } finally {\n setSaving(false)\n }\n }\n\n async function removeField(idx: number) {\n const def = defs[idx]\n if (!def) return\n if (def.key) {\n try {\n const call = await apiCall('/api/entities/definitions', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key: def.key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, 'Failed to delete field')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to delete field'\n flash(message, 'error')\n return\n }\n }\n setDefs((arr) => arr.filter((_, i) => i !== idx))\n setOrderDirty(true)\n if (def.key) {\n await invalidateCustomFieldDefs(queryClient, entityId)\n }\n }\n\n const handleFieldsetCodeChange = React.useCallback((previousCode: string, nextCode: string) => {\n if (!previousCode || !nextCode || previousCode === nextCode) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== previousCode) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n nextConfig.fieldset = nextCode\n return { ...entry, configJson: nextConfig }\n })\n )\n setActiveFieldset((current) => (current === previousCode ? nextCode : current))\n }, [])\n\n const handleFieldsetRemoved = React.useCallback((code: string) => {\n if (!code) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== code) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n delete nextConfig.fieldset\n delete nextConfig.group\n return { ...entry, configJson: nextConfig }\n })\n )\n }, [])\n\n async function saveOrderIfDirty() {\n if (!orderDirty) return\n setOrderSaving(true)\n try {\n // Do not save order when there are invalid keys/kinds\n if (!validateAll()) throw new Error('Validation failed')\n const payload = {\n entityId,\n definitions: defs.filter(d => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n }\n const call = await apiCall('/api/entities/definitions.batch', {\n method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload)\n })\n if (!call.ok) {\n await raiseCrudError(call.response, 'Failed to save order')\n }\n setOrderDirty(false)\n flash('Order saved', 'success')\n await invalidateCustomFieldDefs(queryClient, entityId)\n } catch (e: any) {\n flash(e?.message || 'Failed to save order', 'error')\n } finally {\n setOrderSaving(false)\n }\n }\n\n // Unify loader via CrudForm isLoading; do not return early here\n\n // Schema for inline field-level validation in CrudForm\n const entityFormSchema = upsertCustomEntitySchema\n .pick({ label: true, description: true, defaultEditor: true as any })\n .extend({\n // Allow empty string in the UI select, treat as undefined later\n defaultEditor: z.union([z.enum(['markdown','simpleMarkdown','htmlRichText']).optional(), z.literal('')]).optional(),\n // Include showInSidebar so CrudForm doesn't strip it on submit\n showInSidebar: z.boolean().optional(),\n }) as z.ZodType<Record<string, unknown>>\n\n const fields: CrudField[] = [\n { id: 'label', label: 'Label', type: 'text', required: true },\n { id: 'description', label: 'Description', type: 'textarea' },\n {\n id: 'defaultEditor',\n label: 'Default Editor (multiline)',\n type: 'select',\n options: [\n { value: '', label: 'Default (Markdown)' },\n { value: 'markdown', label: 'Markdown (UIW)' },\n { value: 'simpleMarkdown', label: 'Simple Markdown' },\n { value: 'htmlRichText', label: 'HTML Rich Text' },\n ],\n } as any,\n ...(entitySource === 'custom' ? [{ id: 'showInSidebar', label: 'Show in sidebar', type: 'checkbox' }] : []),\n ]\n const renderFieldDefinitions = React.useCallback(() => (\n <FieldDefinitionsEditor\n definitions={defs}\n errors={defErrors}\n deletedKeys={deletedKeys}\n fieldsets={fieldsets}\n activeFieldset={activeFieldset}\n onActiveFieldsetChange={setActiveFieldset}\n onFieldsetsChange={(next) => {\n setFieldsets(next)\n if (!next.some((fs) => fs.code === activeFieldset)) {\n setActiveFieldset(next[0]?.code ?? null)\n }\n }}\n onFieldsetCodeChange={handleFieldsetCodeChange}\n onFieldsetRemoved={handleFieldsetRemoved}\n singleFieldsetPerRecord={singleFieldsetPerRecord}\n onSingleFieldsetPerRecordChange={setSingleFieldsetPerRecord}\n onAddField={addField}\n onRemoveField={(index) => { void removeField(index) }}\n onDefinitionChange={(index, nextDef) => {\n setDefs((arr) => arr.map((entry, idx) => (idx === index ? nextDef : entry)))\n validateAndSetErrorAt(index, nextDef)\n }}\n onTranslate={(def) => setTranslateDef({ def, entityId })}\n onRestoreField={(key) => { void restoreField(key) }}\n onReorder={(from, to) => {\n setDefs((arr) => {\n const next = [...arr]\n const [moved] = next.splice(from, 1)\n next.splice(to, 0, moved)\n return next\n })\n setOrderDirty(true)\n }}\n orderNotice={orderDirty ? { dirty: true, saving: orderSaving, message: 'Reordered \u2014 will auto-save on blur' } : undefined}\n addButtonLabel=\"Add Field\"\n translate={t}\n listRef={listRef}\n listProps={{\n tabIndex: -1,\n onBlur: (event) => {\n const current = listRef.current\n const next = event.relatedTarget as Node | null\n if (!current) return\n if (!next || !current.contains(next)) {\n void saveOrderIfDirty()\n }\n },\n }}\n />\n ),\n [defs, defErrors, deletedKeys, fieldsets, activeFieldset, singleFieldsetPerRecord, orderDirty, orderSaving, addField, removeField, restoreField, saveOrderIfDirty])\n\n const definitionsGroup: CrudFormGroup = { id: 'definitions', title: 'Field Definitions', column: 1, component: renderFieldDefinitions }\n\n const groups: CrudFormGroup[] = [\n { id: 'settings', title: 'Entity Settings', column: 1, fields: entitySource === 'custom' ? ['label','description','defaultEditor','showInSidebar'] : ['label','description','defaultEditor'] },\n definitionsGroup,\n ]\n\n const handleCrudFormSubmit = React.useCallback(async (vals: Record<string, unknown>) => {\n if (!entityId) {\n throw createCrudFormError('Invalid entity ID')\n }\n if (!validateAll()) {\n flash('Please fix validation errors in field definitions', 'error')\n throw createCrudFormError('Please fix validation errors in field definitions')\n }\n {\n const entityPayload = buildEntityMetadataPayload(entitySource, vals)\n if (!entityPayload) throw createCrudFormError('Validation failed')\n const callEntity = await apiCall('/api/entities/entities', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, ...entityPayload }),\n })\n if (!callEntity.ok) {\n await raiseCrudError(callEntity.response, 'Failed to save entity')\n }\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n }\n const defsPayload = {\n entityId,\n definitions: defs.filter((d) => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n fieldsets: buildFieldsetPayload(),\n singleFieldsetPerRecord,\n }\n const callDefs = await apiCall('/api/entities/definitions.batch', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(defsPayload),\n })\n if (!callDefs.ok) {\n await raiseCrudError(callDefs.response, 'Failed to save definitions')\n }\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n await invalidateCustomFieldDefs(queryClient, entityId)\n flash('Definitions saved', 'success')\n }, [buildFieldsetPayload, defs, entityId, entitySource, queryClient, singleFieldsetPerRecord, validateAll])\n\n if (!entityId) {\n return (\n <Page>\n <PageBody>\n <div className=\"p-6\">\n <ErrorNotice title=\"Invalid entity\" message=\"The requested entity ID is missing or invalid.\" />\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (embedFieldsetView) {\n return (\n <div className=\"p-4\">\n <CrudForm\n schema={entityFormSchema}\n title={`Edit fieldset: ${requestedFieldset ?? entityId}`}\n fields={[]}\n groups={[definitionsGroup]}\n initialValues={entityInitial as any}\n isLoading={entityFormLoading || loading}\n submitLabel=\"Save\"\n deleteVisible={false}\n backHref={undefined}\n cancelHref={undefined}\n embedded\n onSubmit={handleCrudFormSubmit}\n />\n </div>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n schema={entityFormSchema}\n title={`Edit Entity: ${entityId}`}\n backHref={entitySource === 'code' ? \"/backend/entities/system\" : \"/backend/entities/user\"}\n fields={fields}\n groups={groups}\n initialValues={entityInitial as any}\n isLoading={entityFormLoading || loading}\n submitLabel=\"Save\"\n deleteVisible={entitySource === 'custom'}\n extraActions={entitySource === 'custom' ? (\n <Button variant=\"outline\" asChild>\n <Link href={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}>\n Show Records\n </Link>\n </Button>\n ) : null}\n cancelHref={entitySource === 'code' ? \"/backend/entities/system\" : \"/backend/entities/user\"}\n successRedirect={entitySource === 'code' ? \"/backend/entities/system?flash=Definitions%20saved&type=success\" : \"/backend/entities/user?flash=Definitions%20saved&type=success\"}\n onSubmit={handleCrudFormSubmit}\n onDelete={entitySource === 'custom' ? async () => {\n const callDelete = await apiCall('/api/entities/entities', { method: 'DELETE', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ entityId }) })\n if (!callDelete.ok) {\n await raiseCrudError(callDelete.response, 'Failed to delete entity')\n }\n flash('Entity deleted', 'success')\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n } : undefined}\n />\n </PageBody>\n <Dialog open={!!translateDef} onOpenChange={(open) => { if (!open) setTranslateDef(null) }}>\n <DialogContent className=\"max-w-2xl max-h-[80vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>{t('translations.manager.translateField', 'Translate field: {{key}}', { key: translateDef?.def.key ?? '' })}</DialogTitle>\n </DialogHeader>\n {translateDef && (\n <TranslationManager\n mode=\"embedded\"\n compact\n entityType=\"entities:custom_field_def\"\n recordId={`${translateDef.entityId}:${translateDef.def.key}`}\n baseValues={translateBaseValues}\n translatableFields={translateFields}\n />\n )}\n </DialogContent>\n </Dialog>\n </Page>\n )\n}\n\nexport function buildEntityMetadataPayload(\n entitySource: 'code' | 'custom',\n vals: Record<string, unknown>,\n): Record<string, unknown> | null {\n const partial = entitySource === 'custom'\n ? upsertCustomEntitySchema\n .pick({ label: true, description: true, labelField: true as any, defaultEditor: true as any })\n .extend({ showInSidebar: z.boolean().optional() }) as unknown as z.ZodTypeAny\n : upsertCustomEntitySchema\n .pick({ label: true, description: true, defaultEditor: true as any }) as unknown as z.ZodTypeAny\n const normalized = {\n ...vals,\n defaultEditor: typeof vals.defaultEditor === 'string' && vals.defaultEditor ? vals.defaultEditor : undefined,\n }\n const parsed = partial.safeParse(normalized)\n return parsed.success ? (parsed.data as Record<string, unknown>) : null\n}\n"],
|
|
5
|
-
"mappings": ";AAuZM,cA4KE,YA5KF;AAtZN,OAAO,SAAS,WAAW,SAAS,gBAAgB;AACpD,SAAS,WAAW,uBAAuB;AAC3C,SAAS,sBAAsB;AAC/B,SAAS,gBAAoD;AAC7D,SAAS,aAAa;AACtB,SAAS,SAAS,4BAA4B;AAC9C,SAAS,iCAAiC;AAC1C,SAAS,0BAA0B,kCAAkC;AACrE,SAAS,SAAS;AAClB,SAAS,MAAM,gBAAgB;AAC/B,SAAS,mBAAmB;AAC5B,OAAO,UAAU;AACjB,SAAS,cAAc;AACvB,SAAS,uCAAuC;AAEhD,SAAS,qBAAqB,sBAAsB;AACpD,SAAS,8BAA+E;AACxF,SAAS,YAAY;AACrB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mCAAmC;AAC5C,SAAS,0BAA0B;AAWpB,SAAR,oBAAqC,EAAE,OAAO,GAAuC;AAC1F,QAAM,UAAU,MAAM;AAAE,oCAAgC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAAE,GAAG,CAAC,CAAC;AAC/E,QAAM,SAAS,UAAU;AACzB,QAAM,eAAe,gBAAgB;AACrC,QAAM,IAAI,KAAK;AACf,QAAM,cAAc,eAAe;AACnC,QAAM,WAAW,QAAQ,MAAM,mBAAoB,QAAQ,YAAoB,EAAE,GAAG,CAAC,MAAM,CAAC;AAC5F,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,QAAM,CAAC,cAAc,eAAe,IAAI,SAA0B,QAAQ;AAC1E,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,IAAI;AAC/D,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAyH,CAAC,CAAC;AACrK,QAAM,CAAC,MAAM,OAAO,IAAI,SAAgB,CAAC,CAAC;AAC1C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AACpD,QAAM,UAAU,MAAM,OAA8B,IAAI;AACxD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,IAAI;AAC3C,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAmB,CAAC,CAAC;AAC3D,QAAM,CAAC,WAAW,YAAY,IAAI,SAAoC,CAAC,CAAC;AACxE,QAAM,CAAC,WAAW,YAAY,IAAI,SAA+B,CAAC,CAAC;AACnE,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAwB,IAAI;AACxE,QAAM,CAAC,yBAAyB,0BAA0B,IAAI,SAAS,IAAI;AAC3E,QAAM,CAAC,cAAc,eAAe,IAAI,SAAgD,IAAI;AAE5F,QAAM,kBAAkB,MAAM,QAAQ,MAAM;AAC1C,QAAI,CAAC,aAAc,QAAO;AAC1B,UAAM,EAAE,IAAI,IAAI;AAChB,UAAMA,UAAmB,CAAC,SAAS,aAAa;AAChD,UAAM,UAAU,4BAA4B,IAAI,YAAY,OAAO;AACnE,eAAW,OAAO,SAAS;AACzB,UAAI,IAAI,MAAO,CAAAA,QAAO,KAAK,WAAW,IAAI,KAAK,QAAQ;AAAA,IACzD;AACA,WAAOA;AAAA,EACT,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,sBAAsB,MAAM,QAAQ,MAAM;AAC9C,QAAI,CAAC,aAAc,QAAO;AAC1B,UAAM,EAAE,IAAI,IAAI;AAChB,UAAM,OAA+B,CAAC;AACtC,QAAI,OAAO,IAAI,YAAY,UAAU,SAAU,MAAK,QAAQ,IAAI,WAAW;AAC3E,QAAI,OAAO,IAAI,YAAY,gBAAgB,SAAU,MAAK,cAAc,IAAI,WAAW;AACvF,UAAM,UAAU,4BAA4B,IAAI,YAAY,OAAO;AACnE,eAAW,OAAO,SAAS;AACzB,UAAI,IAAI,SAAS,IAAI,MAAO,MAAK,WAAW,IAAI,KAAK,QAAQ,IAAI,IAAI;AAAA,IACvE;AACA,WAAO;AAAA,EACT,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,oBAAoB,MAAM,QAAQ,MAAM;AAC5C,UAAM,MAAM,cAAc,IAAI,UAAU;AACxC,WAAO,OAAO,IAAI,KAAK,EAAE,SAAS,IAAI,KAAK,IAAI;AAAA,EACjD,GAAG,CAAC,YAAY,CAAC;AACjB,QAAM,oBAAoB,MAAM,QAAQ,MAAM,cAAc,IAAI,MAAM,MAAM,YAAY,CAAC,YAAY,CAAC;AACtG,QAAM,wBAAwB,MAAM,YAAY,CAAC,UAAmB;AAClE,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,OAAO,UAAU,UAAU;AAC7B,YAAMC,QAAO,MAAM,KAAK;AACxB,aAAOA,QAAO,EAAE,MAAAA,MAAK,IAAI;AAAA,IAC3B;AACA,QAAI,OAAO,UAAU,SAAU,QAAO;AACtC,UAAM,QAAQ;AACd,UAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,KAAK,KAAK,IAAI;AAClE,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,QAAuB,EAAE,KAAK;AACpC,QAAI,OAAO,MAAM,UAAU,YAAY,MAAM,MAAM,KAAK,EAAG,OAAM,QAAQ,MAAM,MAAM,KAAK;AAC1F,QAAI,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,KAAK,EAAG,OAAM,OAAO,MAAM,KAAK,KAAK;AACtF,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AAEL,QAAM,uBAAuB,MAAM,YAAY,MAAM;AACnD,UAAM,WAAW,oBAAI,IAA6B;AAClD,SAAK,QAAQ,CAAC,eAAe;AAC3B,YAAM,OAAO,OAAO,WAAW,YAAY,aAAa,WAAW,WAAW,WAAW,WAAW;AACpG,UAAI,CAAC,KAAM;AACX,YAAM,aAAa,sBAAsB,WAAW,YAAY,KAAK;AACrE,UAAI,CAAC,WAAY;AACjB,YAAM,OAAO,SAAS,IAAI,IAAI,KAAK,CAAC;AACpC,UAAI,CAAC,KAAK,KAAK,CAAC,UAAU,MAAM,SAAS,WAAW,IAAI,GAAG;AACzD,aAAK,KAAK,UAAU;AACpB,iBAAS,IAAI,MAAM,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AACD,WAAO,UAAU,IAAI,CAAC,QAAQ;AAAA,MAC5B,GAAG;AAAA,MACH,QAAQ,SAAS,IAAI,GAAG,IAAI,KAAK,CAAC;AAAA,IACpC,EAAE;AAAA,EACJ,GAAG,CAAC,MAAM,WAAW,qBAAqB,CAAC;AAE3C,QAAM,cAAc,MAAM,YAAY,CAAC,MAAsB;AAC3D,UAAM,SAAS,2BAA2B,UAAU,EAAE,UAAU,KAAK,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,EAAE,YAAY,UAAU,EAAE,SAAS,CAAC;AAC1I,QAAI,OAAO,QAAS,QAAO,CAAC;AAC5B,UAAM,OAAkB,CAAC;AACzB,eAAW,SAAS,OAAO,MAAM,QAAQ;AACvC,WAAK,MAAM,QAAQ,CAAC,GAAG,SAAS,KAAK,EAAG,MAAK,MAAM,MAAM;AACzD,WAAK,MAAM,QAAQ,CAAC,GAAG,SAAS,MAAM,EAAG,MAAK,OAAO,MAAM;AAAA,IAC7D;AACA,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,iBAAiB,CAAC;AAEhC,QAAM,wBAAwB,CAAC,OAAe,MAAW;AACvD,UAAM,OAAO,YAAY,CAAC;AAC1B,iBAAa,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,KAAK,GAAG,KAAK,EAAE;AACnD,WAAO,CAAC,KAAK,OAAO,CAAC,KAAK;AAAA,EAC5B;AAEA,QAAM,cAAc,MAAM;AACxB,UAAM,aAAwC,CAAC;AAC/C,SAAK,QAAQ,CAAC,GAAG,MAAM;AACrB,iBAAW,CAAC,IAAI,YAAY,CAAC;AAAA,IAC/B,CAAC;AACD,iBAAa,UAAU;AACvB,WAAO,OAAO,OAAO,UAAU,EAAE,MAAM,OAAK,CAAC,EAAE,OAAO,CAAC,EAAE,IAAI;AAAA,EAC/D;AAEA,YAAU,MAAM;AACd,QAAI,UAAU;AACd,mBAAe,OAAO;AACpB,iBAAW,IAAI;AACf,UAAI;AACF,cAAM,UAAU,MAAM;AAAA,UACpB;AAAA,UACA;AAAA,UACA,EAAE,cAAc,kCAAkC,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,QAC5E;AACA,cAAM,OAAO,QAAQ,SAAS,CAAC,GAAG,KAAK,CAAC,MAAW,EAAE,aAAa,QAAQ;AAC1E,YAAI,SAAS;AACX,gBAAM,SAAS;AACf,gBAAM,aACJ,OAAO,QAAQ,UAAU,YAAY,OAAO,MAAM,KAAK,EAAE,SAAS,IAAI,OAAO,QAAQ;AACvF,gBAAM,mBAAmB,OAAO,QAAQ,gBAAgB,WAAW,OAAO,cAAc;AACxF,gBAAM,kBACJ,OAAO,QAAQ,eAAe,YAAY,OAAO,WAAW,SAAS,IAAI,OAAO,aAAa;AAC/F,gBAAM,qBACJ,OAAO,QAAQ,kBAAkB,WAAW,OAAO,gBAAgB;AACrE,gBAAM,qBAAqB,QAAQ,kBAAkB;AACrD,mBAAS,UAAU;AACnB,cAAI,QAAQ,WAAW,UAAU,QAAQ,WAAW,SAAU,iBAAgB,OAAO,MAAM;AAC3F,2BAAiB;AAAA,YACf,OAAO;AAAA,YACP,aAAa;AAAA,YACb,YAAY;AAAA,YACZ,eAAe;AAAA,YACf,eAAe;AAAA,UACjB,CAAC;AACD,+BAAqB,KAAK;AAAA,QAC5B;AACA,cAAM,OAAO,MAAM;AAAA,UACjB,6CAA6C,mBAAmB,QAAQ,CAAC;AAAA,UACzE;AAAA,UACA,EAAE,cAAc,qCAAqC,UAAU,EAAE,OAAO,CAAC,GAAG,aAAa,CAAC,EAAE,EAAE;AAAA,QAChG;AACA,YAAI,SAAS;AACX,gBAAM,UAAiB,KAAK,SAAS,CAAC,GAAG,IAAI,CAAC,OAAY,EAAE,KAAK,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,EAAE,cAAc,CAAC,GAAG,UAAU,EAAE,aAAa,MAAM,EAAE;AACvJ,iBAAO;AAAA,YACL,CAAC,GAAG,MAAM,OAAO,EAAE,YAAY,YAAY,CAAC,IAAI,OAAO,EAAE,YAAY,YAAY,CAAC;AAAA,UACpF;AACA,kBAAQ,MAAM;AACd,uBAAa,CAAC,CAAC;AACf,yBAAe,MAAM,QAAQ,KAAK,WAAW,IAAI,KAAK,cAAc,CAAC,CAAC;AACtE,gBAAM,kBAAkB,MAAM,QAAQ,KAAK,SAAS,IAAI,KAAK,YAAY,CAAC;AAC1E,uBAAa,eAAe;AAC5B,4BAAkB,CAAC,SAAS;AAC1B,gBAAI,qBAAqB,gBAAgB,KAAK,CAAC,OAAO,GAAG,SAAS,iBAAiB,GAAG;AACpF,qBAAO;AAAA,YACT;AACA,gBAAI,QAAQ,gBAAgB,KAAK,CAAC,OAAO,GAAG,SAAS,IAAI,EAAG,QAAO;AACnE,mBAAO,gBAAgB,CAAC,GAAG,QAAQ;AAAA,UACrC,CAAC;AACD,qCAA2B,KAAK,UAAU,4BAA4B,KAAK;AAAA,QAC7E;AAAA,MACF,SAAS,GAAQ;AACf,YAAI,QAAS,UAAS,EAAE,WAAW,gBAAgB;AAAA,MACrD,UAAE;AACA,YAAI,QAAS,YAAW,KAAK;AAAA,MAC/B;AAAA,IACF;AACA,QAAI,SAAU,MAAK;AACnB,WAAO,MAAM;AAAE,gBAAU;AAAA,IAAM;AAAA,EACjC,GAAG,CAAC,QAAQ,CAAC;AAEb,WAAS,WAAW;AAClB,YAAQ,CAAC,QAAQ;AAAA,MACf,GAAG;AAAA,MACH;AAAA,QACE,KAAK;AAAA,QACL,MAAM;AAAA,QACN,YAAY,iBAAiB,EAAE,UAAU,eAAe,IAAI,CAAC;AAAA,QAC7D,UAAU;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH;AAEA,iBAAe,aAAa,KAAa;AACvC,QAAI;AACF,YAAM,OAAO,MAAM,QAAQ,qCAAqC;AAAA,QAC9D,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,IAAI,CAAC;AAAA,MACxC,CAAC;AACD,UAAI,CAAC,KAAK,IAAI;AACZ,cAAM,eAAe,KAAK,UAAU,yBAAyB;AAAA,MAC/D;AAEA,YAAM,KAAK,MAAM;AAAA,QACf,6CAA6C,mBAAmB,QAAQ,CAAC;AAAA,QACzE;AAAA,QACA,EAAE,cAAc,sCAAsC,UAAU,EAAE,OAAO,CAAC,GAAG,aAAa,CAAC,EAAE,EAAE;AAAA,MACjG;AACA,YAAM,UAAiB,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,OAAY,EAAE,KAAK,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,EAAE,cAAc,CAAC,GAAG,UAAU,EAAE,aAAa,MAAM,EAAE;AACrJ,aAAO;AAAA,QACL,CAAC,GAAG,MAAM,OAAO,EAAE,YAAY,YAAY,CAAC,IAAI,OAAO,EAAE,YAAY,YAAY,CAAC;AAAA,MACpF;AACA,cAAQ,MAAM;AACd,qBAAe,MAAM,QAAQ,GAAG,WAAW,IAAI,GAAG,cAAc,CAAC,CAAC;AAClE,YAAM,YAAY,GAAG,IAAI,SAAS;AAClC,YAAM,0BAA0B,aAAa,QAAQ;AAAA,IACvD,SAAS,GAAQ;AACf,YAAM,GAAG,WAAW,2BAA2B,OAAO;AAAA,IACxD;AAAA,EACF;AAEA,iBAAe,UAAU;AACvB,cAAU,IAAI;AACd,aAAS,IAAI;AACb,QAAI;AACF,UAAI,CAAC,YAAY,GAAG;AAClB,cAAM,qDAAqD,OAAO;AAClE,cAAM,IAAI,MAAM,mBAAmB;AAAA,MACrC;AACA,YAAM,UAAU;AAAA,QACd;AAAA,QACA,aAAa,KAAK,OAAO,OAAK,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO;AAAA,UACjD,KAAK,EAAE;AAAA,UACP,MAAM,EAAE;AAAA,UACR,YAAY,EAAE;AAAA,UACd,UAAU,EAAE,aAAa;AAAA,QAC3B,EAAE;AAAA,MACJ;AACA,YAAM,OAAO,MAAM,QAAQ,mCAAmC;AAAA,QAC5D,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,MAC9B,CAAC;AACD,UAAI,CAAC,KAAK,IAAI;AACZ,cAAM,eAAe,KAAK,UAAU,4BAA4B;AAAA,MAClE;AACA,YAAM,0BAA0B,aAAa,QAAQ;AACrD,aAAO,KAAK,+DAA+D;AAAA,IAC7E,SAAS,GAAQ;AACf,eAAS,EAAE,WAAW,gBAAgB;AAAA,IACxC,UAAE;AACA,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF;AAEA,iBAAe,YAAY,KAAa;AACtC,UAAM,MAAM,KAAK,GAAG;AACpB,QAAI,CAAC,IAAK;AACV,QAAI,IAAI,KAAK;AACX,UAAI;AACF,cAAM,OAAO,MAAM,QAAQ,6BAA6B;AAAA,UACtD,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,KAAK,IAAI,IAAI,CAAC;AAAA,QACjD,CAAC;AACD,YAAI,CAAC,KAAK,IAAI;AACZ,gBAAM,eAAe,KAAK,UAAU,wBAAwB;AAAA,QAC9D;AAAA,MACF,SAASC,QAAO;AACd,cAAM,UAAUA,kBAAiB,QAAQA,OAAM,UAAU;AACzD,cAAM,SAAS,OAAO;AACtB;AAAA,MACF;AAAA,IACF;AACA,YAAQ,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,MAAM,MAAM,GAAG,CAAC;AAChD,kBAAc,IAAI;AAClB,QAAI,IAAI,KAAK;AACX,YAAM,0BAA0B,aAAa,QAAQ;AAAA,IACvD;AAAA,EACF;AAEA,QAAM,2BAA2B,MAAM,YAAY,CAAC,cAAsB,aAAqB;AAC7F,QAAI,CAAC,gBAAgB,CAAC,YAAY,iBAAiB,SAAU;AAC7D;AAAA,MAAQ,CAAC,QACP,IAAI,IAAI,CAAC,UAAU;AACjB,cAAM,UAAU,OAAO,MAAM,YAAY,aAAa,WAAW,MAAM,WAAW,WAAW;AAC7F,YAAI,YAAY,aAAc,QAAO;AACrC,cAAM,aAAa,EAAE,GAAI,MAAM,cAAc,CAAC,EAAG;AACjD,mBAAW,WAAW;AACtB,eAAO,EAAE,GAAG,OAAO,YAAY,WAAW;AAAA,MAC5C,CAAC;AAAA,IACH;AACA,sBAAkB,CAAC,YAAa,YAAY,eAAe,WAAW,OAAQ;AAAA,EAChF,GAAG,CAAC,CAAC;AAEL,QAAM,wBAAwB,MAAM,YAAY,CAAC,SAAiB;AAChE,QAAI,CAAC,KAAM;AACX;AAAA,MAAQ,CAAC,QACP,IAAI,IAAI,CAAC,UAAU;AACjB,cAAM,UAAU,OAAO,MAAM,YAAY,aAAa,WAAW,MAAM,WAAW,WAAW;AAC7F,YAAI,YAAY,KAAM,QAAO;AAC7B,cAAM,aAAa,EAAE,GAAI,MAAM,cAAc,CAAC,EAAG;AACjD,eAAO,WAAW;AAClB,eAAO,WAAW;AAClB,eAAO,EAAE,GAAG,OAAO,YAAY,WAAW;AAAA,MAC5C,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,iBAAe,mBAAmB;AAChC,QAAI,CAAC,WAAY;AACjB,mBAAe,IAAI;AACnB,QAAI;AAEF,UAAI,CAAC,YAAY,EAAG,OAAM,IAAI,MAAM,mBAAmB;AACvD,YAAM,UAAU;AAAA,QACd;AAAA,QACA,aAAa,KAAK,OAAO,OAAK,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO;AAAA,UACjD,KAAK,EAAE;AAAA,UACP,MAAM,EAAE;AAAA,UACR,YAAY,EAAE;AAAA,UACd,UAAU,EAAE,aAAa;AAAA,QAC3B,EAAE;AAAA,MACJ;AACA,YAAM,OAAO,MAAM,QAAQ,mCAAmC;AAAA,QAC5D,QAAQ;AAAA,QAAQ,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAAG,MAAM,KAAK,UAAU,OAAO;AAAA,MAC/F,CAAC;AACD,UAAI,CAAC,KAAK,IAAI;AACZ,cAAM,eAAe,KAAK,UAAU,sBAAsB;AAAA,MAC5D;AACA,oBAAc,KAAK;AACnB,YAAM,eAAe,SAAS;AAC9B,YAAM,0BAA0B,aAAa,QAAQ;AAAA,IACvD,SAAS,GAAQ;AACf,YAAM,GAAG,WAAW,wBAAwB,OAAO;AAAA,IACrD,UAAE;AACA,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF;AAKA,QAAM,mBAAmB,yBACtB,KAAK,EAAE,OAAO,MAAM,aAAa,MAAM,eAAe,KAAY,CAAC,EACnE,OAAO;AAAA;AAAA,IAEN,eAAe,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,YAAW,kBAAiB,cAAc,CAAC,EAAE,SAAS,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,SAAS;AAAA;AAAA,IAElH,eAAe,EAAE,QAAQ,EAAE,SAAS;AAAA,EACtC,CAAC;AAEH,QAAM,SAAsB;AAAA,IAC1B,EAAE,IAAI,SAAS,OAAO,SAAS,MAAM,QAAQ,UAAU,KAAK;AAAA,IAC5D,EAAE,IAAI,eAAe,OAAO,eAAe,MAAM,WAAW;AAAA,IAC5D;AAAA,MACE,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,MAAM;AAAA,MACN,SAAS;AAAA,QACP,EAAE,OAAO,IAAI,OAAO,qBAAqB;AAAA,QACzC,EAAE,OAAO,YAAY,OAAO,iBAAiB;AAAA,QAC7C,EAAE,OAAO,kBAAkB,OAAO,kBAAkB;AAAA,QACpD,EAAE,OAAO,gBAAgB,OAAO,iBAAiB;AAAA,MACnD;AAAA,IACF;AAAA,IACA,GAAI,iBAAiB,WAAW,CAAC,EAAE,IAAI,iBAAiB,OAAO,mBAAmB,MAAM,WAAW,CAAC,IAAI,CAAC;AAAA,EAC3G;AACA,QAAM,yBAAyB,MAAM;AAAA,IAAY,MAC7C;AAAA,MAAC;AAAA;AAAA,QACC,aAAa;AAAA,QACb,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA,wBAAwB;AAAA,QACxB,mBAAmB,CAAC,SAAS;AAC3B,uBAAa,IAAI;AACjB,cAAI,CAAC,KAAK,KAAK,CAAC,OAAO,GAAG,SAAS,cAAc,GAAG;AAClD,8BAAkB,KAAK,CAAC,GAAG,QAAQ,IAAI;AAAA,UACzC;AAAA,QACF;AAAA,QACA,sBAAsB;AAAA,QACtB,mBAAmB;AAAA,QACnB;AAAA,QACA,iCAAiC;AAAA,QACjC,YAAY;AAAA,QACZ,eAAe,CAAC,UAAU;AAAE,eAAK,YAAY,KAAK;AAAA,QAAE;AAAA,QACpD,oBAAoB,CAAC,OAAO,YAAY;AACtC,kBAAQ,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,QAAS,QAAQ,QAAQ,UAAU,KAAM,CAAC;AAC3E,gCAAsB,OAAO,OAAO;AAAA,QACtC;AAAA,QACA,aAAa,CAAC,QAAQ,gBAAgB,EAAE,KAAK,SAAS,CAAC;AAAA,QACvD,gBAAgB,CAAC,QAAQ;AAAE,eAAK,aAAa,GAAG;AAAA,QAAE;AAAA,QAClD,WAAW,CAAC,MAAM,OAAO;AACvB,kBAAQ,CAAC,QAAQ;AACf,kBAAM,OAAO,CAAC,GAAG,GAAG;AACpB,kBAAM,CAAC,KAAK,IAAI,KAAK,OAAO,MAAM,CAAC;AACnC,iBAAK,OAAO,IAAI,GAAG,KAAK;AACxB,mBAAO;AAAA,UACT,CAAC;AACD,wBAAc,IAAI;AAAA,QACpB;AAAA,QACA,aAAa,aAAa,EAAE,OAAO,MAAM,QAAQ,aAAa,SAAS,0CAAqC,IAAI;AAAA,QAChH,gBAAe;AAAA,QACf,WAAW;AAAA,QACX;AAAA,QACA,WAAW;AAAA,UACT,UAAU;AAAA,UACV,QAAQ,CAAC,UAAU;AACjB,kBAAM,UAAU,QAAQ;AACxB,kBAAM,OAAO,MAAM;AACnB,gBAAI,CAAC,QAAS;AACd,gBAAI,CAAC,QAAQ,CAAC,QAAQ,SAAS,IAAI,GAAG;AACpC,mBAAK,iBAAiB;AAAA,YACxB;AAAA,UACF;AAAA,QACF;AAAA;AAAA,IACF;AAAA,IAEJ,CAAC,MAAM,WAAW,aAAa,WAAW,gBAAgB,yBAAyB,YAAY,aAAa,UAAU,aAAa,cAAc,gBAAgB;AAAA,EAAC;AAElK,QAAM,mBAAkC,EAAE,IAAI,eAAe,OAAO,qBAAqB,QAAQ,GAAG,WAAW,uBAAuB;AAEtI,QAAM,SAA0B;AAAA,IAC9B,EAAE,IAAI,YAAY,OAAO,mBAAmB,QAAQ,GAAG,QAAQ,iBAAiB,WAAW,CAAC,SAAQ,eAAc,iBAAgB,eAAe,IAAI,CAAC,SAAQ,eAAc,eAAe,EAAE;AAAA,IAC7L;AAAA,EACF;AAEA,QAAM,uBAAuB,MAAM,YAAY,OAAO,SAAkC;AACtF,QAAI,CAAC,UAAU;AACb,YAAM,oBAAoB,mBAAmB;AAAA,IAC/C;AACA,QAAI,CAAC,YAAY,GAAG;AAClB,YAAM,qDAAqD,OAAO;AAClE,YAAM,oBAAoB,mDAAmD;AAAA,IAC/E;AACA;AACE,YAAM,gBAAgB,2BAA2B,cAAc,IAAI;AACnE,UAAI,CAAC,cAAe,OAAM,oBAAoB,mBAAmB;AACjE,YAAM,aAAa,MAAM,QAAQ,0BAA0B;AAAA,QACzD,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,GAAG,cAAc,CAAC;AAAA,MACrD,CAAC;AACD,UAAI,CAAC,WAAW,IAAI;AAClB,cAAM,eAAe,WAAW,UAAU,uBAAuB;AAAA,MACnE;AACA,UAAI;AAAE,eAAO,cAAc,IAAI,MAAM,oBAAoB,CAAC;AAAA,MAAE,QAAQ;AAAA,MAAC;AAAA,IACvE;AACA,UAAM,cAAc;AAAA,MAClB;AAAA,MACA,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO;AAAA,QACnD,KAAK,EAAE;AAAA,QACP,MAAM,EAAE;AAAA,QACR,YAAY,EAAE;AAAA,QACd,UAAU,EAAE,aAAa;AAAA,MAC3B,EAAE;AAAA,MACF,WAAW,qBAAqB;AAAA,MAChC;AAAA,IACF;AACA,UAAM,WAAW,MAAM,QAAQ,mCAAmC;AAAA,MAChE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,WAAW;AAAA,IAClC,CAAC;AACD,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,eAAe,SAAS,UAAU,4BAA4B;AAAA,IACtE;AACA,QAAI;AAAE,aAAO,cAAc,IAAI,MAAM,oBAAoB,CAAC;AAAA,IAAE,QAAQ;AAAA,IAAC;AACrE,UAAM,0BAA0B,aAAa,QAAQ;AACrD,UAAM,qBAAqB,SAAS;AAAA,EACtC,GAAG,CAAC,sBAAsB,MAAM,UAAU,cAAc,aAAa,yBAAyB,WAAW,CAAC;AAE1G,MAAI,CAAC,UAAU;AACb,WACE,oBAAC,QACC,8BAAC,YACC,8BAAC,SAAI,WAAU,OACb,8BAAC,eAAY,OAAM,kBAAiB,SAAQ,kDAAiD,GAC/F,GACF,GACF;AAAA,EAEJ;AAEA,MAAI,mBAAmB;AACrB,WACE,oBAAC,SAAI,WAAU,OACb;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,OAAO,kBAAkB,qBAAqB,QAAQ;AAAA,QACtD,QAAQ,CAAC;AAAA,QACT,QAAQ,CAAC,gBAAgB;AAAA,QACzB,eAAe;AAAA,QACf,WAAW,qBAAqB;AAAA,QAChC,aAAY;AAAA,QACZ,eAAe;AAAA,QACf,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,UAAQ;AAAA,QACR,UAAU;AAAA;AAAA,IACZ,GACF;AAAA,EAEJ;AAEA,SACE,qBAAC,QACC;AAAA,wBAAC,YACC;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,OAAO,gBAAgB,QAAQ;AAAA,QAC/B,UAAU,iBAAiB,SAAS,6BAA6B;AAAA,QACjE;AAAA,QACA;AAAA,QACA,eAAe;AAAA,QACf,WAAW,qBAAqB;AAAA,QAChC,aAAY;AAAA,QACZ,eAAe,iBAAiB;AAAA,QAChC,cAAc,iBAAiB,WAC7B,oBAAC,UAAO,SAAQ,WAAU,SAAO,MAC/B,8BAAC,QAAK,MAAM,0BAA0B,mBAAmB,QAAQ,CAAC,YAAY,0BAE9E,GACF,IACE;AAAA,QACJ,YAAY,iBAAiB,SAAS,6BAA6B;AAAA,QACnE,iBAAiB,iBAAiB,SAAS,oEAAoE;AAAA,QAC/G,UAAU;AAAA,QACZ,UAAU,iBAAiB,WAAW,YAAY;AAChD,gBAAM,aAAa,MAAM,QAAQ,0BAA0B,EAAE,QAAQ,UAAU,SAAS,EAAE,gBAAgB,mBAAmB,GAAG,MAAM,KAAK,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC;AACpK,cAAI,CAAC,WAAW,IAAI;AAClB,kBAAM,eAAe,WAAW,UAAU,yBAAyB;AAAA,UACrE;AACA,gBAAM,kBAAkB,SAAS;AACjC,cAAI;AAAE,mBAAO,cAAc,IAAI,MAAM,oBAAoB,CAAC;AAAA,UAAE,QAAQ;AAAA,UAAC;AAAA,QACvE,IAAI;AAAA;AAAA,IACN,GACA;AAAA,IACA,oBAAC,UAAO,MAAM,CAAC,CAAC,cAAc,cAAc,CAAC,SAAS;AAAE,UAAI,CAAC,KAAM,iBAAgB,IAAI;AAAA,IAAE,GACvF,+BAAC,iBAAc,WAAU,0CACvB;AAAA,0BAAC,gBACC,8BAAC,eAAa,YAAE,uCAAuC,4BAA4B,EAAE,KAAK,cAAc,IAAI,OAAO,GAAG,CAAC,GAAE,GAC3H;AAAA,MACC,gBACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAO;AAAA,UACP,YAAW;AAAA,UACX,UAAU,GAAG,aAAa,QAAQ,IAAI,aAAa,IAAI,GAAG;AAAA,UAC1D,YAAY;AAAA,UACZ,oBAAoB;AAAA;AAAA,MACtB;AAAA,OAEJ,GACF;AAAA,KACF;AAEJ;AAEO,SAAS,2BACd,cACA,MACgC;AAChC,QAAM,UAAU,iBAAiB,WAC7B,yBACG,KAAK,EAAE,OAAO,MAAM,aAAa,MAAM,YAAY,MAAa,eAAe,KAAY,CAAC,EAC5F,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,IACnD,yBACG,KAAK,EAAE,OAAO,MAAM,aAAa,MAAM,eAAe,KAAY,CAAC;AAC1E,QAAM,aAAa;AAAA,IACjB,GAAG;AAAA,IACH,eAAe,OAAO,KAAK,kBAAkB,YAAY,KAAK,gBAAgB,KAAK,gBAAgB;AAAA,EACrG;AACA,QAAM,SAAS,QAAQ,UAAU,UAAU;AAC3C,SAAO,OAAO,UAAW,OAAO,OAAmC;AACrE;",
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport React, { useEffect, useMemo, useState } from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { invalidateCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { upsertCustomEntitySchema, upsertCustomFieldDefSchema } from '@open-mercato/core/modules/entities/data/validators'\nimport { z } from 'zod'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'\nimport Link from 'next/link'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { loadGeneratedFieldRegistrations } from '@open-mercato/ui/backend/fields/registry'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { createCrudFormError, raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { FieldDefinitionsEditor, type FieldDefinition, type FieldDefinitionError } from '@open-mercato/ui/backend/custom-fields/FieldDefinitionsEditor'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { normalizeCustomFieldOptions } from '@open-mercato/shared/modules/entities/options'\nimport { TranslationManager } from '@open-mercato/core/modules/translations/components/TranslationManager'\n\ntype Def = FieldDefinition\ntype EntitiesListResponse = { items?: Array<Record<string, unknown>> }\ntype FieldsetGroup = { code: string; title?: string; hint?: string }\ntype FieldsetDefinition = { code: string; label: string; icon?: string; description?: string; groups?: FieldsetGroup[] }\ntype DefinitionsManageResponse = { items?: any[]; deletedKeys?: string[]; fieldsets?: FieldsetDefinition[]; settings?: { singleFieldsetPerRecord?: boolean } }\n\ntype DefErrors = FieldDefinitionError\n\n\nexport default function EditDefinitionsPage({ params }: { params?: { entityId?: string } }) {\n React.useEffect(() => { loadGeneratedFieldRegistrations().catch(() => {}) }, [])\n const router = useRouter()\n const searchParams = useSearchParams()\n const t = useT()\n const queryClient = useQueryClient()\n const entityId = useMemo(() => decodeURIComponent((params?.entityId as any) || ''), [params])\n const [label, setLabel] = useState('')\n const [entitySource, setEntitySource] = useState<'code'|'custom'>('custom')\n const [entityFormLoading, setEntityFormLoading] = useState(true)\n const [entityInitial, setEntityInitial] = useState<{ label?: string; description?: string; labelField?: string; defaultEditor?: string; showInSidebar?: boolean; updatedAt?: string }>({})\n const [defs, setDefs] = useState<Def[]>([])\n const [orderDirty, setOrderDirty] = useState(false)\n const [orderSaving, setOrderSaving] = useState(false)\n const listRef = React.useRef<HTMLDivElement | null>(null)\n const [loading, setLoading] = useState(true)\n const [saving, setSaving] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [deletedKeys, setDeletedKeys] = useState<string[]>([])\n const [defErrors, setDefErrors] = useState<Record<number, DefErrors>>({})\n const [fieldsets, setFieldsets] = useState<FieldsetDefinition[]>([])\n const [activeFieldset, setActiveFieldset] = useState<string | null>(null)\n const [singleFieldsetPerRecord, setSingleFieldsetPerRecord] = useState(true)\n const [translateDef, setTranslateDef] = useState<{ def: Def; entityId: string } | null>(null)\n\n const translateFields = React.useMemo(() => {\n if (!translateDef) return undefined\n const { def } = translateDef\n const fields: string[] = ['label', 'description']\n const options = normalizeCustomFieldOptions(def.configJson?.options)\n for (const opt of options) {\n if (opt.value) fields.push(`options.${opt.value}.label`)\n }\n return fields\n }, [translateDef])\n\n const translateBaseValues = React.useMemo(() => {\n if (!translateDef) return undefined\n const { def } = translateDef\n const base: Record<string, string> = {}\n if (typeof def.configJson?.label === 'string') base.label = def.configJson.label\n if (typeof def.configJson?.description === 'string') base.description = def.configJson.description\n const options = normalizeCustomFieldOptions(def.configJson?.options)\n for (const opt of options) {\n if (opt.value && opt.label) base[`options.${opt.value}.label`] = opt.label\n }\n return base\n }, [translateDef])\n\n const requestedFieldset = React.useMemo(() => {\n const raw = searchParams?.get('fieldset')\n return raw && raw.trim().length ? raw.trim() : null\n }, [searchParams])\n const embedFieldsetView = React.useMemo(() => searchParams?.get('view') === 'fieldset', [searchParams])\n const normalizeGroupPayload = React.useCallback((value: unknown) => {\n if (!value) return null\n if (typeof value === 'string') {\n const code = value.trim()\n return code ? { code } : null\n }\n if (typeof value !== 'object') return null\n const entry = value as Record<string, unknown>\n const code = typeof entry.code === 'string' ? entry.code.trim() : ''\n if (!code) return null\n const group: FieldsetGroup = { code }\n if (typeof entry.title === 'string' && entry.title.trim()) group.title = entry.title.trim()\n if (typeof entry.hint === 'string' && entry.hint.trim()) group.hint = entry.hint.trim()\n return group\n }, [])\n\n const buildFieldsetPayload = React.useCallback(() => {\n const groupMap = new Map<string, FieldsetGroup[]>()\n defs.forEach((definition) => {\n const code = typeof definition.configJson?.fieldset === 'string' ? definition.configJson.fieldset : null\n if (!code) return\n const normalized = normalizeGroupPayload(definition.configJson?.group)\n if (!normalized) return\n const list = groupMap.get(code) ?? []\n if (!list.some((entry) => entry.code === normalized.code)) {\n list.push(normalized)\n groupMap.set(code, list)\n }\n })\n return fieldsets.map((fs) => ({\n ...fs,\n groups: groupMap.get(fs.code) ?? [],\n }))\n }, [defs, fieldsets, normalizeGroupPayload])\n\n const validateDef = React.useCallback((d: Def): DefErrors => {\n const parsed = upsertCustomFieldDefSchema.safeParse({ entityId, key: d.key, kind: d.kind, configJson: d.configJson, isActive: d.isActive })\n if (parsed.success) return {}\n const errs: DefErrors = {}\n for (const issue of parsed.error.issues) {\n if ((issue.path || []).includes('key')) errs.key = issue.message\n if ((issue.path || []).includes('kind')) errs.kind = issue.message\n }\n return errs\n }, [entityId, requestedFieldset])\n\n const validateAndSetErrorAt = (index: number, d: Def) => {\n const errs = validateDef(d)\n setDefErrors((prev) => ({ ...prev, [index]: errs }))\n return !errs.key && !errs.kind\n }\n\n const validateAll = () => {\n const nextErrors: Record<number, DefErrors> = {}\n defs.forEach((d, i) => {\n nextErrors[i] = validateDef(d)\n })\n setDefErrors(nextErrors)\n return Object.values(nextErrors).every(e => !e.key && !e.kind)\n }\n\n useEffect(() => {\n let mounted = true\n async function load() {\n setLoading(true)\n try {\n const entJson = await readApiResultOrThrow<EntitiesListResponse>(\n '/api/entities/entities',\n undefined,\n { errorMessage: 'Failed to load entity metadata', fallback: { items: [] } },\n )\n const ent = (entJson.items || []).find((x: any) => x.entityId === entityId)\n if (mounted) {\n const record = ent as Record<string, unknown> | undefined\n const labelValue =\n typeof record?.label === 'string' && record.label.trim().length > 0 ? record.label : entityId\n const descriptionValue = typeof record?.description === 'string' ? record.description : ''\n const labelFieldValue =\n typeof record?.labelField === 'string' && record.labelField.length > 0 ? record.labelField : 'name'\n const defaultEditorValue =\n typeof record?.defaultEditor === 'string' ? record.defaultEditor : ''\n const showInSidebarValue = record?.showInSidebar === true\n const updatedAtValue =\n typeof record?.updatedAt === 'string' && record.updatedAt.length > 0 ? record.updatedAt : undefined\n setLabel(labelValue)\n if (record?.source === 'code' || record?.source === 'custom') setEntitySource(record.source)\n setEntityInitial({\n label: labelValue,\n description: descriptionValue,\n labelField: labelFieldValue,\n defaultEditor: defaultEditorValue,\n showInSidebar: showInSidebarValue,\n updatedAt: updatedAtValue,\n })\n setEntityFormLoading(false)\n }\n const json = await readApiResultOrThrow<DefinitionsManageResponse>(\n `/api/entities/definitions.manage?entityId=${encodeURIComponent(entityId)}`,\n undefined,\n { errorMessage: 'Failed to load entity definitions', fallback: { items: [], deletedKeys: [] } },\n )\n if (mounted) {\n const loaded: Def[] = (json.items || []).map((d: any) => ({ key: d.key, kind: d.kind, configJson: d.configJson || {}, isActive: d.isActive !== false }))\n loaded.sort(\n (a, b) => Number(a.configJson?.priority ?? 0) - Number(b.configJson?.priority ?? 0)\n )\n setDefs(loaded)\n setDefErrors({})\n setDeletedKeys(Array.isArray(json.deletedKeys) ? json.deletedKeys : [])\n const loadedFieldsets = Array.isArray(json.fieldsets) ? json.fieldsets : []\n setFieldsets(loadedFieldsets)\n setActiveFieldset((prev) => {\n if (requestedFieldset && loadedFieldsets.some((fs) => fs.code === requestedFieldset)) {\n return requestedFieldset\n }\n if (prev && loadedFieldsets.some((fs) => fs.code === prev)) return prev\n return loadedFieldsets[0]?.code ?? null\n })\n setSingleFieldsetPerRecord(json.settings?.singleFieldsetPerRecord !== false)\n }\n } catch (e: any) {\n if (mounted) setError(e.message || 'Failed to load')\n } finally {\n if (mounted) setLoading(false)\n }\n }\n if (entityId) load()\n return () => { mounted = false }\n }, [entityId])\n\n function addField() {\n setDefs((arr) => [\n ...arr,\n {\n key: '',\n kind: 'text',\n configJson: activeFieldset ? { fieldset: activeFieldset } : {},\n isActive: true,\n },\n ])\n }\n\n async function restoreField(key: string) {\n try {\n const call = await apiCall('/api/entities/definitions.restore', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, 'Failed to restore field')\n }\n // Reload definitions & deleted keys\n const j2 = await readApiResultOrThrow<DefinitionsManageResponse>(\n `/api/entities/definitions.manage?entityId=${encodeURIComponent(entityId)}`,\n undefined,\n { errorMessage: 'Failed to reload field definitions', fallback: { items: [], deletedKeys: [] } },\n )\n const loaded: Def[] = (j2.items || []).map((d: any) => ({ key: d.key, kind: d.kind, configJson: d.configJson || {}, isActive: d.isActive !== false }))\n loaded.sort(\n (a, b) => Number(a.configJson?.priority ?? 0) - Number(b.configJson?.priority ?? 0)\n )\n setDefs(loaded)\n setDeletedKeys(Array.isArray(j2.deletedKeys) ? j2.deletedKeys : [])\n flash(`Restored ${key}`, 'success')\n await invalidateCustomFieldDefs(queryClient, entityId)\n } catch (e: any) {\n flash(e?.message || 'Failed to restore field', 'error')\n }\n }\n\n async function saveAll() {\n setSaving(true)\n setError(null)\n try {\n if (!validateAll()) {\n flash('Please fix validation errors in field definitions', 'error')\n throw new Error('Validation failed')\n }\n const payload = {\n entityId,\n definitions: defs.filter(d => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n }\n const call = await apiCall('/api/entities/definitions.batch', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, 'Failed to save definitions')\n }\n await invalidateCustomFieldDefs(queryClient, entityId)\n router.push(`/backend/entities/user?flash=Definitions%20saved&type=success`)\n } catch (e: any) {\n setError(e.message || 'Failed to save')\n } finally {\n setSaving(false)\n }\n }\n\n async function removeField(idx: number) {\n const def = defs[idx]\n if (!def) return\n if (def.key) {\n try {\n const call = await apiCall('/api/entities/definitions', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, key: def.key }),\n })\n if (!call.ok) {\n await raiseCrudError(call.response, 'Failed to delete field')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to delete field'\n flash(message, 'error')\n return\n }\n }\n setDefs((arr) => arr.filter((_, i) => i !== idx))\n setOrderDirty(true)\n if (def.key) {\n await invalidateCustomFieldDefs(queryClient, entityId)\n }\n }\n\n const handleFieldsetCodeChange = React.useCallback((previousCode: string, nextCode: string) => {\n if (!previousCode || !nextCode || previousCode === nextCode) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== previousCode) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n nextConfig.fieldset = nextCode\n return { ...entry, configJson: nextConfig }\n })\n )\n setActiveFieldset((current) => (current === previousCode ? nextCode : current))\n }, [])\n\n const handleFieldsetRemoved = React.useCallback((code: string) => {\n if (!code) return\n setDefs((arr) =>\n arr.map((entry) => {\n const current = typeof entry.configJson?.fieldset === 'string' ? entry.configJson.fieldset : undefined\n if (current !== code) return entry\n const nextConfig = { ...(entry.configJson || {}) }\n delete nextConfig.fieldset\n delete nextConfig.group\n return { ...entry, configJson: nextConfig }\n })\n )\n }, [])\n\n async function saveOrderIfDirty() {\n if (!orderDirty) return\n setOrderSaving(true)\n try {\n // Do not save order when there are invalid keys/kinds\n if (!validateAll()) throw new Error('Validation failed')\n const payload = {\n entityId,\n definitions: defs.filter(d => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n }\n const call = await apiCall('/api/entities/definitions.batch', {\n method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload)\n })\n if (!call.ok) {\n await raiseCrudError(call.response, 'Failed to save order')\n }\n setOrderDirty(false)\n flash('Order saved', 'success')\n await invalidateCustomFieldDefs(queryClient, entityId)\n } catch (e: any) {\n flash(e?.message || 'Failed to save order', 'error')\n } finally {\n setOrderSaving(false)\n }\n }\n\n // Unify loader via CrudForm isLoading; do not return early here\n\n // Schema for inline field-level validation in CrudForm\n const entityFormSchema = upsertCustomEntitySchema\n .pick({ label: true, description: true, defaultEditor: true as any })\n .extend({\n // Allow empty string in the UI select, treat as undefined later\n defaultEditor: z.union([z.enum(['markdown','simpleMarkdown','htmlRichText']).optional(), z.literal('')]).optional(),\n // Include showInSidebar so CrudForm doesn't strip it on submit\n showInSidebar: z.boolean().optional(),\n }) as z.ZodType<Record<string, unknown>>\n\n const fields: CrudField[] = [\n { id: 'label', label: 'Label', type: 'text', required: true },\n { id: 'description', label: 'Description', type: 'textarea' },\n {\n id: 'defaultEditor',\n label: 'Default Editor (multiline)',\n type: 'select',\n options: [\n { value: '', label: 'Default (Markdown)' },\n { value: 'markdown', label: 'Markdown (UIW)' },\n { value: 'simpleMarkdown', label: 'Simple Markdown' },\n { value: 'htmlRichText', label: 'HTML Rich Text' },\n ],\n } as any,\n ...(entitySource === 'custom' ? [{ id: 'showInSidebar', label: 'Show in sidebar', type: 'checkbox' }] : []),\n ]\n const renderFieldDefinitions = React.useCallback(() => (\n <FieldDefinitionsEditor\n definitions={defs}\n errors={defErrors}\n deletedKeys={deletedKeys}\n fieldsets={fieldsets}\n activeFieldset={activeFieldset}\n onActiveFieldsetChange={setActiveFieldset}\n onFieldsetsChange={(next) => {\n setFieldsets(next)\n if (!next.some((fs) => fs.code === activeFieldset)) {\n setActiveFieldset(next[0]?.code ?? null)\n }\n }}\n onFieldsetCodeChange={handleFieldsetCodeChange}\n onFieldsetRemoved={handleFieldsetRemoved}\n singleFieldsetPerRecord={singleFieldsetPerRecord}\n onSingleFieldsetPerRecordChange={setSingleFieldsetPerRecord}\n onAddField={addField}\n onRemoveField={(index) => { void removeField(index) }}\n onDefinitionChange={(index, nextDef) => {\n setDefs((arr) => arr.map((entry, idx) => (idx === index ? nextDef : entry)))\n validateAndSetErrorAt(index, nextDef)\n }}\n onTranslate={(def) => setTranslateDef({ def, entityId })}\n onRestoreField={(key) => { void restoreField(key) }}\n onReorder={(from, to) => {\n setDefs((arr) => {\n const next = [...arr]\n const [moved] = next.splice(from, 1)\n next.splice(to, 0, moved)\n return next\n })\n setOrderDirty(true)\n }}\n orderNotice={orderDirty ? { dirty: true, saving: orderSaving, message: 'Reordered \u2014 will auto-save on blur' } : undefined}\n addButtonLabel=\"Add Field\"\n translate={t}\n listRef={listRef}\n listProps={{\n tabIndex: -1,\n onBlur: (event) => {\n const current = listRef.current\n const next = event.relatedTarget as Node | null\n if (!current) return\n if (!next || !current.contains(next)) {\n void saveOrderIfDirty()\n }\n },\n }}\n />\n ),\n [defs, defErrors, deletedKeys, fieldsets, activeFieldset, singleFieldsetPerRecord, orderDirty, orderSaving, addField, removeField, restoreField, saveOrderIfDirty])\n\n const definitionsGroup: CrudFormGroup = { id: 'definitions', title: 'Field Definitions', column: 1, component: renderFieldDefinitions }\n\n const groups: CrudFormGroup[] = [\n { id: 'settings', title: 'Entity Settings', column: 1, fields: entitySource === 'custom' ? ['label','description','defaultEditor','showInSidebar'] : ['label','description','defaultEditor'] },\n definitionsGroup,\n ]\n\n const handleCrudFormSubmit = React.useCallback(async (vals: Record<string, unknown>) => {\n if (!entityId) {\n throw createCrudFormError('Invalid entity ID')\n }\n if (!validateAll()) {\n flash('Please fix validation errors in field definitions', 'error')\n throw createCrudFormError('Please fix validation errors in field definitions')\n }\n {\n const entityPayload = buildEntityMetadataPayload(entitySource, vals)\n if (!entityPayload) throw createCrudFormError('Validation failed')\n const callEntity = await apiCall('/api/entities/entities', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId, ...entityPayload }),\n })\n if (!callEntity.ok) {\n await raiseCrudError(callEntity.response, 'Failed to save entity')\n }\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n }\n const defsPayload = {\n entityId,\n definitions: defs.filter((d) => !!d.key).map((d) => ({\n key: d.key,\n kind: d.kind,\n configJson: d.configJson,\n isActive: d.isActive !== false,\n })),\n fieldsets: buildFieldsetPayload(),\n singleFieldsetPerRecord,\n }\n const callDefs = await apiCall('/api/entities/definitions.batch', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(defsPayload),\n })\n if (!callDefs.ok) {\n await raiseCrudError(callDefs.response, 'Failed to save definitions')\n }\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n await invalidateCustomFieldDefs(queryClient, entityId)\n flash('Definitions saved', 'success')\n }, [buildFieldsetPayload, defs, entityId, entitySource, queryClient, singleFieldsetPerRecord, validateAll])\n\n if (!entityId) {\n return (\n <Page>\n <PageBody>\n <div className=\"p-6\">\n <ErrorNotice title=\"Invalid entity\" message=\"The requested entity ID is missing or invalid.\" />\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (embedFieldsetView) {\n return (\n <div className=\"p-4\">\n <CrudForm\n schema={entityFormSchema}\n title={`Edit fieldset: ${requestedFieldset ?? entityId}`}\n fields={[]}\n groups={[definitionsGroup]}\n initialValues={entityInitial as any}\n isLoading={entityFormLoading || loading}\n submitLabel=\"Save\"\n deleteVisible={false}\n backHref={undefined}\n cancelHref={undefined}\n embedded\n onSubmit={handleCrudFormSubmit}\n />\n </div>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n schema={entityFormSchema}\n title={`Edit Entity: ${entityId}`}\n backHref={entitySource === 'code' ? \"/backend/entities/system\" : \"/backend/entities/user\"}\n fields={fields}\n groups={groups}\n initialValues={entityInitial as any}\n isLoading={entityFormLoading || loading}\n submitLabel=\"Save\"\n deleteVisible={entitySource === 'custom'}\n extraActions={entitySource === 'custom' ? (\n <Button variant=\"outline\" asChild>\n <Link href={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}>\n Show Records\n </Link>\n </Button>\n ) : null}\n cancelHref={entitySource === 'code' ? \"/backend/entities/system\" : \"/backend/entities/user\"}\n successRedirect={entitySource === 'code' ? \"/backend/entities/system?flash=Definitions%20saved&type=success\" : \"/backend/entities/user?flash=Definitions%20saved&type=success\"}\n onSubmit={handleCrudFormSubmit}\n onDelete={entitySource === 'custom' ? async () => {\n const callDelete = await apiCall('/api/entities/entities', { method: 'DELETE', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ entityId }) })\n if (!callDelete.ok) {\n await raiseCrudError(callDelete.response, 'Failed to delete entity')\n }\n flash('Entity deleted', 'success')\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n } : undefined}\n />\n </PageBody>\n <Dialog open={!!translateDef} onOpenChange={(open) => { if (!open) setTranslateDef(null) }}>\n <DialogContent className=\"max-w-2xl max-h-[80vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>{t('translations.manager.translateField', 'Translate field: {{key}}', { key: translateDef?.def.key ?? '' })}</DialogTitle>\n </DialogHeader>\n {translateDef && (\n <TranslationManager\n mode=\"embedded\"\n compact\n entityType=\"entities:custom_field_def\"\n recordId={`${translateDef.entityId}:${translateDef.def.key}`}\n baseValues={translateBaseValues}\n translatableFields={translateFields}\n />\n )}\n </DialogContent>\n </Dialog>\n </Page>\n )\n}\n\nexport function buildEntityMetadataPayload(\n entitySource: 'code' | 'custom',\n vals: Record<string, unknown>,\n): Record<string, unknown> | null {\n const partial = entitySource === 'custom'\n ? upsertCustomEntitySchema\n .pick({ label: true, description: true, labelField: true as any, defaultEditor: true as any })\n .extend({ showInSidebar: z.boolean().optional() }) as unknown as z.ZodTypeAny\n : upsertCustomEntitySchema\n .pick({ label: true, description: true, defaultEditor: true as any }) as unknown as z.ZodTypeAny\n const normalized = {\n ...vals,\n defaultEditor: typeof vals.defaultEditor === 'string' && vals.defaultEditor ? vals.defaultEditor : undefined,\n }\n const parsed = partial.safeParse(normalized)\n return parsed.success ? (parsed.data as Record<string, unknown>) : null\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["fields", "code", "error"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.6-develop.
|
|
3
|
+
"version": "0.6.6-develop.5651.1.c43359070c",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -245,16 +245,16 @@
|
|
|
245
245
|
"zod": "^4.4.3"
|
|
246
246
|
},
|
|
247
247
|
"peerDependencies": {
|
|
248
|
-
"@open-mercato/ai-assistant": "0.6.6-develop.
|
|
249
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
250
|
-
"@open-mercato/ui": "0.6.6-develop.
|
|
248
|
+
"@open-mercato/ai-assistant": "0.6.6-develop.5651.1.c43359070c",
|
|
249
|
+
"@open-mercato/shared": "0.6.6-develop.5651.1.c43359070c",
|
|
250
|
+
"@open-mercato/ui": "0.6.6-develop.5651.1.c43359070c",
|
|
251
251
|
"react": "^19.0.0",
|
|
252
252
|
"react-dom": "^19.0.0"
|
|
253
253
|
},
|
|
254
254
|
"devDependencies": {
|
|
255
|
-
"@open-mercato/ai-assistant": "0.6.6-develop.
|
|
256
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
257
|
-
"@open-mercato/ui": "0.6.6-develop.
|
|
255
|
+
"@open-mercato/ai-assistant": "0.6.6-develop.5651.1.c43359070c",
|
|
256
|
+
"@open-mercato/shared": "0.6.6-develop.5651.1.c43359070c",
|
|
257
|
+
"@open-mercato/ui": "0.6.6-develop.5651.1.c43359070c",
|
|
258
258
|
"@testing-library/dom": "^10.4.1",
|
|
259
259
|
"@testing-library/jest-dom": "^6.9.1",
|
|
260
260
|
"@testing-library/react": "^16.3.1",
|
|
@@ -342,9 +342,11 @@ export async function GET(req: Request) {
|
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
const slugByOrgId = new Map<string, string | null>()
|
|
345
|
+
const updatedAtByOrgId = new Map<string, string | null>()
|
|
345
346
|
const logoUrlByOrgId = new Map<string, string | null>()
|
|
346
347
|
for (const org of allOrgs) {
|
|
347
348
|
slugByOrgId.set(String(org.id), org.slug ?? null)
|
|
349
|
+
updatedAtByOrgId.set(String(org.id), org.updatedAt instanceof Date ? org.updatedAt.toISOString() : null)
|
|
348
350
|
logoUrlByOrgId.set(String(org.id), org.logoUrl ?? null)
|
|
349
351
|
}
|
|
350
352
|
|
|
@@ -429,6 +431,7 @@ export async function GET(req: Request) {
|
|
|
429
431
|
id: node.id,
|
|
430
432
|
name: node.name,
|
|
431
433
|
slug: slugByOrgId.get(recordId) ?? null,
|
|
434
|
+
updatedAt: updatedAtByOrgId.get(recordId) ?? null,
|
|
432
435
|
logoUrl: logoUrlByOrgId.get(recordId) ?? null,
|
|
433
436
|
tenantId: tid,
|
|
434
437
|
tenantName: tenantNameMap[tid] ?? tid,
|
|
@@ -8,6 +8,10 @@ import { upsertCustomEntitySchema } from '@open-mercato/core/modules/entities/da
|
|
|
8
8
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
9
9
|
import { isSystemEntitySelectable } from '@open-mercato/shared/lib/entities/system-entities'
|
|
10
10
|
import { SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, isOrmBackedSystemEntityId } from '@open-mercato/shared/lib/data/engine'
|
|
11
|
+
import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
|
|
12
|
+
import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
13
|
+
|
|
14
|
+
const CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND = 'entities.entity'
|
|
11
15
|
|
|
12
16
|
export const metadata = {
|
|
13
17
|
GET: { requireAuth: true },
|
|
@@ -60,6 +64,7 @@ export async function GET(req: Request) {
|
|
|
60
64
|
labelField: (c as any).labelField ?? undefined,
|
|
61
65
|
defaultEditor: (c as any).defaultEditor ?? undefined,
|
|
62
66
|
showInSidebar: (c as any).showInSidebar ?? false,
|
|
67
|
+
updatedAt: c.updatedAt instanceof Date ? c.updatedAt.toISOString() : (c.updatedAt ?? undefined),
|
|
63
68
|
}))
|
|
64
69
|
|
|
65
70
|
const byId = new Map<string, any>()
|
|
@@ -120,6 +125,21 @@ export async function POST(req: Request) {
|
|
|
120
125
|
|
|
121
126
|
const where: any = { entityId: input.entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }
|
|
122
127
|
let ent = await em.findOne(CustomEntity, where)
|
|
128
|
+
if (ent) {
|
|
129
|
+
try {
|
|
130
|
+
enforceCommandOptimisticLock({
|
|
131
|
+
resourceKind: CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND,
|
|
132
|
+
resourceId: ent.id,
|
|
133
|
+
current: ent.updatedAt ?? null,
|
|
134
|
+
request: req,
|
|
135
|
+
})
|
|
136
|
+
} catch (lockError) {
|
|
137
|
+
if (isCrudHttpError(lockError)) {
|
|
138
|
+
return NextResponse.json(lockError.body, { status: lockError.status })
|
|
139
|
+
}
|
|
140
|
+
throw lockError
|
|
141
|
+
}
|
|
142
|
+
}
|
|
123
143
|
if (!ent) ent = em.create(CustomEntity, { ...where, createdAt: new Date() })
|
|
124
144
|
ent.label = input.label
|
|
125
145
|
ent.description = input.description ?? null
|
|
@@ -177,6 +197,7 @@ const entitySummarySchema = z.object({
|
|
|
177
197
|
labelField: z.string().optional(),
|
|
178
198
|
defaultEditor: z.string().optional(),
|
|
179
199
|
showInSidebar: z.boolean().optional(),
|
|
200
|
+
updatedAt: z.string().optional(),
|
|
180
201
|
count: z.number(),
|
|
181
202
|
})
|
|
182
203
|
|
|
@@ -45,7 +45,7 @@ export default function EditDefinitionsPage({ params }: { params?: { entityId?:
|
|
|
45
45
|
const [label, setLabel] = useState('')
|
|
46
46
|
const [entitySource, setEntitySource] = useState<'code'|'custom'>('custom')
|
|
47
47
|
const [entityFormLoading, setEntityFormLoading] = useState(true)
|
|
48
|
-
const [entityInitial, setEntityInitial] = useState<{ label?: string; description?: string; labelField?: string; defaultEditor?: string; showInSidebar?: boolean }>({})
|
|
48
|
+
const [entityInitial, setEntityInitial] = useState<{ label?: string; description?: string; labelField?: string; defaultEditor?: string; showInSidebar?: boolean; updatedAt?: string }>({})
|
|
49
49
|
const [defs, setDefs] = useState<Def[]>([])
|
|
50
50
|
const [orderDirty, setOrderDirty] = useState(false)
|
|
51
51
|
const [orderSaving, setOrderSaving] = useState(false)
|
|
@@ -171,6 +171,8 @@ export default function EditDefinitionsPage({ params }: { params?: { entityId?:
|
|
|
171
171
|
const defaultEditorValue =
|
|
172
172
|
typeof record?.defaultEditor === 'string' ? record.defaultEditor : ''
|
|
173
173
|
const showInSidebarValue = record?.showInSidebar === true
|
|
174
|
+
const updatedAtValue =
|
|
175
|
+
typeof record?.updatedAt === 'string' && record.updatedAt.length > 0 ? record.updatedAt : undefined
|
|
174
176
|
setLabel(labelValue)
|
|
175
177
|
if (record?.source === 'code' || record?.source === 'custom') setEntitySource(record.source)
|
|
176
178
|
setEntityInitial({
|
|
@@ -179,6 +181,7 @@ export default function EditDefinitionsPage({ params }: { params?: { entityId?:
|
|
|
179
181
|
labelField: labelFieldValue,
|
|
180
182
|
defaultEditor: defaultEditorValue,
|
|
181
183
|
showInSidebar: showInSidebarValue,
|
|
184
|
+
updatedAt: updatedAtValue,
|
|
182
185
|
})
|
|
183
186
|
setEntityFormLoading(false)
|
|
184
187
|
}
|