@open-mercato/core 0.6.5-develop.5382.1.f542de69af → 0.6.6-develop.5412.1.e2a52b14f0

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.
Files changed (138) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/crmFixtures.js +4 -0
  3. package/dist/helpers/integration/crmFixtures.js.map +2 -2
  4. package/dist/modules/attachments/api/route.js +2 -0
  5. package/dist/modules/attachments/api/route.js.map +2 -2
  6. package/dist/modules/attachments/lib/access.js +18 -0
  7. package/dist/modules/attachments/lib/access.js.map +2 -2
  8. package/dist/modules/auth/services/rbacService.js +3 -2
  9. package/dist/modules/auth/services/rbacService.js.map +2 -2
  10. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +0 -3
  11. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +2 -2
  12. package/dist/modules/customers/api/deals/route.js +43 -2
  13. package/dist/modules/customers/api/deals/route.js.map +2 -2
  14. package/dist/modules/customers/api/deals/summary/route.js +402 -0
  15. package/dist/modules/customers/api/deals/summary/route.js.map +7 -0
  16. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js +16 -5
  17. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js.map +2 -2
  18. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js +22 -5
  19. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js.map +2 -2
  20. package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -2
  21. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  22. package/dist/modules/customers/backend/customers/deals/page.js +221 -56
  23. package/dist/modules/customers/backend/customers/deals/page.js.map +3 -3
  24. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +1 -1
  25. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  26. package/dist/modules/customers/cli.js +15 -9
  27. package/dist/modules/customers/cli.js.map +2 -2
  28. package/dist/modules/customers/components/DealsKpiStrip.js +282 -0
  29. package/dist/modules/customers/components/DealsKpiStrip.js.map +7 -0
  30. package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +0 -1
  31. package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +2 -2
  32. package/dist/modules/customers/components/detail/DealForm.js +100 -17
  33. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  34. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +1 -2
  35. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  36. package/dist/modules/customers/components/kpi/PipelineStageBar.js +63 -0
  37. package/dist/modules/customers/components/kpi/PipelineStageBar.js.map +7 -0
  38. package/dist/modules/customers/lib/dealsMetrics.js +82 -0
  39. package/dist/modules/customers/lib/dealsMetrics.js.map +7 -0
  40. package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js +2 -1
  41. package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js.map +2 -2
  42. package/dist/modules/directory/utils/organizationScope.js +59 -27
  43. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  44. package/dist/modules/entities/api/entities.js +7 -0
  45. package/dist/modules/entities/api/entities.js.map +2 -2
  46. package/dist/modules/entities/api/records.js +26 -15
  47. package/dist/modules/entities/api/records.js.map +2 -2
  48. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +14 -0
  49. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
  50. package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js +14 -0
  51. package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js.map +2 -2
  52. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +12 -0
  53. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  54. package/dist/modules/entities/components/useRecordsEntityGuard.js +30 -0
  55. package/dist/modules/entities/components/useRecordsEntityGuard.js.map +7 -0
  56. package/dist/modules/query_index/lib/engine.js +4 -2
  57. package/dist/modules/query_index/lib/engine.js.map +2 -2
  58. package/dist/modules/staff/api/team-members.js +9 -2
  59. package/dist/modules/staff/api/team-members.js.map +2 -2
  60. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +24 -1
  61. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
  62. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +11 -6
  63. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  64. package/dist/modules/staff/commands/team-members.js +1 -1
  65. package/dist/modules/staff/commands/team-members.js.map +2 -2
  66. package/dist/modules/staff/components/TeamMemberForm.js +1 -1
  67. package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
  68. package/dist/modules/staff/lib/scheduleSwitch.js +23 -0
  69. package/dist/modules/staff/lib/scheduleSwitch.js.map +7 -0
  70. package/dist/modules/workflows/backend/definitions/create/page.js +1 -2
  71. package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
  72. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +1 -2
  73. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  74. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -2
  75. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
  76. package/dist/modules/workflows/components/NodeEditDialog.js +4 -13
  77. package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
  78. package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +4 -13
  79. package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
  80. package/dist/modules/workflows/components/WorkflowGraphImpl.js +1 -4
  81. package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
  82. package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +2 -5
  83. package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
  84. package/package.json +8 -8
  85. package/src/helpers/integration/crmFixtures.ts +21 -1
  86. package/src/modules/attachments/AGENTS.md +79 -0
  87. package/src/modules/attachments/api/route.ts +2 -0
  88. package/src/modules/attachments/lib/access.ts +36 -0
  89. package/src/modules/auth/services/rbacService.ts +11 -2
  90. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +0 -3
  91. package/src/modules/customers/api/deals/route.ts +51 -2
  92. package/src/modules/customers/api/deals/summary/route.ts +496 -0
  93. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.ts +28 -6
  94. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealData.ts +33 -6
  95. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +17 -2
  96. package/src/modules/customers/backend/customers/deals/page.tsx +254 -66
  97. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +1 -2
  98. package/src/modules/customers/cli.ts +15 -15
  99. package/src/modules/customers/components/DealsKpiStrip.tsx +389 -0
  100. package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +0 -1
  101. package/src/modules/customers/components/detail/DealForm.tsx +121 -19
  102. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +1 -2
  103. package/src/modules/customers/components/kpi/PipelineStageBar.tsx +77 -0
  104. package/src/modules/customers/i18n/de.json +43 -0
  105. package/src/modules/customers/i18n/en.json +43 -0
  106. package/src/modules/customers/i18n/es.json +43 -0
  107. package/src/modules/customers/i18n/pl.json +43 -0
  108. package/src/modules/customers/lib/dealsMetrics.ts +159 -0
  109. package/src/modules/directory/subscribers/invalidateOrgScopeCache.ts +3 -1
  110. package/src/modules/directory/utils/organizationScope.ts +85 -30
  111. package/src/modules/entities/api/entities.ts +11 -0
  112. package/src/modules/entities/api/records.ts +46 -25
  113. package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +15 -0
  114. package/src/modules/entities/backend/entities/user/[entityId]/records/create/page.tsx +15 -0
  115. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +23 -0
  116. package/src/modules/entities/components/useRecordsEntityGuard.ts +41 -0
  117. package/src/modules/entities/i18n/de.json +1 -0
  118. package/src/modules/entities/i18n/en.json +1 -0
  119. package/src/modules/entities/i18n/es.json +1 -0
  120. package/src/modules/entities/i18n/pl.json +1 -0
  121. package/src/modules/query_index/lib/engine.ts +11 -5
  122. package/src/modules/staff/api/team-members.ts +9 -2
  123. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +31 -1
  124. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +18 -8
  125. package/src/modules/staff/commands/team-members.ts +5 -2
  126. package/src/modules/staff/components/TeamMemberForm.tsx +4 -1
  127. package/src/modules/staff/i18n/de.json +1 -0
  128. package/src/modules/staff/i18n/en.json +1 -0
  129. package/src/modules/staff/i18n/es.json +1 -0
  130. package/src/modules/staff/i18n/pl.json +1 -0
  131. package/src/modules/staff/lib/scheduleSwitch.ts +46 -0
  132. package/src/modules/workflows/backend/definitions/create/page.tsx +1 -2
  133. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +1 -2
  134. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -2
  135. package/src/modules/workflows/components/NodeEditDialog.tsx +1 -4
  136. package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +4 -7
  137. package/src/modules/workflows/components/WorkflowGraphImpl.tsx +1 -2
  138. package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +2 -3
@@ -7,6 +7,8 @@ import { z } from "zod";
7
7
  import { apiCall, readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
8
8
  import { updateCrud } from "@open-mercato/ui/backend/utils/crud";
9
9
  import { createCrudFormError, raiseCrudError } from "@open-mercato/ui/backend/utils/serverErrors";
10
+ import { ErrorMessage, LoadingMessage } from "@open-mercato/ui/backend/detail";
11
+ import { useRecordsEntityGuard } from "@open-mercato/core/modules/entities/components/useRecordsEntityGuard";
10
12
  const defaultUpdateRecordRequest = async (payload) => {
11
13
  await updateCrud("entities/records", payload);
12
14
  };
@@ -23,6 +25,18 @@ async function submitCustomEntityRecordUpdate(options) {
23
25
  await updateRecord({ entityId, recordId, values });
24
26
  }
25
27
  function EditRecordPage({ params }) {
28
+ const t = useT();
29
+ const entityId = decodeURIComponent(params?.entityId || "");
30
+ const guard = useRecordsEntityGuard(entityId);
31
+ if (guard === "blocked") {
32
+ return /* @__PURE__ */ jsx(ErrorMessage, { label: t("entities.userEntities.records.errors.systemEntity", "This entity is system-managed. Records are available for custom entities only.") });
33
+ }
34
+ if (guard === "checking") {
35
+ return /* @__PURE__ */ jsx(LoadingMessage, { label: t("entities.userEntities.records.loading", "Loading records...") });
36
+ }
37
+ return /* @__PURE__ */ jsx(EditRecordPageInner, { params });
38
+ }
39
+ function EditRecordPageInner({ params }) {
26
40
  const t = useT();
27
41
  const entityId = decodeURIComponent(params?.entityId || "");
28
42
  const recordId = decodeURIComponent(params?.recordId || "");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../../src/modules/entities/backend/entities/user/%5BentityId%5D/records/%5BrecordId%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { z } from 'zod'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError, raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\n\ntype UpdateRecordRequest = (payload: { entityId: string; recordId: string; values: Record<string, unknown> }) => Promise<void>\n\nconst defaultUpdateRecordRequest: UpdateRecordRequest = async (payload) => {\n await updateCrud('entities/records', payload)\n}\n\nexport async function submitCustomEntityRecordUpdate(options: {\n entityId: string\n recordId: string\n values: Record<string, unknown>\n updateRecord?: UpdateRecordRequest\n messages?: {\n entityIdRequired?: string\n recordIdRequired?: string\n }\n}) {\n const { entityId, recordId, values, updateRecord = defaultUpdateRecordRequest, messages } = options\n if (!entityId || !entityId.trim()) {\n const message = messages?.entityIdRequired ?? 'Entity identifier is required'\n throw createCrudFormError(message, { entityId: message })\n }\n if (!recordId || !recordId.trim()) {\n const message = messages?.recordIdRequired ?? 'Record identifier is required'\n throw createCrudFormError(message, { recordId: message })\n }\n await updateRecord({ entityId, recordId, values })\n}\n\ntype RecordsResponse = { items: any[] }\n\nexport default function EditRecordPage({ params }: { params: { entityId?: string; recordId?: string } }) {\n const t = useT()\n const entityId = decodeURIComponent(params?.entityId || '')\n const recordId = decodeURIComponent(params?.recordId || '')\n\n const [initialValues, setInitialValues] = React.useState<Record<string, any> | null>(null)\n const [loading, setLoading] = React.useState(true)\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n try {\n const j = await readApiResultOrThrow<RecordsResponse>(\n `/api/entities/records?entityId=${encodeURIComponent(entityId)}&page=1&pageSize=1&sortField=id&sortDir=asc&id=${encodeURIComponent(recordId)}`,\n undefined,\n { errorMessage: 'Failed to load record', fallback: { items: [] } },\n )\n const item = (j.items || []).find((x: any) => String(x.id) === String(recordId)) || null\n if (!cancelled) setInitialValues(item || {})\n } catch {\n if (!cancelled) setInitialValues({})\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n if (entityId && recordId) load()\n return () => { cancelled = true }\n }, [entityId, recordId])\n\n const schema = React.useMemo(() => z.object({}).passthrough(), [])\n\n const fields: CrudField[] = []\n\n return (\n <CrudForm\n title={t('entities.userEntities.records.form.editTitle', 'Edit record')}\n backHref={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}\n schema={schema}\n fields={fields}\n entityId={entityId}\n customEntity\n initialValues={initialValues || {}}\n optimisticLockUpdatedAt={\n typeof initialValues?.updatedAt === 'string'\n ? initialValues.updatedAt\n : typeof initialValues?.updated_at === 'string'\n ? initialValues.updated_at\n : null\n }\n isLoading={loading}\n loadingMessage={t('entities.userEntities.records.loading', 'Loading record...')}\n submitLabel={t('entities.userEntities.records.form.submitSave', 'Save')}\n cancelHref={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}\n successRedirect={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}\n onSubmit={async (values) => {\n await submitCustomEntityRecordUpdate({\n entityId,\n recordId,\n values: values as Record<string, unknown>,\n messages: {\n entityIdRequired: t('entities.userEntities.records.errors.entityIdRequired', 'Entity identifier is required'),\n recordIdRequired: t('entities.userEntities.records.errors.recordIdRequired', 'Record identifier is required'),\n },\n })\n }}\n onDelete={async () => {\n const call = await apiCall(\n `/api/entities/records?entityId=${encodeURIComponent(entityId)}&recordId=${encodeURIComponent(recordId)}`,\n { method: 'DELETE' },\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.userEntities.records.errors.deleteFailed', 'Failed to delete record'))\n }\n // navigate back\n if (typeof window !== 'undefined') window.location.href = `/backend/entities/user/${encodeURIComponent(entityId)}/records`\n }}\n />\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { z } from 'zod'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError, raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { useRecordsEntityGuard } from '@open-mercato/core/modules/entities/components/useRecordsEntityGuard'\n\ntype UpdateRecordRequest = (payload: { entityId: string; recordId: string; values: Record<string, unknown> }) => Promise<void>\n\nconst defaultUpdateRecordRequest: UpdateRecordRequest = async (payload) => {\n await updateCrud('entities/records', payload)\n}\n\nexport async function submitCustomEntityRecordUpdate(options: {\n entityId: string\n recordId: string\n values: Record<string, unknown>\n updateRecord?: UpdateRecordRequest\n messages?: {\n entityIdRequired?: string\n recordIdRequired?: string\n }\n}) {\n const { entityId, recordId, values, updateRecord = defaultUpdateRecordRequest, messages } = options\n if (!entityId || !entityId.trim()) {\n const message = messages?.entityIdRequired ?? 'Entity identifier is required'\n throw createCrudFormError(message, { entityId: message })\n }\n if (!recordId || !recordId.trim()) {\n const message = messages?.recordIdRequired ?? 'Record identifier is required'\n throw createCrudFormError(message, { recordId: message })\n }\n await updateRecord({ entityId, recordId, values })\n}\n\ntype RecordsResponse = { items: any[] }\n\nexport default function EditRecordPage({ params }: { params: { entityId?: string; recordId?: string } }) {\n const t = useT()\n const entityId = decodeURIComponent(params?.entityId || '')\n const guard = useRecordsEntityGuard(entityId)\n if (guard === 'blocked') {\n return <ErrorMessage label={t('entities.userEntities.records.errors.systemEntity', 'This entity is system-managed. Records are available for custom entities only.')} />\n }\n if (guard === 'checking') {\n return <LoadingMessage label={t('entities.userEntities.records.loading', 'Loading records...')} />\n }\n return <EditRecordPageInner params={params} />\n}\n\nfunction EditRecordPageInner({ params }: { params: { entityId?: string; recordId?: string } }) {\n const t = useT()\n const entityId = decodeURIComponent(params?.entityId || '')\n const recordId = decodeURIComponent(params?.recordId || '')\n\n const [initialValues, setInitialValues] = React.useState<Record<string, any> | null>(null)\n const [loading, setLoading] = React.useState(true)\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n try {\n const j = await readApiResultOrThrow<RecordsResponse>(\n `/api/entities/records?entityId=${encodeURIComponent(entityId)}&page=1&pageSize=1&sortField=id&sortDir=asc&id=${encodeURIComponent(recordId)}`,\n undefined,\n { errorMessage: 'Failed to load record', fallback: { items: [] } },\n )\n const item = (j.items || []).find((x: any) => String(x.id) === String(recordId)) || null\n if (!cancelled) setInitialValues(item || {})\n } catch {\n if (!cancelled) setInitialValues({})\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n if (entityId && recordId) load()\n return () => { cancelled = true }\n }, [entityId, recordId])\n\n const schema = React.useMemo(() => z.object({}).passthrough(), [])\n\n const fields: CrudField[] = []\n\n return (\n <CrudForm\n title={t('entities.userEntities.records.form.editTitle', 'Edit record')}\n backHref={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}\n schema={schema}\n fields={fields}\n entityId={entityId}\n customEntity\n initialValues={initialValues || {}}\n optimisticLockUpdatedAt={\n typeof initialValues?.updatedAt === 'string'\n ? initialValues.updatedAt\n : typeof initialValues?.updated_at === 'string'\n ? initialValues.updated_at\n : null\n }\n isLoading={loading}\n loadingMessage={t('entities.userEntities.records.loading', 'Loading record...')}\n submitLabel={t('entities.userEntities.records.form.submitSave', 'Save')}\n cancelHref={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}\n successRedirect={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}\n onSubmit={async (values) => {\n await submitCustomEntityRecordUpdate({\n entityId,\n recordId,\n values: values as Record<string, unknown>,\n messages: {\n entityIdRequired: t('entities.userEntities.records.errors.entityIdRequired', 'Entity identifier is required'),\n recordIdRequired: t('entities.userEntities.records.errors.recordIdRequired', 'Record identifier is required'),\n },\n })\n }}\n onDelete={async () => {\n const call = await apiCall(\n `/api/entities/records?entityId=${encodeURIComponent(entityId)}&recordId=${encodeURIComponent(recordId)}`,\n { method: 'DELETE' },\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('entities.userEntities.records.errors.deleteFailed', 'Failed to delete record'))\n }\n // navigate back\n if (typeof window !== 'undefined') window.location.href = `/backend/entities/user/${encodeURIComponent(entityId)}/records`\n }}\n />\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -7,6 +7,8 @@ import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
7
7
  import { z } from "zod";
8
8
  import { createCrud } from "@open-mercato/ui/backend/utils/crud";
9
9
  import { createCrudFormError } from "@open-mercato/ui/backend/utils/serverErrors";
10
+ import { ErrorMessage, LoadingMessage } from "@open-mercato/ui/backend/detail";
11
+ import { useRecordsEntityGuard } from "@open-mercato/core/modules/entities/components/useRecordsEntityGuard";
10
12
  const defaultCreateRecordRequest = async (payload) => {
11
13
  await createCrud("entities/records", payload);
12
14
  };
@@ -19,6 +21,18 @@ async function submitCustomEntityRecord(options) {
19
21
  await createRecord({ entityId, values });
20
22
  }
21
23
  function CreateRecordPage({ params }) {
24
+ const t = useT();
25
+ const entityId = decodeURIComponent(params?.entityId || "");
26
+ const guard = useRecordsEntityGuard(entityId);
27
+ if (guard === "blocked") {
28
+ return /* @__PURE__ */ jsx(ErrorMessage, { label: t("entities.userEntities.records.errors.systemEntity", "This entity is system-managed. Records are available for custom entities only.") });
29
+ }
30
+ if (guard === "checking") {
31
+ return /* @__PURE__ */ jsx(LoadingMessage, { label: t("entities.userEntities.records.loading", "Loading records...") });
32
+ }
33
+ return /* @__PURE__ */ jsx(CreateRecordPageInner, { params });
34
+ }
35
+ function CreateRecordPageInner({ params }) {
22
36
  const t = useT();
23
37
  const router = useRouter();
24
38
  const entityId = decodeURIComponent(params?.entityId || "");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../../src/modules/entities/backend/entities/user/%5BentityId%5D/records/create/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { z } from 'zod'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\n\ntype CreateRecordRequest = (payload: { entityId: string; values: Record<string, unknown> }) => Promise<void>\n\nconst defaultCreateRecordRequest: CreateRecordRequest = async (payload) => {\n await createCrud('entities/records', payload)\n}\n\nexport async function submitCustomEntityRecord(options: {\n entityId: string\n values: Record<string, unknown>\n createRecord?: CreateRecordRequest\n messages?: {\n entityIdRequired?: string\n }\n}) {\n const { entityId, values, createRecord = defaultCreateRecordRequest, messages } = options\n if (!entityId || !entityId.trim()) {\n const message = messages?.entityIdRequired ?? 'Entity identifier is required'\n throw createCrudFormError(message, { entityId: message })\n }\n await createRecord({ entityId, values })\n}\n\nexport default function CreateRecordPage({ params }: { params: { entityId?: string } }) {\n const t = useT()\n const router = useRouter()\n const entityId = decodeURIComponent(params?.entityId || '')\n\n const schema = React.useMemo(() => z.object({\n // Dynamic: all fields are optional; keep unknown keys\n }).passthrough(), [])\n\n const fields: CrudField[] = []\n\n return (\n <CrudForm\n title={t('entities.userEntities.records.form.createTitle', 'Create record')}\n backHref={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}\n schema={schema}\n fields={fields}\n entityId={entityId}\n customEntity\n submitLabel={t('entities.userEntities.records.form.submitCreate', 'Create')}\n cancelHref={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}\n onSubmit={async (values) => {\n await submitCustomEntityRecord({\n entityId,\n values: values as Record<string, unknown>,\n messages: {\n entityIdRequired: t('entities.userEntities.records.errors.entityIdRequired', 'Entity identifier is required'),\n },\n })\n router.push(`/backend/entities/user/${encodeURIComponent(entityId)}/records`)\n }}\n />\n )\n}\n"],
5
- "mappings": ";AA2CI;AA1CJ,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,YAAY;AACrB,SAAS,gBAAgC;AACzC,SAAS,SAAS;AAClB,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;AAIpC,MAAM,6BAAkD,OAAO,YAAY;AACzE,QAAM,WAAW,oBAAoB,OAAO;AAC9C;AAEA,eAAsB,yBAAyB,SAO5C;AACD,QAAM,EAAE,UAAU,QAAQ,eAAe,4BAA4B,SAAS,IAAI;AAClF,MAAI,CAAC,YAAY,CAAC,SAAS,KAAK,GAAG;AACjC,UAAM,UAAU,UAAU,oBAAoB;AAC9C,UAAM,oBAAoB,SAAS,EAAE,UAAU,QAAQ,CAAC;AAAA,EAC1D;AACA,QAAM,aAAa,EAAE,UAAU,OAAO,CAAC;AACzC;AAEe,SAAR,iBAAkC,EAAE,OAAO,GAAsC;AACtF,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,WAAW,mBAAmB,QAAQ,YAAY,EAAE;AAE1D,QAAM,SAAS,MAAM,QAAQ,MAAM,EAAE,OAAO;AAAA;AAAA,EAE5C,CAAC,EAAE,YAAY,GAAG,CAAC,CAAC;AAEpB,QAAM,SAAsB,CAAC;AAE7B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,kDAAkD,eAAe;AAAA,MAC1E,UAAU,0BAA0B,mBAAmB,QAAQ,CAAC;AAAA,MAChE;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAY;AAAA,MACZ,aAAa,EAAE,mDAAmD,QAAQ;AAAA,MAC1E,YAAY,0BAA0B,mBAAmB,QAAQ,CAAC;AAAA,MAClE,UAAU,OAAO,WAAW;AAC1B,cAAM,yBAAyB;AAAA,UAC7B;AAAA,UACA;AAAA,UACA,UAAU;AAAA,YACR,kBAAkB,EAAE,yDAAyD,+BAA+B;AAAA,UAC9G;AAAA,QACF,CAAC;AACD,eAAO,KAAK,0BAA0B,mBAAmB,QAAQ,CAAC,UAAU;AAAA,MAC9E;AAAA;AAAA,EACF;AAEJ;",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { z } from 'zod'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { useRecordsEntityGuard } from '@open-mercato/core/modules/entities/components/useRecordsEntityGuard'\n\ntype CreateRecordRequest = (payload: { entityId: string; values: Record<string, unknown> }) => Promise<void>\n\nconst defaultCreateRecordRequest: CreateRecordRequest = async (payload) => {\n await createCrud('entities/records', payload)\n}\n\nexport async function submitCustomEntityRecord(options: {\n entityId: string\n values: Record<string, unknown>\n createRecord?: CreateRecordRequest\n messages?: {\n entityIdRequired?: string\n }\n}) {\n const { entityId, values, createRecord = defaultCreateRecordRequest, messages } = options\n if (!entityId || !entityId.trim()) {\n const message = messages?.entityIdRequired ?? 'Entity identifier is required'\n throw createCrudFormError(message, { entityId: message })\n }\n await createRecord({ entityId, values })\n}\n\nexport default function CreateRecordPage({ params }: { params: { entityId?: string } }) {\n const t = useT()\n const entityId = decodeURIComponent(params?.entityId || '')\n const guard = useRecordsEntityGuard(entityId)\n if (guard === 'blocked') {\n return <ErrorMessage label={t('entities.userEntities.records.errors.systemEntity', 'This entity is system-managed. Records are available for custom entities only.')} />\n }\n if (guard === 'checking') {\n return <LoadingMessage label={t('entities.userEntities.records.loading', 'Loading records...')} />\n }\n return <CreateRecordPageInner params={params} />\n}\n\nfunction CreateRecordPageInner({ params }: { params: { entityId?: string } }) {\n const t = useT()\n const router = useRouter()\n const entityId = decodeURIComponent(params?.entityId || '')\n\n const schema = React.useMemo(() => z.object({\n // Dynamic: all fields are optional; keep unknown keys\n }).passthrough(), [])\n\n const fields: CrudField[] = []\n\n return (\n <CrudForm\n title={t('entities.userEntities.records.form.createTitle', 'Create record')}\n backHref={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}\n schema={schema}\n fields={fields}\n entityId={entityId}\n customEntity\n submitLabel={t('entities.userEntities.records.form.submitCreate', 'Create')}\n cancelHref={`/backend/entities/user/${encodeURIComponent(entityId)}/records`}\n onSubmit={async (values) => {\n await submitCustomEntityRecord({\n entityId,\n values: values as Record<string, unknown>,\n messages: {\n entityIdRequired: t('entities.userEntities.records.errors.entityIdRequired', 'Entity identifier is required'),\n },\n })\n router.push(`/backend/entities/user/${encodeURIComponent(entityId)}/records`)\n }}\n />\n )\n}\n"],
5
+ "mappings": ";AAsCW;AArCX,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,YAAY;AACrB,SAAS,gBAAgC;AACzC,SAAS,SAAS;AAClB,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;AACpC,SAAS,cAAc,sBAAsB;AAC7C,SAAS,6BAA6B;AAItC,MAAM,6BAAkD,OAAO,YAAY;AACzE,QAAM,WAAW,oBAAoB,OAAO;AAC9C;AAEA,eAAsB,yBAAyB,SAO5C;AACD,QAAM,EAAE,UAAU,QAAQ,eAAe,4BAA4B,SAAS,IAAI;AAClF,MAAI,CAAC,YAAY,CAAC,SAAS,KAAK,GAAG;AACjC,UAAM,UAAU,UAAU,oBAAoB;AAC9C,UAAM,oBAAoB,SAAS,EAAE,UAAU,QAAQ,CAAC;AAAA,EAC1D;AACA,QAAM,aAAa,EAAE,UAAU,OAAO,CAAC;AACzC;AAEe,SAAR,iBAAkC,EAAE,OAAO,GAAsC;AACtF,QAAM,IAAI,KAAK;AACf,QAAM,WAAW,mBAAmB,QAAQ,YAAY,EAAE;AAC1D,QAAM,QAAQ,sBAAsB,QAAQ;AAC5C,MAAI,UAAU,WAAW;AACvB,WAAO,oBAAC,gBAAa,OAAO,EAAE,qDAAqD,gFAAgF,GAAG;AAAA,EACxK;AACA,MAAI,UAAU,YAAY;AACxB,WAAO,oBAAC,kBAAe,OAAO,EAAE,yCAAyC,oBAAoB,GAAG;AAAA,EAClG;AACA,SAAO,oBAAC,yBAAsB,QAAgB;AAChD;AAEA,SAAS,sBAAsB,EAAE,OAAO,GAAsC;AAC5E,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,WAAW,mBAAmB,QAAQ,YAAY,EAAE;AAE1D,QAAM,SAAS,MAAM,QAAQ,MAAM,EAAE,OAAO;AAAA;AAAA,EAE5C,CAAC,EAAE,YAAY,GAAG,CAAC,CAAC;AAEpB,QAAM,SAAsB,CAAC;AAE7B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,kDAAkD,eAAe;AAAA,MAC1E,UAAU,0BAA0B,mBAAmB,QAAQ,CAAC;AAAA,MAChE;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAY;AAAA,MACZ,aAAa,EAAE,mDAAmD,QAAQ;AAAA,MAC1E,YAAY,0BAA0B,mBAAmB,QAAQ,CAAC;AAAA,MAClE,UAAU,OAAO,WAAW;AAC1B,cAAM,yBAAyB;AAAA,UAC7B;AAAA,UACA;AAAA,UACA,UAAU;AAAA,YACR,kBAAkB,EAAE,yDAAyD,+BAA+B;AAAA,UAC9G;AAAA,QACF,CAAC;AACD,eAAO,KAAK,0BAA0B,mBAAmB,QAAQ,CAAC,UAAU;AAAA,MAC9E;AAAA;AAAA,EACF;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -14,6 +14,9 @@ import { flash } from "@open-mercato/ui/backend/FlashMessages";
14
14
  import { raiseCrudError } from "@open-mercato/ui/backend/utils/serverErrors";
15
15
  import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
16
16
  import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
17
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
18
+ import { ErrorMessage, LoadingMessage } from "@open-mercato/ui/backend/detail";
19
+ import { useRecordsEntityGuard } from "@open-mercato/core/modules/entities/components/useRecordsEntityGuard";
17
20
  function toCsvUrl(base, params) {
18
21
  const p = new URLSearchParams(params);
19
22
  p.set("format", "csv");
@@ -29,6 +32,15 @@ function normalizeCell(v) {
29
32
  return String(v);
30
33
  }
31
34
  function RecordsPage({ params }) {
35
+ const t = useT();
36
+ const entityId = decodeURIComponent(params?.entityId || "");
37
+ const guard = useRecordsEntityGuard(entityId);
38
+ if (guard !== "allowed") {
39
+ return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: guard === "blocked" ? /* @__PURE__ */ jsx(ErrorMessage, { label: t("entities.userEntities.records.errors.systemEntity", "This entity is system-managed. Records are available for custom entities only.") }) : /* @__PURE__ */ jsx(LoadingMessage, { label: t("entities.userEntities.records.loading", "Loading records...") }) }) });
40
+ }
41
+ return /* @__PURE__ */ jsx(RecordsPageInner, { params });
42
+ }
43
+ function RecordsPageInner({ params }) {
32
44
  const entityId = decodeURIComponent(params?.entityId || "");
33
45
  const [sorting, setSorting] = React.useState([{ id: "id", desc: false }]);
34
46
  const [page, setPage] = React.useState(1);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/entities/backend/entities/user/%5BentityId%5D/records/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useSearchParams } from 'next/navigation'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { filterCustomFieldDefs, useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable, type DataTableExportFormat } from '@open-mercato/ui/backend/DataTable'\nimport type { PreparedExport } from '@open-mercato/shared/lib/crud/exporters'\nimport type { FilterDef } from '@open-mercato/ui/backend/FilterBar'\nimport { ContextHelp } from '@open-mercato/ui/backend/ContextHelp'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport Link from 'next/link'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\n\ntype RecordsResponse = {\n items: any[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\nfunction toCsvUrl(base: string, params: URLSearchParams) {\n // Build a relative URL to avoid SSR/CSR origin mismatch hydration issues\n const p = new URLSearchParams(params)\n p.set('format', 'csv')\n const qs = p.toString()\n return qs ? `${base}?${qs}` : base\n}\n\nfunction normalizeCell(v: any): string {\n if (Array.isArray(v)) return v.filter((x) => x != null && x !== '').join(', ')\n if (v === true) return 'Yes'\n if (v === false) return 'No'\n if (v == null) return ''\n if (v instanceof Date) return v.toISOString()\n return String(v)\n}\n\nexport default function RecordsPage({ params }: { params: { entityId?: string } }) {\n const entityId = decodeURIComponent(params?.entityId || '')\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'id', desc: false }])\n const [page, setPage] = React.useState(1)\n const [pageSize, setPageSize] = React.useState(50)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<Record<string, any>>({})\n const [columns, setColumns] = React.useState<ColumnDef<any>[]>([])\n const [rawData, setRawData] = React.useState<any[]>([])\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [loading, setLoading] = React.useState(false)\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const { data: cfDefs = [] } = useCustomFieldDefs(entityId, {\n enabled: Boolean(entityId),\n keyExtras: [scopeVersion],\n })\n\n // Fetch records whenever paging/sorting/filters change (do NOT refetch on cfDefs/search changes)\n React.useEffect(() => {\n let cancelled = false\n const run = async () => {\n setLoading(true)\n try {\n const params = new URLSearchParams()\n params.set('entityId', entityId)\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n const s = sorting?.[0]\n if (s?.id) {\n params.set('sortField', String(s.id))\n params.set('sortDir', s.desc ? 'desc' : 'asc')\n }\n // Flatten filter values into query params\n for (const [k, v] of Object.entries(filterValues)) {\n if (v == null) continue\n if (Array.isArray(v)) {\n if (v.length) params.set(k, v.join(','))\n } else if (typeof v === 'object') {\n // dateRange-like shapes are not supported generically here; skip\n } else {\n params.set(k, String(v))\n }\n }\n const j = await readApiResultOrThrow<RecordsResponse>(\n `/api/entities/records?${params.toString()}`,\n undefined,\n {\n errorMessage: 'Failed to load records',\n fallback: {\n items: [],\n total: 0,\n page,\n pageSize,\n totalPages: 1,\n },\n },\n )\n if (!cancelled) {\n setRawData(j.items || [])\n setTotal(j.total)\n setTotalPages(j.totalPages)\n }\n } catch (e) {\n if (!cancelled) {\n setRawData([])\n setTotal(0)\n setTotalPages(1)\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n if (entityId) run()\n return () => { cancelled = true }\n }, [entityId, page, pageSize, sorting, filterValues, scopeVersion])\n\n // Build columns from custom field definitions only (no data round-trip)\n React.useEffect(() => {\n const visibleDefs = filterCustomFieldDefs(cfDefs, 'list') as any\n const maxVisible = 10\n const cols: ColumnDef<any>[] = visibleDefs.map((d: any, idx: number) => ({\n accessorKey: d.key,\n header: d.label || d.key,\n meta: { priority: idx < 4 ? 1 : idx < 6 ? 2 : idx < 8 ? 3 : idx < maxVisible ? 4 : 5 },\n cell: ({ getValue }: { getValue: () => unknown }) => {\n const v = getValue() as any\n return <span className=\"truncate max-w-[24ch] inline-block align-top\" title={normalizeCell(v)}>{normalizeCell(v)}</span>\n },\n }))\n // Ensure hidden 'id' column exists for sorting/state\n const hasIdCol = cols.some((c) => (c as any).accessorKey === 'id' || (c as any).id === 'id')\n if (!hasIdCol) cols.unshift({ accessorKey: 'id', header: 'ID', meta: { hidden: true, priority: 6 } } as any)\n setColumns(cols)\n }, [cfDefs])\n\n // Client-side quick search filtering without triggering server refetch\n const data = React.useMemo(() => {\n if (!search.trim()) return rawData\n const q = search.trim().toLowerCase()\n return (rawData || []).filter((row: any) => {\n const values = Object.values(row || {})\n return values.some((v) => normalizeCell(v).toLowerCase().includes(q))\n })\n }, [rawData, search])\n\n const viewExportColumns = React.useMemo(() => {\n return (columns || [])\n .map((col) => {\n const accessorKey = (col as any).accessorKey\n if (!accessorKey || typeof accessorKey !== 'string') return null\n if ((col as any).meta?.hidden) return null\n const header = typeof col.header === 'string'\n ? col.header\n : accessorKey.startsWith('cf_')\n ? accessorKey.slice(3)\n : accessorKey\n return { field: accessorKey, header }\n })\n .filter((col): col is { field: string; header: string } => !!col)\n }, [columns])\n\n const buildFullExportUrl = React.useCallback((format: DataTableExportFormat) => {\n const qp = new URLSearchParams({\n entityId,\n format,\n exportScope: 'full',\n all: 'true',\n })\n const sort = sorting?.[0]\n if (sort?.id) {\n qp.set('sortField', String(sort.id))\n qp.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n return `/api/entities/records?${qp.toString()}`\n }, [entityId, sorting])\n\n const exportConfig = React.useMemo(() => {\n const safeEntityId = entityId.replace(/[^a-z0-9_-]/gi, '_') || 'records'\n return {\n view: {\n description: 'Exports the current list respecting filters and column visibility.',\n prepare: async (): Promise<{ prepared: PreparedExport; filename: string }> => {\n const rowsForExport = data.map((row) => {\n const out: Record<string, unknown> = {}\n for (const col of viewExportColumns) {\n out[col.field] = (row as Record<string, unknown>)[col.field]\n }\n return out\n })\n const prepared: PreparedExport = {\n columns: viewExportColumns.map((col) => ({ field: col.field, header: col.header })),\n rows: rowsForExport,\n }\n return { prepared, filename: `${safeEntityId}_view` }\n },\n },\n full: {\n description: 'Exports raw records with every field and custom field included.',\n getUrl: (format: DataTableExportFormat) => buildFullExportUrl(format),\n filename: () => `${safeEntityId}_full`,\n },\n }\n }, [buildFullExportUrl, data, entityId, viewExportColumns])\n\n const hasAnyFormFields = React.useMemo(() => filterCustomFieldDefs(cfDefs, 'form').length > 0, [cfDefs])\n const actions = (\n <>\n <Button asChild variant=\"outline\" size=\"sm\">\n <Link href={`/backend/entities/user/${encodeURIComponent(entityId)}`}>\n Edit Entity Definition\n </Link>\n </Button>\n {hasAnyFormFields && (\n <Button asChild>\n <Link href={`/backend/entities/user/${encodeURIComponent(entityId)}/records/create`}>\n Create\n </Link>\n </Button>\n )}\n </>\n )\n\n // Ensure filters are visible even if no custom fields are marked filterable\n const baseFilters: FilterDef[] = React.useMemo(() => ([\n { id: 'id', label: 'ID', type: 'text' },\n ]), [])\n\n return (\n <Page>\n <PageBody>\n <ContextHelp bulb title=\"API: Manage Records via cURL\" className=\"mb-4\">\n <p className=\"mb-2\">\n Interact with this custom entity via the backend API using cURL. Use API keys for machine-to-machine access\u2014mint one from the{' '}\n <a className=\"underline\" target=\"_blank\" rel=\"noreferrer\" href=\"https://docs.openmercato.com/user-guide/api-keys\">\n Managing API keys guide\n </a>{' '}\n or the{' '}\n <a className=\"underline\" target=\"_blank\" rel=\"noreferrer\" href=\"https://docs.openmercato.com/cli/api-keys\">\n API keys CLI documentation\n </a>{' '}\n before running these calls.\n </p>\n <div className=\"space-y-2\">\n <div>\n <div className=\"font-medium mb-1\">1) Configure environment variables</div>\n <pre className=\"bg-muted p-3 rounded text-xs overflow-auto\"><code>{`export BASE_URL=\"http://localhost:3000/api\"\nexport API_KEY=\"<paste API key secret here>\" # scoped with entities.features\nexport ENTITY_ID=\"${entityId}\"\nexport RECORD_ID=\"<record uuid>\"`}</code></pre>\n <p className=\"text-muted-foreground mt-1\">\n Need a new key? Follow the{' '}\n <a className=\"underline\" target=\"_blank\" rel=\"noreferrer\" href=\"https://docs.openmercato.com/user-guide/api-keys\">\n Managing API keys\n </a>{' '}\n walkthrough or mint one via{' '}\n <a className=\"underline\" target=\"_blank\" rel=\"noreferrer\" href=\"https://docs.openmercato.com/cli/api-keys\">\n mercato api_keys add\n </a>\n .\n </p>\n </div>\n\n <div>\n <div className=\"font-medium mb-1\">2) List records</div>\n <pre className=\"bg-muted p-3 rounded text-xs overflow-auto\"><code>{`curl -s -H \"X-Api-Key: $API_KEY\" \\\n \"$BASE_URL/entities/records?entityId=$ENTITY_ID\" | jq`}</code></pre>\n </div>\n\n <div>\n <div className=\"font-medium mb-1\">3) Read a single record (by id)</div>\n <pre className=\"bg-muted p-3 rounded text-xs overflow-auto\"><code>{`curl -s -H \"X-Api-Key: $API_KEY\" \\\n \"$BASE_URL/entities/records?entityId=$ENTITY_ID&id=$RECORD_ID\" | jq`}</code></pre>\n <p className=\"text-muted-foreground mt-1\">Note: Response is a list; filter by <code>id</code> to get a single item.</p>\n </div>\n\n <div>\n <div className=\"font-medium mb-1\">4) Create a record</div>\n <pre className=\"bg-muted p-3 rounded text-xs overflow-auto\"><code>{`curl -s -X POST \\\n -H \"X-Api-Key: $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\\\"entityId\\\\\": \\\\\"$ENTITY_ID\\\\\",\n \\\\\"values\\\\\": {\n \\\\\"field_one\\\\\": \\\\\"Example\\\\\",\n \\\\\"field_two\\\\\": 123\n }\n }\" \\\n \"$BASE_URL/entities/records\" | jq`}</code></pre>\n <p className=\"text-muted-foreground mt-1\">For custom entities, send field keys without the <code>cf_</code> prefix. The API normalizes this server-side.</p>\n </div>\n\n <div>\n <div className=\"font-medium mb-1\">5) Update a record</div>\n <pre className=\"bg-muted p-3 rounded text-xs overflow-auto\"><code>{`curl -s -X PUT \\\n -H \"X-Api-Key: $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\\\"entityId\\\\\": \\\\\"$ENTITY_ID\\\\\",\n \\\\\"recordId\\\\\": \\\\\"$RECORD_ID\\\\\",\n \\\\\"values\\\\\": {\n \\\\\"field_one\\\\\": \\\\\"Updated\\\\\"\n }\n }\" \\\n \"$BASE_URL/entities/records\" | jq`}</code></pre>\n </div>\n\n <div>\n <div className=\"font-medium mb-1\">6) Delete a record</div>\n <pre className=\"bg-muted p-3 rounded text-xs overflow-auto\"><code>{`curl -s -X DELETE \\\n -H \"X-Api-Key: $API_KEY\" \\\n \"$BASE_URL/entities/records?entityId=$ENTITY_ID&recordId=$RECORD_ID\" | jq`}</code></pre>\n </div>\n\n <div className=\"text-muted-foreground\">\n Security notes:\n <ul className=\"list-disc pl-5 mt-1 space-y-1\">\n <li>All endpoints require a valid API key. Keys inherit tenant, organization, and feature scope.</li>\n <li>Rotate keys regularly and delete unused ones in the admin UI.</li>\n <li>Store the secret in a secure vault; anyone with the header can act within the key&apos;s permissions.</li>\n </ul>\n </div>\n </div>\n </ContextHelp>\n <DataTable\n stickyActionsColumn\n title={`Records: ${entityId}`}\n entityId={entityId}\n actions={actions}\n columns={columns}\n data={data}\n perspective={{ tableId: `entities.user.records.${entityId}` }}\n exporter={exportConfig}\n filters={baseFilters}\n filterValues={filterValues}\n rowActions={(row) => (\n <RowActions\n items={[\n { id: 'edit', label: 'Edit', href: `/backend/entities/user/${encodeURIComponent(entityId)}/records/${encodeURIComponent(String((row as any).id))}` },\n { id: 'delete', label: 'Delete', destructive: true, onSelect: async () => {\n try {\n const confirmed = await confirm({\n title: 'Delete this record?',\n variant: 'destructive',\n })\n if (!confirmed) return\n const deleteCall = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader((row as any).updatedAt),\n () => apiCall(\n `/api/entities/records?entityId=${encodeURIComponent(entityId)}&recordId=${encodeURIComponent(String((row as any).id))}`,\n { method: 'DELETE' },\n ),\n )\n if (!deleteCall.ok) {\n await raiseCrudError(deleteCall.response, 'Failed to delete record')\n }\n const j = await readApiResultOrThrow<RecordsResponse>(\n `/api/entities/records?entityId=${encodeURIComponent(entityId)}&page=${page}&pageSize=${pageSize}`,\n undefined,\n {\n errorMessage: 'Failed to reload records',\n fallback: { items: [], total: 0, page, pageSize, totalPages: 1 },\n },\n )\n setRawData(j.items || [])\n setTotal(j.total || 0)\n setTotalPages(j.totalPages || 1)\n flash('Record has been removed', 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to delete record'\n flash(message, 'error')\n }\n } },\n ]}\n />\n )}\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n searchValue={search}\n onSearchChange={(v) => { setSearch(v); setPage(1) }}\n onFiltersApply={(vals) => { setFilterValues(vals); setPage(1) }}\n onFiltersClear={() => { setFilterValues({}); setPage(1) }}\n pagination={{ page, pageSize, total, totalPages, onPageChange: setPage }}\n isLoading={loading}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useSearchParams } from 'next/navigation'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { filterCustomFieldDefs, useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable, type DataTableExportFormat } from '@open-mercato/ui/backend/DataTable'\nimport type { PreparedExport } from '@open-mercato/shared/lib/crud/exporters'\nimport type { FilterDef } from '@open-mercato/ui/backend/FilterBar'\nimport { ContextHelp } from '@open-mercato/ui/backend/ContextHelp'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport Link from 'next/link'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { useRecordsEntityGuard } from '@open-mercato/core/modules/entities/components/useRecordsEntityGuard'\n\ntype RecordsResponse = {\n items: any[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\nfunction toCsvUrl(base: string, params: URLSearchParams) {\n // Build a relative URL to avoid SSR/CSR origin mismatch hydration issues\n const p = new URLSearchParams(params)\n p.set('format', 'csv')\n const qs = p.toString()\n return qs ? `${base}?${qs}` : base\n}\n\nfunction normalizeCell(v: any): string {\n if (Array.isArray(v)) return v.filter((x) => x != null && x !== '').join(', ')\n if (v === true) return 'Yes'\n if (v === false) return 'No'\n if (v == null) return ''\n if (v instanceof Date) return v.toISOString()\n return String(v)\n}\n\nexport default function RecordsPage({ params }: { params: { entityId?: string } }) {\n const t = useT()\n const entityId = decodeURIComponent(params?.entityId || '')\n const guard = useRecordsEntityGuard(entityId)\n if (guard !== 'allowed') {\n return (\n <Page>\n <PageBody>\n {guard === 'blocked' ? (\n <ErrorMessage label={t('entities.userEntities.records.errors.systemEntity', 'This entity is system-managed. Records are available for custom entities only.')} />\n ) : (\n <LoadingMessage label={t('entities.userEntities.records.loading', 'Loading records...')} />\n )}\n </PageBody>\n </Page>\n )\n }\n return <RecordsPageInner params={params} />\n}\n\nfunction RecordsPageInner({ params }: { params: { entityId?: string } }) {\n const entityId = decodeURIComponent(params?.entityId || '')\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'id', desc: false }])\n const [page, setPage] = React.useState(1)\n const [pageSize, setPageSize] = React.useState(50)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<Record<string, any>>({})\n const [columns, setColumns] = React.useState<ColumnDef<any>[]>([])\n const [rawData, setRawData] = React.useState<any[]>([])\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [loading, setLoading] = React.useState(false)\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const { data: cfDefs = [] } = useCustomFieldDefs(entityId, {\n enabled: Boolean(entityId),\n keyExtras: [scopeVersion],\n })\n\n // Fetch records whenever paging/sorting/filters change (do NOT refetch on cfDefs/search changes)\n React.useEffect(() => {\n let cancelled = false\n const run = async () => {\n setLoading(true)\n try {\n const params = new URLSearchParams()\n params.set('entityId', entityId)\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n const s = sorting?.[0]\n if (s?.id) {\n params.set('sortField', String(s.id))\n params.set('sortDir', s.desc ? 'desc' : 'asc')\n }\n // Flatten filter values into query params\n for (const [k, v] of Object.entries(filterValues)) {\n if (v == null) continue\n if (Array.isArray(v)) {\n if (v.length) params.set(k, v.join(','))\n } else if (typeof v === 'object') {\n // dateRange-like shapes are not supported generically here; skip\n } else {\n params.set(k, String(v))\n }\n }\n const j = await readApiResultOrThrow<RecordsResponse>(\n `/api/entities/records?${params.toString()}`,\n undefined,\n {\n errorMessage: 'Failed to load records',\n fallback: {\n items: [],\n total: 0,\n page,\n pageSize,\n totalPages: 1,\n },\n },\n )\n if (!cancelled) {\n setRawData(j.items || [])\n setTotal(j.total)\n setTotalPages(j.totalPages)\n }\n } catch (e) {\n if (!cancelled) {\n setRawData([])\n setTotal(0)\n setTotalPages(1)\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n if (entityId) run()\n return () => { cancelled = true }\n }, [entityId, page, pageSize, sorting, filterValues, scopeVersion])\n\n // Build columns from custom field definitions only (no data round-trip)\n React.useEffect(() => {\n const visibleDefs = filterCustomFieldDefs(cfDefs, 'list') as any\n const maxVisible = 10\n const cols: ColumnDef<any>[] = visibleDefs.map((d: any, idx: number) => ({\n accessorKey: d.key,\n header: d.label || d.key,\n meta: { priority: idx < 4 ? 1 : idx < 6 ? 2 : idx < 8 ? 3 : idx < maxVisible ? 4 : 5 },\n cell: ({ getValue }: { getValue: () => unknown }) => {\n const v = getValue() as any\n return <span className=\"truncate max-w-[24ch] inline-block align-top\" title={normalizeCell(v)}>{normalizeCell(v)}</span>\n },\n }))\n // Ensure hidden 'id' column exists for sorting/state\n const hasIdCol = cols.some((c) => (c as any).accessorKey === 'id' || (c as any).id === 'id')\n if (!hasIdCol) cols.unshift({ accessorKey: 'id', header: 'ID', meta: { hidden: true, priority: 6 } } as any)\n setColumns(cols)\n }, [cfDefs])\n\n // Client-side quick search filtering without triggering server refetch\n const data = React.useMemo(() => {\n if (!search.trim()) return rawData\n const q = search.trim().toLowerCase()\n return (rawData || []).filter((row: any) => {\n const values = Object.values(row || {})\n return values.some((v) => normalizeCell(v).toLowerCase().includes(q))\n })\n }, [rawData, search])\n\n const viewExportColumns = React.useMemo(() => {\n return (columns || [])\n .map((col) => {\n const accessorKey = (col as any).accessorKey\n if (!accessorKey || typeof accessorKey !== 'string') return null\n if ((col as any).meta?.hidden) return null\n const header = typeof col.header === 'string'\n ? col.header\n : accessorKey.startsWith('cf_')\n ? accessorKey.slice(3)\n : accessorKey\n return { field: accessorKey, header }\n })\n .filter((col): col is { field: string; header: string } => !!col)\n }, [columns])\n\n const buildFullExportUrl = React.useCallback((format: DataTableExportFormat) => {\n const qp = new URLSearchParams({\n entityId,\n format,\n exportScope: 'full',\n all: 'true',\n })\n const sort = sorting?.[0]\n if (sort?.id) {\n qp.set('sortField', String(sort.id))\n qp.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n return `/api/entities/records?${qp.toString()}`\n }, [entityId, sorting])\n\n const exportConfig = React.useMemo(() => {\n const safeEntityId = entityId.replace(/[^a-z0-9_-]/gi, '_') || 'records'\n return {\n view: {\n description: 'Exports the current list respecting filters and column visibility.',\n prepare: async (): Promise<{ prepared: PreparedExport; filename: string }> => {\n const rowsForExport = data.map((row) => {\n const out: Record<string, unknown> = {}\n for (const col of viewExportColumns) {\n out[col.field] = (row as Record<string, unknown>)[col.field]\n }\n return out\n })\n const prepared: PreparedExport = {\n columns: viewExportColumns.map((col) => ({ field: col.field, header: col.header })),\n rows: rowsForExport,\n }\n return { prepared, filename: `${safeEntityId}_view` }\n },\n },\n full: {\n description: 'Exports raw records with every field and custom field included.',\n getUrl: (format: DataTableExportFormat) => buildFullExportUrl(format),\n filename: () => `${safeEntityId}_full`,\n },\n }\n }, [buildFullExportUrl, data, entityId, viewExportColumns])\n\n const hasAnyFormFields = React.useMemo(() => filterCustomFieldDefs(cfDefs, 'form').length > 0, [cfDefs])\n const actions = (\n <>\n <Button asChild variant=\"outline\" size=\"sm\">\n <Link href={`/backend/entities/user/${encodeURIComponent(entityId)}`}>\n Edit Entity Definition\n </Link>\n </Button>\n {hasAnyFormFields && (\n <Button asChild>\n <Link href={`/backend/entities/user/${encodeURIComponent(entityId)}/records/create`}>\n Create\n </Link>\n </Button>\n )}\n </>\n )\n\n // Ensure filters are visible even if no custom fields are marked filterable\n const baseFilters: FilterDef[] = React.useMemo(() => ([\n { id: 'id', label: 'ID', type: 'text' },\n ]), [])\n\n return (\n <Page>\n <PageBody>\n <ContextHelp bulb title=\"API: Manage Records via cURL\" className=\"mb-4\">\n <p className=\"mb-2\">\n Interact with this custom entity via the backend API using cURL. Use API keys for machine-to-machine access\u2014mint one from the{' '}\n <a className=\"underline\" target=\"_blank\" rel=\"noreferrer\" href=\"https://docs.openmercato.com/user-guide/api-keys\">\n Managing API keys guide\n </a>{' '}\n or the{' '}\n <a className=\"underline\" target=\"_blank\" rel=\"noreferrer\" href=\"https://docs.openmercato.com/cli/api-keys\">\n API keys CLI documentation\n </a>{' '}\n before running these calls.\n </p>\n <div className=\"space-y-2\">\n <div>\n <div className=\"font-medium mb-1\">1) Configure environment variables</div>\n <pre className=\"bg-muted p-3 rounded text-xs overflow-auto\"><code>{`export BASE_URL=\"http://localhost:3000/api\"\nexport API_KEY=\"<paste API key secret here>\" # scoped with entities.features\nexport ENTITY_ID=\"${entityId}\"\nexport RECORD_ID=\"<record uuid>\"`}</code></pre>\n <p className=\"text-muted-foreground mt-1\">\n Need a new key? Follow the{' '}\n <a className=\"underline\" target=\"_blank\" rel=\"noreferrer\" href=\"https://docs.openmercato.com/user-guide/api-keys\">\n Managing API keys\n </a>{' '}\n walkthrough or mint one via{' '}\n <a className=\"underline\" target=\"_blank\" rel=\"noreferrer\" href=\"https://docs.openmercato.com/cli/api-keys\">\n mercato api_keys add\n </a>\n .\n </p>\n </div>\n\n <div>\n <div className=\"font-medium mb-1\">2) List records</div>\n <pre className=\"bg-muted p-3 rounded text-xs overflow-auto\"><code>{`curl -s -H \"X-Api-Key: $API_KEY\" \\\n \"$BASE_URL/entities/records?entityId=$ENTITY_ID\" | jq`}</code></pre>\n </div>\n\n <div>\n <div className=\"font-medium mb-1\">3) Read a single record (by id)</div>\n <pre className=\"bg-muted p-3 rounded text-xs overflow-auto\"><code>{`curl -s -H \"X-Api-Key: $API_KEY\" \\\n \"$BASE_URL/entities/records?entityId=$ENTITY_ID&id=$RECORD_ID\" | jq`}</code></pre>\n <p className=\"text-muted-foreground mt-1\">Note: Response is a list; filter by <code>id</code> to get a single item.</p>\n </div>\n\n <div>\n <div className=\"font-medium mb-1\">4) Create a record</div>\n <pre className=\"bg-muted p-3 rounded text-xs overflow-auto\"><code>{`curl -s -X POST \\\n -H \"X-Api-Key: $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\\\"entityId\\\\\": \\\\\"$ENTITY_ID\\\\\",\n \\\\\"values\\\\\": {\n \\\\\"field_one\\\\\": \\\\\"Example\\\\\",\n \\\\\"field_two\\\\\": 123\n }\n }\" \\\n \"$BASE_URL/entities/records\" | jq`}</code></pre>\n <p className=\"text-muted-foreground mt-1\">For custom entities, send field keys without the <code>cf_</code> prefix. The API normalizes this server-side.</p>\n </div>\n\n <div>\n <div className=\"font-medium mb-1\">5) Update a record</div>\n <pre className=\"bg-muted p-3 rounded text-xs overflow-auto\"><code>{`curl -s -X PUT \\\n -H \"X-Api-Key: $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\\\"entityId\\\\\": \\\\\"$ENTITY_ID\\\\\",\n \\\\\"recordId\\\\\": \\\\\"$RECORD_ID\\\\\",\n \\\\\"values\\\\\": {\n \\\\\"field_one\\\\\": \\\\\"Updated\\\\\"\n }\n }\" \\\n \"$BASE_URL/entities/records\" | jq`}</code></pre>\n </div>\n\n <div>\n <div className=\"font-medium mb-1\">6) Delete a record</div>\n <pre className=\"bg-muted p-3 rounded text-xs overflow-auto\"><code>{`curl -s -X DELETE \\\n -H \"X-Api-Key: $API_KEY\" \\\n \"$BASE_URL/entities/records?entityId=$ENTITY_ID&recordId=$RECORD_ID\" | jq`}</code></pre>\n </div>\n\n <div className=\"text-muted-foreground\">\n Security notes:\n <ul className=\"list-disc pl-5 mt-1 space-y-1\">\n <li>All endpoints require a valid API key. Keys inherit tenant, organization, and feature scope.</li>\n <li>Rotate keys regularly and delete unused ones in the admin UI.</li>\n <li>Store the secret in a secure vault; anyone with the header can act within the key&apos;s permissions.</li>\n </ul>\n </div>\n </div>\n </ContextHelp>\n <DataTable\n stickyActionsColumn\n title={`Records: ${entityId}`}\n entityId={entityId}\n actions={actions}\n columns={columns}\n data={data}\n perspective={{ tableId: `entities.user.records.${entityId}` }}\n exporter={exportConfig}\n filters={baseFilters}\n filterValues={filterValues}\n rowActions={(row) => (\n <RowActions\n items={[\n { id: 'edit', label: 'Edit', href: `/backend/entities/user/${encodeURIComponent(entityId)}/records/${encodeURIComponent(String((row as any).id))}` },\n { id: 'delete', label: 'Delete', destructive: true, onSelect: async () => {\n try {\n const confirmed = await confirm({\n title: 'Delete this record?',\n variant: 'destructive',\n })\n if (!confirmed) return\n const deleteCall = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader((row as any).updatedAt),\n () => apiCall(\n `/api/entities/records?entityId=${encodeURIComponent(entityId)}&recordId=${encodeURIComponent(String((row as any).id))}`,\n { method: 'DELETE' },\n ),\n )\n if (!deleteCall.ok) {\n await raiseCrudError(deleteCall.response, 'Failed to delete record')\n }\n const j = await readApiResultOrThrow<RecordsResponse>(\n `/api/entities/records?entityId=${encodeURIComponent(entityId)}&page=${page}&pageSize=${pageSize}`,\n undefined,\n {\n errorMessage: 'Failed to reload records',\n fallback: { items: [], total: 0, page, pageSize, totalPages: 1 },\n },\n )\n setRawData(j.items || [])\n setTotal(j.total || 0)\n setTotalPages(j.totalPages || 1)\n flash('Record has been removed', 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to delete record'\n flash(message, 'error')\n }\n } },\n ]}\n />\n )}\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n searchValue={search}\n onSearchChange={(v) => { setSearch(v); setPage(1) }}\n onFiltersApply={(vals) => { setFilterValues(vals); setPage(1) }}\n onFiltersClear={() => { setFilterValues({}); setPage(1) }}\n pagination={{ page, pageSize, total, totalPages, onPageChange: setPage }}\n isLoading={loading}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["params"]
7
7
  }
@@ -0,0 +1,30 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
4
+ const SYSTEM_ENTITY_RECORDS_BLOCKED_CODE = "system_entity_records_blocked";
5
+ function useRecordsEntityGuard(entityId) {
6
+ const [state, setState] = React.useState(entityId ? "checking" : "allowed");
7
+ React.useEffect(() => {
8
+ if (!entityId) {
9
+ setState("allowed");
10
+ return;
11
+ }
12
+ let cancelled = false;
13
+ setState("checking");
14
+ apiCall(`/api/entities/records?entityId=${encodeURIComponent(entityId)}&page=1&pageSize=1`).then((res) => {
15
+ if (cancelled) return;
16
+ const blocked = res.status === 400 && res.result?.code === SYSTEM_ENTITY_RECORDS_BLOCKED_CODE;
17
+ setState(blocked ? "blocked" : "allowed");
18
+ }).catch(() => {
19
+ if (!cancelled) setState("allowed");
20
+ });
21
+ return () => {
22
+ cancelled = true;
23
+ };
24
+ }, [entityId]);
25
+ return state;
26
+ }
27
+ export {
28
+ useRecordsEntityGuard
29
+ };
30
+ //# sourceMappingURL=useRecordsEntityGuard.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/entities/components/useRecordsEntityGuard.ts"],
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\n\nexport type RecordsEntityGuardState = 'checking' | 'blocked' | 'allowed'\n\n// Mirrors SYSTEM_ENTITY_RECORDS_BLOCKED_CODE from @open-mercato/shared/lib/data/engine \u2014\n// kept as a literal so client bundles do not pull the server-side data engine in.\nconst SYSTEM_ENTITY_RECORDS_BLOCKED_CODE = 'system_entity_records_blocked'\n\n/**\n * The records surface serves custom entities only; the API rejects system\n * (table-backed) entity ids with 400 + `system_entity_records_blocked` (#2939\n * hardening). Records pages are URL-addressable for any entity id, so they probe\n * once and render a dedicated error state instead of a broken table/form.\n * Fails open on transport errors \u2014 the page's own data calls surface those.\n */\nexport function useRecordsEntityGuard(entityId: string): RecordsEntityGuardState {\n const [state, setState] = React.useState<RecordsEntityGuardState>(entityId ? 'checking' : 'allowed')\n React.useEffect(() => {\n if (!entityId) {\n setState('allowed')\n return\n }\n let cancelled = false\n setState('checking')\n apiCall<{ code?: string }>(`/api/entities/records?entityId=${encodeURIComponent(entityId)}&page=1&pageSize=1`)\n .then((res) => {\n if (cancelled) return\n const blocked = res.status === 400 && res.result?.code === SYSTEM_ENTITY_RECORDS_BLOCKED_CODE\n setState(blocked ? 'blocked' : 'allowed')\n })\n .catch(() => {\n if (!cancelled) setState('allowed')\n })\n return () => {\n cancelled = true\n }\n }, [entityId])\n return state\n}\n"],
5
+ "mappings": ";AACA,YAAY,WAAW;AACvB,SAAS,eAAe;AAMxB,MAAM,qCAAqC;AASpC,SAAS,sBAAsB,UAA2C;AAC/E,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAkC,WAAW,aAAa,SAAS;AACnG,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,UAAU;AACb,eAAS,SAAS;AAClB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,aAAS,UAAU;AACnB,YAA2B,kCAAkC,mBAAmB,QAAQ,CAAC,oBAAoB,EAC1G,KAAK,CAAC,QAAQ;AACb,UAAI,UAAW;AACf,YAAM,UAAU,IAAI,WAAW,OAAO,IAAI,QAAQ,SAAS;AAC3D,eAAS,UAAU,YAAY,SAAS;AAAA,IAC1C,CAAC,EACA,MAAM,MAAM;AACX,UAAI,CAAC,UAAW,UAAS,SAAS;AAAA,IACpC,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AACb,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -1,5 +1,5 @@
1
1
  import { SortDir } from "@open-mercato/shared/lib/query/types";
2
- import { resolveEntityTableName } from "@open-mercato/shared/lib/query/engine";
2
+ import { resolveEntityTableName, resolveRegisteredEntityTableName } from "@open-mercato/shared/lib/query/engine";
3
3
  import { sql } from "kysely";
4
4
  import { readCoverageSnapshot, refreshCoverageSnapshot } from "./coverage.js";
5
5
  import { createProfiler, shouldEnableProfiler } from "@open-mercato/shared/lib/profiler";
@@ -144,7 +144,7 @@ class HybridQueryEngine {
144
144
  try {
145
145
  const debugEnabled = this.isDebugVerbosity();
146
146
  if (debugEnabled) this.debug("query:start", { entity });
147
- const isCustom = await this.isCustomEntity(entity);
147
+ const isCustom = opts.forceCustomEntityStorage === true || await this.isCustomEntity(entity);
148
148
  if (isCustom) {
149
149
  if (debugEnabled) this.debug("query:custom-entity", { entity });
150
150
  const section = profiler.section("custom_entity");
@@ -861,6 +861,8 @@ class HybridQueryEngine {
861
861
  const row = await db.selectFrom("custom_entities").select("id").where("entity_id", "=", entity).where("is_active", "=", true).executeTakeFirst();
862
862
  if (row) {
863
863
  result = true;
864
+ } else if (resolveRegisteredEntityTableName(this.em, entity) !== null) {
865
+ result = false;
864
866
  } else {
865
867
  result = await this.hasCustomEntityStorageRows(entity);
866
868
  }