@open-mercato/core 0.6.4-develop.4236.1.9fa6806b34 → 0.6.4-develop.4239.1.4a264a5828
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/business_rules/backend/logs/[id]/page.js +24 -5
- package/dist/modules/business_rules/backend/logs/[id]/page.js.map +2 -2
- package/dist/modules/catalog/api/offers/route.js +15 -5
- package/dist/modules/catalog/api/offers/route.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/[id]/page.js +19 -2
- package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +27 -7
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +27 -7
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/[id]/page.js +29 -8
- package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
- package/dist/modules/progress/acl.js +8 -4
- package/dist/modules/progress/acl.js.map +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.js +24 -6
- package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.js +27 -5
- package/dist/modules/workflows/backend/instances/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.js +25 -6
- package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/business_rules/backend/logs/[id]/page.tsx +32 -7
- package/src/modules/catalog/api/offers/route.ts +20 -5
- package/src/modules/currencies/backend/currencies/[id]/page.tsx +21 -2
- package/src/modules/currencies/i18n/de.json +1 -0
- package/src/modules/currencies/i18n/en.json +1 -0
- package/src/modules/currencies/i18n/es.json +1 -0
- package/src/modules/currencies/i18n/pl.json +1 -0
- package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +34 -11
- package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +34 -11
- package/src/modules/customers/backend/customers/people/[id]/page.tsx +35 -11
- package/src/modules/progress/acl.ts +4 -0
- package/src/modules/workflows/backend/events/[id]/page.tsx +32 -10
- package/src/modules/workflows/backend/instances/[id]/page.tsx +33 -9
- package/src/modules/workflows/backend/tasks/[id]/page.tsx +33 -10
- package/src/modules/workflows/i18n/de.json +1 -0
- package/src/modules/workflows/i18n/en.json +1 -0
- package/src/modules/workflows/i18n/es.json +1 -0
- package/src/modules/workflows/i18n/pl.json +1 -0
|
@@ -9,6 +9,7 @@ import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
|
9
9
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
10
10
|
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
11
11
|
import { JsonDisplay } from "@open-mercato/ui/backend/JsonDisplay";
|
|
12
|
+
import { RecordNotFoundState, ErrorMessage } from "@open-mercato/ui/backend/detail";
|
|
12
13
|
function ExecutionLogDetailPage() {
|
|
13
14
|
const router = useRouter();
|
|
14
15
|
const params = useParams();
|
|
@@ -24,7 +25,11 @@ function ExecutionLogDetailPage() {
|
|
|
24
25
|
queryFn: async () => {
|
|
25
26
|
const response = await apiFetch(`/api/business_rules/logs/${logId}`);
|
|
26
27
|
if (!response.ok) {
|
|
27
|
-
|
|
28
|
+
const httpErr = new Error(
|
|
29
|
+
response.status === 404 ? t("business_rules.logs.errors.notFound", "Execution log not found.") : t("business_rules.logs.errors.fetchFailed")
|
|
30
|
+
);
|
|
31
|
+
httpErr.status = response.status;
|
|
32
|
+
throw httpErr;
|
|
28
33
|
}
|
|
29
34
|
const result = await response.json();
|
|
30
35
|
return result;
|
|
@@ -37,11 +42,25 @@ function ExecutionLogDetailPage() {
|
|
|
37
42
|
/* @__PURE__ */ jsx("span", { children: t("business_rules.logs.detail.loading") })
|
|
38
43
|
] }) }) });
|
|
39
44
|
}
|
|
45
|
+
const isNotFound = !isLoading && error?.status === 404;
|
|
46
|
+
if (isNotFound) {
|
|
47
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
48
|
+
RecordNotFoundState,
|
|
49
|
+
{
|
|
50
|
+
label: t("business_rules.logs.errors.notFound", "Execution log not found."),
|
|
51
|
+
backHref: "/backend/logs",
|
|
52
|
+
backLabel: t("business_rules.logs.backToList", "Back to logs")
|
|
53
|
+
}
|
|
54
|
+
) }) });
|
|
55
|
+
}
|
|
40
56
|
if (error || !log) {
|
|
41
|
-
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
57
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
58
|
+
ErrorMessage,
|
|
59
|
+
{
|
|
60
|
+
label: error?.message ?? t("business_rules.logs.errors.loadFailed"),
|
|
61
|
+
action: /* @__PURE__ */ jsx(Button, { asChild: true, variant: "outline", size: "sm", children: /* @__PURE__ */ jsx(Link, { href: "/backend/logs", children: t("business_rules.logs.backToList", "Back to logs") }) })
|
|
62
|
+
}
|
|
63
|
+
) }) });
|
|
45
64
|
}
|
|
46
65
|
const getResultBadgeClass = (result) => {
|
|
47
66
|
switch (result) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/business_rules/backend/logs/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useParams, useRouter } from 'next/navigation'\nimport { useQuery } from '@tanstack/react-query'\nimport { apiFetch } from '@open-mercato/ui/backend/utils/api'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { JsonDisplay } from '@open-mercato/ui/backend/JsonDisplay'\n\ntype RuleExecutionLog = {\n id: string\n ruleId: string\n rule?: {\n id: string\n ruleId: string\n ruleName: string\n ruleType: string\n } | null\n entityType: string\n entityId: string | null\n eventType: string | null\n executedAt: string\n executionTimeMs: number\n executionResult: 'SUCCESS' | 'FAILURE' | 'ERROR'\n resultValue: any | null\n errorMessage: string | null\n inputContext: any | null\n outputContext: any | null\n executedBy: string | null\n tenantId: string | null\n organizationId: string | null\n}\n\nexport default function ExecutionLogDetailPage() {\n const router = useRouter()\n const params = useParams()\n\n // Handle catch-all route: params.slug = ['logs', 'id']\n let logId: string | undefined\n if (params?.slug && Array.isArray(params.slug)) {\n logId = params.slug[1] // Second element is the ID\n } else if (params?.id) {\n logId = Array.isArray(params.id) ? params.id[0] : params.id\n }\n\n const t = useT()\n\n const { data: log, isLoading, error } = useQuery({\n queryKey: ['business-rules', 'logs', logId],\n queryFn: async () => {\n const response = await apiFetch(`/api/business_rules/logs/${logId}`)\n if (!response.ok) {\n throw new Error(t('business_rules.logs.errors.fetchFailed'))\n }\n const result = await response.json()\n return result as RuleExecutionLog\n },\n enabled: !!logId,\n })\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <div className=\"flex h-[50vh] flex-col items-center justify-center gap-2 text-muted-foreground\">\n <Spinner className=\"h-6 w-6\" />\n <span>{t('business_rules.logs.detail.loading')}</span>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (error || !log) {\n return (\n <Page>\n <PageBody>\n <div className=\"flex h-[50vh] flex-col items-center justify-center gap-2 text-muted-foreground\">\n <p>{error ? t('business_rules.logs.errors.loadFailed') : t('business_rules.logs.errors.notFound')}</p>\n <Button asChild variant=\"outline\">\n <Link href=\"/backend/logs\">{t('business_rules.logs.backToList')}</Link>\n </Button>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n const getResultBadgeClass = (result: string) => {\n switch (result) {\n case 'SUCCESS':\n return 'bg-green-100 text-green-800'\n case 'FAILURE':\n return 'bg-yellow-100 text-yellow-800'\n case 'ERROR':\n return 'bg-red-100 text-red-800'\n default:\n return 'bg-muted text-foreground'\n }\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">\n {t('business_rules.logs.detail.title')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {t('business_rules.logs.detail.logId')}: {log.id}\n </p>\n </div>\n <Button onClick={() => router.push('/backend/logs')} variant=\"outline\">\n {t('business_rules.logs.backToList')}\n </Button>\n </div>\n\n {/* Execution Summary */}\n <div className=\"rounded-lg border bg-card p-6\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {t('business_rules.logs.detail.summary')}\n </h2>\n <dl className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.executedAt')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground\">\n {new Date(log.executedAt).toLocaleString()}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.result')}\n </dt>\n <dd className=\"mt-1\">\n <span\n className={`inline-flex items-center px-2 py-1 rounded text-xs font-medium ${getResultBadgeClass(\n log.executionResult\n )}`}\n >\n {t(`business_rules.logs.result.${log.executionResult.toLowerCase()}`)}\n </span>\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.executionTime')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground\">{log.executionTimeMs}ms</dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.executedBy')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground\">\n {log.executedBy || t('common.unknown')}\n </dd>\n </div>\n </dl>\n </div>\n\n {/* Rule Information */}\n <div className=\"rounded-lg border bg-card p-6\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {t('business_rules.logs.detail.ruleInfo')}\n </h2>\n {log.rule ? (\n <dl className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.ruleName')}\n </dt>\n <dd className=\"mt-1\">\n <Link\n href={`/backend/rules/${log.rule.id}`}\n className=\"text-sm text-primary hover:underline\"\n >\n {log.rule.ruleName}\n </Link>\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.ruleType')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground\">{log.rule.ruleType}</dd>\n </div>\n <div className=\"md:col-span-2\">\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.ruleId')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground font-mono\">{log.rule.ruleId}</dd>\n </div>\n </dl>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{t('business_rules.logs.ruleDeleted')}</p>\n )}\n </div>\n\n {/* Entity Information */}\n <div className=\"rounded-lg border bg-card p-6\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {t('business_rules.logs.detail.entityInfo')}\n </h2>\n <dl className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.entityType')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground\">{log.entityType}</dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.eventType')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground\">\n {log.eventType || t('common.none')}\n </dd>\n </div>\n {log.entityId && (\n <div className=\"md:col-span-2\">\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.entityId')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground font-mono break-all\">\n {log.entityId}\n </dd>\n </div>\n )}\n </dl>\n </div>\n\n {/* Error Message (if present) */}\n {log.errorMessage && (\n <div className=\"rounded-lg border border-destructive bg-destructive/5 p-6\">\n <h2 className=\"text-lg font-semibold mb-4 text-destructive\">\n {t('business_rules.logs.detail.errorMessage')}\n </h2>\n <pre className=\"text-sm text-destructive whitespace-pre-wrap font-mono\">\n {log.errorMessage}\n </pre>\n </div>\n )}\n\n {/* Input Context */}\n {log.inputContext && (\n <JsonDisplay\n data={log.inputContext}\n title={t('business_rules.logs.detail.inputContext')}\n />\n )}\n\n {/* Output Context */}\n {log.outputContext && (\n <JsonDisplay\n data={log.outputContext}\n title={t('business_rules.logs.detail.outputContext')}\n />\n )}\n\n {/* Result Value */}\n {log.resultValue && (\n <JsonDisplay\n data={log.resultValue}\n title={t('business_rules.logs.detail.resultValue')}\n />\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useParams, useRouter } from 'next/navigation'\nimport { useQuery } from '@tanstack/react-query'\nimport { apiFetch } from '@open-mercato/ui/backend/utils/api'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { JsonDisplay } from '@open-mercato/ui/backend/JsonDisplay'\nimport { RecordNotFoundState, ErrorMessage } from '@open-mercato/ui/backend/detail'\n\ntype RuleExecutionLog = {\n id: string\n ruleId: string\n rule?: {\n id: string\n ruleId: string\n ruleName: string\n ruleType: string\n } | null\n entityType: string\n entityId: string | null\n eventType: string | null\n executedAt: string\n executionTimeMs: number\n executionResult: 'SUCCESS' | 'FAILURE' | 'ERROR'\n resultValue: any | null\n errorMessage: string | null\n inputContext: any | null\n outputContext: any | null\n executedBy: string | null\n tenantId: string | null\n organizationId: string | null\n}\n\nexport default function ExecutionLogDetailPage() {\n const router = useRouter()\n const params = useParams()\n\n // Handle catch-all route: params.slug = ['logs', 'id']\n let logId: string | undefined\n if (params?.slug && Array.isArray(params.slug)) {\n logId = params.slug[1] // Second element is the ID\n } else if (params?.id) {\n logId = Array.isArray(params.id) ? params.id[0] : params.id\n }\n\n const t = useT()\n\n const { data: log, isLoading, error } = useQuery({\n queryKey: ['business-rules', 'logs', logId],\n queryFn: async () => {\n const response = await apiFetch(`/api/business_rules/logs/${logId}`)\n if (!response.ok) {\n const httpErr = new Error(\n response.status === 404\n ? t('business_rules.logs.errors.notFound', 'Execution log not found.')\n : t('business_rules.logs.errors.fetchFailed')\n ) as Error & { status: number }\n httpErr.status = response.status\n throw httpErr\n }\n const result = await response.json()\n return result as RuleExecutionLog\n },\n enabled: !!logId,\n })\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <div className=\"flex h-[50vh] flex-col items-center justify-center gap-2 text-muted-foreground\">\n <Spinner className=\"h-6 w-6\" />\n <span>{t('business_rules.logs.detail.loading')}</span>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n const isNotFound = !isLoading && (error as (Error & { status?: number }) | null)?.status === 404\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('business_rules.logs.errors.notFound', 'Execution log not found.')}\n backHref=\"/backend/logs\"\n backLabel={t('business_rules.logs.backToList', 'Back to logs')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !log) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage\n label={(error as Error | null)?.message ?? t('business_rules.logs.errors.loadFailed')}\n action={\n <Button asChild variant=\"outline\" size=\"sm\">\n <Link href=\"/backend/logs\">{t('business_rules.logs.backToList', 'Back to logs')}</Link>\n </Button>\n }\n />\n </PageBody>\n </Page>\n )\n }\n\n const getResultBadgeClass = (result: string) => {\n switch (result) {\n case 'SUCCESS':\n return 'bg-green-100 text-green-800'\n case 'FAILURE':\n return 'bg-yellow-100 text-yellow-800'\n case 'ERROR':\n return 'bg-red-100 text-red-800'\n default:\n return 'bg-muted text-foreground'\n }\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">\n {t('business_rules.logs.detail.title')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {t('business_rules.logs.detail.logId')}: {log.id}\n </p>\n </div>\n <Button onClick={() => router.push('/backend/logs')} variant=\"outline\">\n {t('business_rules.logs.backToList')}\n </Button>\n </div>\n\n {/* Execution Summary */}\n <div className=\"rounded-lg border bg-card p-6\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {t('business_rules.logs.detail.summary')}\n </h2>\n <dl className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.executedAt')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground\">\n {new Date(log.executedAt).toLocaleString()}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.result')}\n </dt>\n <dd className=\"mt-1\">\n <span\n className={`inline-flex items-center px-2 py-1 rounded text-xs font-medium ${getResultBadgeClass(\n log.executionResult\n )}`}\n >\n {t(`business_rules.logs.result.${log.executionResult.toLowerCase()}`)}\n </span>\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.executionTime')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground\">{log.executionTimeMs}ms</dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.executedBy')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground\">\n {log.executedBy || t('common.unknown')}\n </dd>\n </div>\n </dl>\n </div>\n\n {/* Rule Information */}\n <div className=\"rounded-lg border bg-card p-6\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {t('business_rules.logs.detail.ruleInfo')}\n </h2>\n {log.rule ? (\n <dl className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.ruleName')}\n </dt>\n <dd className=\"mt-1\">\n <Link\n href={`/backend/rules/${log.rule.id}`}\n className=\"text-sm text-primary hover:underline\"\n >\n {log.rule.ruleName}\n </Link>\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.ruleType')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground\">{log.rule.ruleType}</dd>\n </div>\n <div className=\"md:col-span-2\">\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.ruleId')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground font-mono\">{log.rule.ruleId}</dd>\n </div>\n </dl>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{t('business_rules.logs.ruleDeleted')}</p>\n )}\n </div>\n\n {/* Entity Information */}\n <div className=\"rounded-lg border bg-card p-6\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {t('business_rules.logs.detail.entityInfo')}\n </h2>\n <dl className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.entityType')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground\">{log.entityType}</dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.eventType')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground\">\n {log.eventType || t('common.none')}\n </dd>\n </div>\n {log.entityId && (\n <div className=\"md:col-span-2\">\n <dt className=\"text-sm font-medium text-muted-foreground\">\n {t('business_rules.logs.fields.entityId')}\n </dt>\n <dd className=\"mt-1 text-sm text-foreground font-mono break-all\">\n {log.entityId}\n </dd>\n </div>\n )}\n </dl>\n </div>\n\n {/* Error Message (if present) */}\n {log.errorMessage && (\n <div className=\"rounded-lg border border-destructive bg-destructive/5 p-6\">\n <h2 className=\"text-lg font-semibold mb-4 text-destructive\">\n {t('business_rules.logs.detail.errorMessage')}\n </h2>\n <pre className=\"text-sm text-destructive whitespace-pre-wrap font-mono\">\n {log.errorMessage}\n </pre>\n </div>\n )}\n\n {/* Input Context */}\n {log.inputContext && (\n <JsonDisplay\n data={log.inputContext}\n title={t('business_rules.logs.detail.inputContext')}\n />\n )}\n\n {/* Output Context */}\n {log.outputContext && (\n <JsonDisplay\n data={log.outputContext}\n title={t('business_rules.logs.detail.outputContext')}\n />\n )}\n\n {/* Result Value */}\n {log.resultValue && (\n <JsonDisplay\n data={log.resultValue}\n title={t('business_rules.logs.detail.resultValue')}\n />\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -75,10 +75,19 @@ async function decorateOffersWithDetails(items, ctx) {
|
|
|
75
75
|
const productIds = items.map((item) => item?.productId ? String(item.productId) : null).filter((value) => !!value);
|
|
76
76
|
if (!offerIds.length && !productIds.length) return;
|
|
77
77
|
const em = ctx.container.resolve("em");
|
|
78
|
+
const scopeTenantId = ctx.auth?.tenantId ?? null;
|
|
79
|
+
if (!scopeTenantId) {
|
|
80
|
+
throw new CrudHttpError(403, "[internal] Missing tenant scope for offer decoration");
|
|
81
|
+
}
|
|
82
|
+
const scopeOrgIds = Array.isArray(ctx.organizationIds) && ctx.organizationIds.length ? Array.from(new Set(ctx.organizationIds)) : ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null ? [ctx.selectedOrganizationId ?? ctx.auth?.orgId] : [];
|
|
83
|
+
const scopeWhere = { tenantId: scopeTenantId };
|
|
84
|
+
if (scopeOrgIds.length === 1) scopeWhere.organizationId = scopeOrgIds[0];
|
|
85
|
+
else if (scopeOrgIds.length > 1) scopeWhere.organizationId = { $in: scopeOrgIds };
|
|
86
|
+
const scope = { tenantId: scopeTenantId, organizationId: scopeOrgIds.length === 1 ? scopeOrgIds[0] : null };
|
|
78
87
|
const [products, prices, defaultVariants] = await Promise.all([
|
|
79
88
|
productIds.length ? em.find(
|
|
80
89
|
CatalogProduct,
|
|
81
|
-
{ id: { $in: productIds } },
|
|
90
|
+
{ id: { $in: productIds }, ...scopeWhere },
|
|
82
91
|
{
|
|
83
92
|
fields: ["id", "title", "description", "defaultMediaId", "defaultMediaUrl", "sku"]
|
|
84
93
|
}
|
|
@@ -86,13 +95,13 @@ async function decorateOffersWithDetails(items, ctx) {
|
|
|
86
95
|
offerIds.length ? findWithDecryption(
|
|
87
96
|
em,
|
|
88
97
|
CatalogProductPrice,
|
|
89
|
-
{ offer: { $in: offerIds } },
|
|
98
|
+
{ offer: { $in: offerIds }, ...scopeWhere },
|
|
90
99
|
{ populate: ["priceKind"] },
|
|
91
|
-
|
|
100
|
+
scope
|
|
92
101
|
) : [],
|
|
93
102
|
productIds.length ? em.find(
|
|
94
103
|
CatalogProductVariant,
|
|
95
|
-
{ product: { $in: productIds }, isDefault: true },
|
|
104
|
+
{ product: { $in: productIds }, isDefault: true, ...scopeWhere },
|
|
96
105
|
{ fields: ["id", "product"] }
|
|
97
106
|
) : []
|
|
98
107
|
]);
|
|
@@ -174,6 +183,7 @@ async function decorateOffersWithDetails(items, ctx) {
|
|
|
174
183
|
CatalogProductPrice,
|
|
175
184
|
{
|
|
176
185
|
offer: null,
|
|
186
|
+
...scopeWhere,
|
|
177
187
|
$and: [
|
|
178
188
|
{ $or: fallbackTargets },
|
|
179
189
|
channelFilterValues.includes(null) ? {
|
|
@@ -185,7 +195,7 @@ async function decorateOffersWithDetails(items, ctx) {
|
|
|
185
195
|
]
|
|
186
196
|
},
|
|
187
197
|
{ populate: ["priceKind"] },
|
|
188
|
-
|
|
198
|
+
scope
|
|
189
199
|
) : [];
|
|
190
200
|
fallbackEntries.forEach((entry) => {
|
|
191
201
|
const entryChannelId = typeof entry.channelId === "string" && entry.channelId.length ? entry.channelId : null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/catalog/api/offers/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { makeCrudRoute, type CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { CatalogOffer, CatalogProduct, CatalogProductPrice, CatalogProductVariant } from '../../data/entities'\nimport { offerCreateSchema, offerUpdateSchema } from '../../data/validators'\nimport { parseScopedCommandInput, resolveCrudRecordId } from '../utils'\nimport { E } from '#generated/entities.ids.generated'\nimport * as F from '#generated/entities/catalog_offer'\nimport { parseIdList } from '../products/route'\nimport { extractAllCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport {\n createCatalogCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n productId: z.string().uuid().optional(),\n channelId: z.string().uuid().optional(),\n channelIds: z.string().optional(),\n id: z.string().uuid().optional(),\n search: z.string().optional(),\n isActive: z.string().optional(),\n withDeleted: z.coerce.boolean().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\ntype OfferListQuery = z.infer<typeof listSchema>\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['sales.channels.manage'] },\n POST: { requireAuth: true, requireFeatures: ['sales.channels.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['sales.channels.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['sales.channels.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nexport function normalizeSearch(term?: string | null): string | null {\n if (!term) return null\n const trimmed = term.trim()\n if (!trimmed.length) return null\n return trimmed\n}\n\nexport function buildOfferFilters(query: OfferListQuery): Record<string, unknown> {\n const filters: Record<string, unknown> = {}\n const searchTerm = normalizeSearch(query.search)\n if (query.id) {\n filters.id = { $eq: query.id }\n }\n if (query.productId) {\n filters.product_id = { $eq: query.productId }\n }\n if (query.channelId) {\n filters.channel_id = { $eq: query.channelId }\n } else {\n const channelIds = parseIdList(query.channelIds)\n if (channelIds.length) {\n filters.channel_id = { $in: channelIds }\n }\n }\n if (searchTerm) {\n const like = `%${escapeLikePattern(searchTerm)}%`\n filters.$or = [{ [F.title]: { $ilike: like } }, { [F.description]: { $ilike: like } }]\n }\n const isActive = parseBooleanToken(query.isActive)\n if (isActive !== null) filters[F.is_active] = isActive\n return filters\n}\n\nexport async function decorateOffersWithDetails(\n items: Record<string, unknown>[],\n ctx: CrudCtx,\n): Promise<void> {\n if (!items.length) return\n const offerIds = items\n .map((item) => (item?.id ? String(item.id) : null))\n .filter((value): value is string => !!value)\n const productIds = items\n .map((item) => (item?.productId ? String(item.productId) : null))\n .filter((value): value is string => !!value)\n if (!offerIds.length && !productIds.length) return\n const em = ctx.container.resolve('em') as EntityManager\n const [products, prices, defaultVariants] = await Promise.all([\n productIds.length\n ? em.find(\n CatalogProduct,\n { id: { $in: productIds } },\n {\n fields: ['id', 'title', 'description', 'defaultMediaId', 'defaultMediaUrl', 'sku'],\n },\n )\n : [],\n offerIds.length\n ? findWithDecryption(\n em,\n CatalogProductPrice,\n { offer: { $in: offerIds } },\n { populate: ['priceKind'] },\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n : [],\n productIds.length\n ? em.find(\n CatalogProductVariant,\n { product: { $in: productIds }, isDefault: true },\n { fields: ['id', 'product'] },\n )\n : [],\n ])\n const productMap = new Map(\n products.map((product) => [\n product.id,\n {\n id: product.id,\n title: product.title,\n defaultMediaId: product.defaultMediaId ?? null,\n defaultMediaUrl: product.defaultMediaUrl ?? null,\n sku: product.sku ?? null,\n },\n ]),\n )\n const priceMap = new Map<string, Array<Record<string, unknown>>>()\n prices.forEach((price) => {\n const offerRef = price.offer\n const offerId =\n typeof offerRef === 'string'\n ? offerRef\n : offerRef && typeof offerRef === 'object' && 'id' in offerRef\n ? (offerRef as { id?: string }).id ?? null\n : null\n if (!offerId) return\n const priceKind = price.priceKind\n const priceKindId =\n typeof priceKind === 'string'\n ? priceKind\n : priceKind && typeof priceKind === 'object'\n ? (priceKind as { id?: string }).id ?? null\n : null\n const priceKindCode =\n priceKind && typeof priceKind === 'object' && 'code' in priceKind\n ? (priceKind as { code?: string }).code ?? null\n : null\n const priceKindTitle =\n priceKind && typeof priceKind === 'object' && 'title' in priceKind\n ? (priceKind as { title?: string }).title ?? null\n : null\n const displayMode =\n priceKind && typeof priceKind === 'object' && 'displayMode' in priceKind\n ? (priceKind as { displayMode?: string }).displayMode ?? 'excluding-tax'\n : 'excluding-tax'\n const bucket = priceMap.get(offerId) ?? []\n bucket.push({\n id: price.id,\n priceKindId,\n priceKindCode,\n priceKindTitle,\n currencyCode: price.currencyCode ?? null,\n unitPriceNet: price.unitPriceNet ?? null,\n unitPriceGross: price.unitPriceGross ?? null,\n displayMode,\n minQuantity: price.minQuantity ?? null,\n maxQuantity: price.maxQuantity ?? null,\n })\n priceMap.set(offerId, bucket)\n })\n const variantToProductMap = new Map<string, string>()\n defaultVariants.forEach((variant) => {\n const variantId = typeof variant.id === 'string' ? variant.id : null\n const productRef =\n typeof variant.product === 'string'\n ? variant.product\n : variant.product && typeof variant.product === 'object' && 'id' in variant.product\n ? (variant.product as { id?: string }).id ?? null\n : null\n if (variantId && productRef) {\n variantToProductMap.set(variantId, productRef)\n }\n })\n const DEFAULT_CHANNEL_KEY = '__default__'\n type ProductFallbackPrice = { prices: Record<string, unknown>[]; priority: number }\n const productChannelPriceMap = new Map<string, Map<string, ProductFallbackPrice>>()\n const assignFallbackPrice = (productRef: string | null, channelRef: string | null, payload: Record<string, unknown>, priority: number) => {\n if (!productRef) return\n const bucket = productChannelPriceMap.get(productRef) ?? new Map()\n const effectiveChannel = channelRef ?? DEFAULT_CHANNEL_KEY\n const existing = bucket.get(effectiveChannel)\n if (existing && existing.priority > priority) return\n if (existing && existing.priority === priority) {\n bucket.set(effectiveChannel, { prices: [...existing.prices, payload], priority })\n productChannelPriceMap.set(productRef, bucket)\n return\n }\n bucket.set(effectiveChannel, { prices: [payload], priority })\n productChannelPriceMap.set(productRef, bucket)\n }\n const channelIds = Array.from(new Set(\n items\n .map((item) => {\n if (typeof item?.channelId === 'string') return item.channelId\n if (typeof item?.channel_id === 'string') return item.channel_id\n return null\n })\n .filter((value): value is string => !!value),\n ))\n const channelFilterValues = channelIds.length ? [...channelIds, null] : [null]\n const fallbackTargets: Array<Record<string, unknown>> = []\n if (productIds.length) fallbackTargets.push({ product: { $in: productIds } })\n const defaultVariantIds = Array.from(variantToProductMap.keys())\n if (defaultVariantIds.length) fallbackTargets.push({ variant: { $in: defaultVariantIds } })\n const fallbackEntries = fallbackTargets.length\n ? await findWithDecryption(\n em,\n CatalogProductPrice,\n {\n offer: null,\n $and: [\n { $or: fallbackTargets },\n channelFilterValues.includes(null)\n ? {\n $or: [\n { channelId: { $in: channelFilterValues.filter((id): id is string => typeof id === 'string') } },\n { channelId: null },\n ],\n }\n : { channelId: { $in: channelFilterValues } },\n ],\n },\n { populate: ['priceKind'] },\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n : []\n fallbackEntries.forEach((entry) => {\n const entryChannelId = typeof entry.channelId === 'string' && entry.channelId.length\n ? entry.channelId\n : null\n const priceKind = entry.priceKind ?? null\n const priceKindId =\n typeof priceKind?.id === 'string'\n ? priceKind.id\n : null\n const priceKindCode =\n typeof priceKind?.code === 'string'\n ? priceKind.code\n : null\n const priceKindTitle =\n typeof priceKind?.title === 'string'\n ? priceKind.title\n : null\n const displayMode = typeof priceKind?.displayMode === 'string'\n ? priceKind.displayMode\n : typeof (priceKind as any)?.display_mode === 'string'\n ? (priceKind as any).display_mode\n : 'excluding-tax'\n const payload = {\n priceKindId,\n priceKindCode,\n priceKindTitle,\n currencyCode: entry.currencyCode ?? null,\n unitPriceNet: entry.unitPriceNet ?? null,\n unitPriceGross: entry.unitPriceGross ?? null,\n displayMode,\n }\n const variantRef =\n typeof entry.variant === 'string'\n ? entry.variant\n : entry.variant && typeof entry.variant === 'object' && 'id' in entry.variant\n ? (entry.variant as { id?: string }).id ?? null\n : null\n if (variantRef) {\n const variantProduct = variantToProductMap.get(variantRef)\n const productRef =\n variantProduct\n ?? (typeof entry.product === 'string'\n ? entry.product\n : entry.product && typeof entry.product === 'object' && 'id' in entry.product\n ? (entry.product as { id?: string }).id ?? null\n : null)\n const priority = entryChannelId ? 4 : 3\n assignFallbackPrice(productRef, entryChannelId, payload, priority)\n return\n }\n const productRef =\n typeof entry.product === 'string'\n ? entry.product\n : entry.product && typeof entry.product === 'object' && 'id' in entry.product\n ? (entry.product as { id?: string }).id ?? null\n : null\n const priority = entryChannelId ? 2 : 1\n assignFallbackPrice(productRef, entryChannelId, payload, priority)\n })\n items.forEach((item) => {\n const productId = String(item?.productId ?? '')\n item.product = productId ? productMap.get(productId) ?? null : null\n item.prices = priceMap.get(String(item?.id ?? '')) ?? []\n const rowChannelId = typeof item?.channelId === 'string'\n ? item.channelId\n : typeof item?.channel_id === 'string'\n ? item.channel_id\n : null\n const bucket = productChannelPriceMap.get(productId)\n const channelKey = rowChannelId ?? DEFAULT_CHANNEL_KEY\n const channelPrice = bucket?.get(channelKey) ?? null\n const defaultPrice = bucket?.get(DEFAULT_CHANNEL_KEY) ?? null\n const effectivePrice = channelPrice ?? defaultPrice ?? null\n item.productChannelPrice = effectivePrice?.prices?.[0] ?? null\n item.productDefaultPrices = effectivePrice?.prices ?? []\n })\n}\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: CatalogOffer,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: { entityType: E.catalog.catalog_offer },\n list: {\n schema: listSchema,\n entityId: E.catalog.catalog_offer,\n fields: [\n F.id,\n 'product_id',\n F.organization_id,\n F.tenant_id,\n F.channel_id,\n F.title,\n F.description,\n 'default_media_id',\n 'default_media_url',\n F.metadata,\n F.is_active,\n F.created_at,\n F.updated_at,\n ],\n sortFieldMap: {\n title: F.title,\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n },\n buildFilters: async (query) => buildOfferFilters(query),\n transformItem: (item: Record<string, unknown>) => {\n if (!item) return item\n const cfEntries = extractAllCustomFieldEntries(item)\n const base = {\n id: item.id,\n productId: item.product_id ?? null,\n organizationId: item.organization_id ?? null,\n tenantId: item.tenant_id ?? null,\n channelId: item.channel_id ?? null,\n title: item.title ?? '',\n description: item.description ?? null,\n defaultMediaId: item.default_media_id ?? null,\n defaultMediaUrl: item.default_media_url ?? null,\n metadata: item.metadata ?? null,\n isActive: item.is_active ?? false,\n createdAt: item.created_at,\n updatedAt: item.updated_at,\n }\n return Object.keys(cfEntries).length ? { ...base, ...cfEntries } : base\n },\n },\n actions: {\n create: {\n commandId: 'catalog.offers.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(offerCreateSchema, raw ?? {}, ctx, translate)\n },\n response: ({ result }) => ({ id: result?.offerId ?? null }),\n status: 201,\n },\n update: {\n commandId: 'catalog.offers.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(offerUpdateSchema, raw ?? {}, ctx, translate)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'catalog.offers.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id = resolveCrudRecordId(parsed, ctx, translate)\n if (!id) {\n throw new CrudHttpError(400, { error: translate('catalog.errors.id_required', 'Offer id is required.') })\n }\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n afterList: async (payload, ctx) => {\n const items = Array.isArray(payload.items) ? payload.items : []\n if (!items.length) return\n await decorateOffersWithDetails(items, ctx)\n },\n },\n})\n\nexport const GET = crud.GET\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n\nconst offerListItemSchema = z.object({\n id: z.string().uuid(),\n productId: z.string().uuid().nullable().optional(),\n organizationId: z.string().uuid().nullable().optional(),\n tenantId: z.string().uuid().nullable().optional(),\n channelId: z.string().uuid().nullable().optional(),\n title: z.string(),\n description: z.string().nullable().optional(),\n defaultMediaId: z.string().uuid().nullable().optional(),\n defaultMediaUrl: z.string().nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n isActive: z.boolean().nullable().optional(),\n createdAt: z.string().nullable().optional(),\n updatedAt: z.string().nullable().optional(),\n product: z.record(z.string(), z.unknown()).nullable().optional(),\n prices: z.array(z.record(z.string(), z.unknown())).optional(),\n productChannelPrice: z.record(z.string(), z.unknown()).nullable().optional(),\n productDefaultPrices: z.array(z.record(z.string(), z.unknown())).optional(),\n})\n\nexport const openApi = createCatalogCrudOpenApi({\n resourceName: 'Offer',\n pluralName: 'Offers',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(offerListItemSchema),\n create: {\n schema: offerCreateSchema,\n description: 'Creates a new offer linking a product to a sales channel.',\n },\n update: {\n schema: offerUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates an existing offer by id.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes an offer by id.',\n },\n})\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { makeCrudRoute, type CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { CatalogOffer, CatalogProduct, CatalogProductPrice, CatalogProductVariant } from '../../data/entities'\nimport { offerCreateSchema, offerUpdateSchema } from '../../data/validators'\nimport { parseScopedCommandInput, resolveCrudRecordId } from '../utils'\nimport { E } from '#generated/entities.ids.generated'\nimport * as F from '#generated/entities/catalog_offer'\nimport { parseIdList } from '../products/route'\nimport { extractAllCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport {\n createCatalogCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n productId: z.string().uuid().optional(),\n channelId: z.string().uuid().optional(),\n channelIds: z.string().optional(),\n id: z.string().uuid().optional(),\n search: z.string().optional(),\n isActive: z.string().optional(),\n withDeleted: z.coerce.boolean().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\ntype OfferListQuery = z.infer<typeof listSchema>\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['sales.channels.manage'] },\n POST: { requireAuth: true, requireFeatures: ['sales.channels.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['sales.channels.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['sales.channels.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nexport function normalizeSearch(term?: string | null): string | null {\n if (!term) return null\n const trimmed = term.trim()\n if (!trimmed.length) return null\n return trimmed\n}\n\nexport function buildOfferFilters(query: OfferListQuery): Record<string, unknown> {\n const filters: Record<string, unknown> = {}\n const searchTerm = normalizeSearch(query.search)\n if (query.id) {\n filters.id = { $eq: query.id }\n }\n if (query.productId) {\n filters.product_id = { $eq: query.productId }\n }\n if (query.channelId) {\n filters.channel_id = { $eq: query.channelId }\n } else {\n const channelIds = parseIdList(query.channelIds)\n if (channelIds.length) {\n filters.channel_id = { $in: channelIds }\n }\n }\n if (searchTerm) {\n const like = `%${escapeLikePattern(searchTerm)}%`\n filters.$or = [{ [F.title]: { $ilike: like } }, { [F.description]: { $ilike: like } }]\n }\n const isActive = parseBooleanToken(query.isActive)\n if (isActive !== null) filters[F.is_active] = isActive\n return filters\n}\n\nexport async function decorateOffersWithDetails(\n items: Record<string, unknown>[],\n ctx: CrudCtx,\n): Promise<void> {\n if (!items.length) return\n const offerIds = items\n .map((item) => (item?.id ? String(item.id) : null))\n .filter((value): value is string => !!value)\n const productIds = items\n .map((item) => (item?.productId ? String(item.productId) : null))\n .filter((value): value is string => !!value)\n if (!offerIds.length && !productIds.length) return\n const em = ctx.container.resolve('em') as EntityManager\n const scopeTenantId = ctx.auth?.tenantId ?? null\n if (!scopeTenantId) {\n throw new CrudHttpError(403, '[internal] Missing tenant scope for offer decoration')\n }\n const scopeOrgIds =\n Array.isArray(ctx.organizationIds) && ctx.organizationIds.length\n ? Array.from(new Set(ctx.organizationIds))\n : (ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null)\n ? [(ctx.selectedOrganizationId ?? ctx.auth?.orgId) as string]\n : []\n const scopeWhere: Record<string, unknown> = { tenantId: scopeTenantId }\n if (scopeOrgIds.length === 1) scopeWhere.organizationId = scopeOrgIds[0]\n else if (scopeOrgIds.length > 1) scopeWhere.organizationId = { $in: scopeOrgIds }\n const scope = { tenantId: scopeTenantId, organizationId: scopeOrgIds.length === 1 ? scopeOrgIds[0] : null }\n const [products, prices, defaultVariants] = await Promise.all([\n productIds.length\n ? em.find(\n CatalogProduct,\n { id: { $in: productIds }, ...scopeWhere },\n {\n fields: ['id', 'title', 'description', 'defaultMediaId', 'defaultMediaUrl', 'sku'],\n },\n )\n : [],\n offerIds.length\n ? findWithDecryption(\n em,\n CatalogProductPrice,\n { offer: { $in: offerIds }, ...scopeWhere },\n { populate: ['priceKind'] },\n scope,\n )\n : [],\n productIds.length\n ? em.find(\n CatalogProductVariant,\n { product: { $in: productIds }, isDefault: true, ...scopeWhere },\n { fields: ['id', 'product'] },\n )\n : [],\n ])\n const productMap = new Map(\n products.map((product) => [\n product.id,\n {\n id: product.id,\n title: product.title,\n defaultMediaId: product.defaultMediaId ?? null,\n defaultMediaUrl: product.defaultMediaUrl ?? null,\n sku: product.sku ?? null,\n },\n ]),\n )\n const priceMap = new Map<string, Array<Record<string, unknown>>>()\n prices.forEach((price) => {\n const offerRef = price.offer\n const offerId =\n typeof offerRef === 'string'\n ? offerRef\n : offerRef && typeof offerRef === 'object' && 'id' in offerRef\n ? (offerRef as { id?: string }).id ?? null\n : null\n if (!offerId) return\n const priceKind = price.priceKind\n const priceKindId =\n typeof priceKind === 'string'\n ? priceKind\n : priceKind && typeof priceKind === 'object'\n ? (priceKind as { id?: string }).id ?? null\n : null\n const priceKindCode =\n priceKind && typeof priceKind === 'object' && 'code' in priceKind\n ? (priceKind as { code?: string }).code ?? null\n : null\n const priceKindTitle =\n priceKind && typeof priceKind === 'object' && 'title' in priceKind\n ? (priceKind as { title?: string }).title ?? null\n : null\n const displayMode =\n priceKind && typeof priceKind === 'object' && 'displayMode' in priceKind\n ? (priceKind as { displayMode?: string }).displayMode ?? 'excluding-tax'\n : 'excluding-tax'\n const bucket = priceMap.get(offerId) ?? []\n bucket.push({\n id: price.id,\n priceKindId,\n priceKindCode,\n priceKindTitle,\n currencyCode: price.currencyCode ?? null,\n unitPriceNet: price.unitPriceNet ?? null,\n unitPriceGross: price.unitPriceGross ?? null,\n displayMode,\n minQuantity: price.minQuantity ?? null,\n maxQuantity: price.maxQuantity ?? null,\n })\n priceMap.set(offerId, bucket)\n })\n const variantToProductMap = new Map<string, string>()\n defaultVariants.forEach((variant) => {\n const variantId = typeof variant.id === 'string' ? variant.id : null\n const productRef =\n typeof variant.product === 'string'\n ? variant.product\n : variant.product && typeof variant.product === 'object' && 'id' in variant.product\n ? (variant.product as { id?: string }).id ?? null\n : null\n if (variantId && productRef) {\n variantToProductMap.set(variantId, productRef)\n }\n })\n const DEFAULT_CHANNEL_KEY = '__default__'\n type ProductFallbackPrice = { prices: Record<string, unknown>[]; priority: number }\n const productChannelPriceMap = new Map<string, Map<string, ProductFallbackPrice>>()\n const assignFallbackPrice = (productRef: string | null, channelRef: string | null, payload: Record<string, unknown>, priority: number) => {\n if (!productRef) return\n const bucket = productChannelPriceMap.get(productRef) ?? new Map()\n const effectiveChannel = channelRef ?? DEFAULT_CHANNEL_KEY\n const existing = bucket.get(effectiveChannel)\n if (existing && existing.priority > priority) return\n if (existing && existing.priority === priority) {\n bucket.set(effectiveChannel, { prices: [...existing.prices, payload], priority })\n productChannelPriceMap.set(productRef, bucket)\n return\n }\n bucket.set(effectiveChannel, { prices: [payload], priority })\n productChannelPriceMap.set(productRef, bucket)\n }\n const channelIds = Array.from(new Set(\n items\n .map((item) => {\n if (typeof item?.channelId === 'string') return item.channelId\n if (typeof item?.channel_id === 'string') return item.channel_id\n return null\n })\n .filter((value): value is string => !!value),\n ))\n const channelFilterValues = channelIds.length ? [...channelIds, null] : [null]\n const fallbackTargets: Array<Record<string, unknown>> = []\n if (productIds.length) fallbackTargets.push({ product: { $in: productIds } })\n const defaultVariantIds = Array.from(variantToProductMap.keys())\n if (defaultVariantIds.length) fallbackTargets.push({ variant: { $in: defaultVariantIds } })\n const fallbackEntries = fallbackTargets.length\n ? await findWithDecryption(\n em,\n CatalogProductPrice,\n {\n offer: null,\n ...scopeWhere,\n $and: [\n { $or: fallbackTargets },\n channelFilterValues.includes(null)\n ? {\n $or: [\n { channelId: { $in: channelFilterValues.filter((id): id is string => typeof id === 'string') } },\n { channelId: null },\n ],\n }\n : { channelId: { $in: channelFilterValues } },\n ],\n },\n { populate: ['priceKind'] },\n scope,\n )\n : []\n fallbackEntries.forEach((entry) => {\n const entryChannelId = typeof entry.channelId === 'string' && entry.channelId.length\n ? entry.channelId\n : null\n const priceKind = entry.priceKind ?? null\n const priceKindId =\n typeof priceKind?.id === 'string'\n ? priceKind.id\n : null\n const priceKindCode =\n typeof priceKind?.code === 'string'\n ? priceKind.code\n : null\n const priceKindTitle =\n typeof priceKind?.title === 'string'\n ? priceKind.title\n : null\n const displayMode = typeof priceKind?.displayMode === 'string'\n ? priceKind.displayMode\n : typeof (priceKind as any)?.display_mode === 'string'\n ? (priceKind as any).display_mode\n : 'excluding-tax'\n const payload = {\n priceKindId,\n priceKindCode,\n priceKindTitle,\n currencyCode: entry.currencyCode ?? null,\n unitPriceNet: entry.unitPriceNet ?? null,\n unitPriceGross: entry.unitPriceGross ?? null,\n displayMode,\n }\n const variantRef =\n typeof entry.variant === 'string'\n ? entry.variant\n : entry.variant && typeof entry.variant === 'object' && 'id' in entry.variant\n ? (entry.variant as { id?: string }).id ?? null\n : null\n if (variantRef) {\n const variantProduct = variantToProductMap.get(variantRef)\n const productRef =\n variantProduct\n ?? (typeof entry.product === 'string'\n ? entry.product\n : entry.product && typeof entry.product === 'object' && 'id' in entry.product\n ? (entry.product as { id?: string }).id ?? null\n : null)\n const priority = entryChannelId ? 4 : 3\n assignFallbackPrice(productRef, entryChannelId, payload, priority)\n return\n }\n const productRef =\n typeof entry.product === 'string'\n ? entry.product\n : entry.product && typeof entry.product === 'object' && 'id' in entry.product\n ? (entry.product as { id?: string }).id ?? null\n : null\n const priority = entryChannelId ? 2 : 1\n assignFallbackPrice(productRef, entryChannelId, payload, priority)\n })\n items.forEach((item) => {\n const productId = String(item?.productId ?? '')\n item.product = productId ? productMap.get(productId) ?? null : null\n item.prices = priceMap.get(String(item?.id ?? '')) ?? []\n const rowChannelId = typeof item?.channelId === 'string'\n ? item.channelId\n : typeof item?.channel_id === 'string'\n ? item.channel_id\n : null\n const bucket = productChannelPriceMap.get(productId)\n const channelKey = rowChannelId ?? DEFAULT_CHANNEL_KEY\n const channelPrice = bucket?.get(channelKey) ?? null\n const defaultPrice = bucket?.get(DEFAULT_CHANNEL_KEY) ?? null\n const effectivePrice = channelPrice ?? defaultPrice ?? null\n item.productChannelPrice = effectivePrice?.prices?.[0] ?? null\n item.productDefaultPrices = effectivePrice?.prices ?? []\n })\n}\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: CatalogOffer,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: { entityType: E.catalog.catalog_offer },\n list: {\n schema: listSchema,\n entityId: E.catalog.catalog_offer,\n fields: [\n F.id,\n 'product_id',\n F.organization_id,\n F.tenant_id,\n F.channel_id,\n F.title,\n F.description,\n 'default_media_id',\n 'default_media_url',\n F.metadata,\n F.is_active,\n F.created_at,\n F.updated_at,\n ],\n sortFieldMap: {\n title: F.title,\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n },\n buildFilters: async (query) => buildOfferFilters(query),\n transformItem: (item: Record<string, unknown>) => {\n if (!item) return item\n const cfEntries = extractAllCustomFieldEntries(item)\n const base = {\n id: item.id,\n productId: item.product_id ?? null,\n organizationId: item.organization_id ?? null,\n tenantId: item.tenant_id ?? null,\n channelId: item.channel_id ?? null,\n title: item.title ?? '',\n description: item.description ?? null,\n defaultMediaId: item.default_media_id ?? null,\n defaultMediaUrl: item.default_media_url ?? null,\n metadata: item.metadata ?? null,\n isActive: item.is_active ?? false,\n createdAt: item.created_at,\n updatedAt: item.updated_at,\n }\n return Object.keys(cfEntries).length ? { ...base, ...cfEntries } : base\n },\n },\n actions: {\n create: {\n commandId: 'catalog.offers.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(offerCreateSchema, raw ?? {}, ctx, translate)\n },\n response: ({ result }) => ({ id: result?.offerId ?? null }),\n status: 201,\n },\n update: {\n commandId: 'catalog.offers.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(offerUpdateSchema, raw ?? {}, ctx, translate)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'catalog.offers.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id = resolveCrudRecordId(parsed, ctx, translate)\n if (!id) {\n throw new CrudHttpError(400, { error: translate('catalog.errors.id_required', 'Offer id is required.') })\n }\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n afterList: async (payload, ctx) => {\n const items = Array.isArray(payload.items) ? payload.items : []\n if (!items.length) return\n await decorateOffersWithDetails(items, ctx)\n },\n },\n})\n\nexport const GET = crud.GET\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n\nconst offerListItemSchema = z.object({\n id: z.string().uuid(),\n productId: z.string().uuid().nullable().optional(),\n organizationId: z.string().uuid().nullable().optional(),\n tenantId: z.string().uuid().nullable().optional(),\n channelId: z.string().uuid().nullable().optional(),\n title: z.string(),\n description: z.string().nullable().optional(),\n defaultMediaId: z.string().uuid().nullable().optional(),\n defaultMediaUrl: z.string().nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n isActive: z.boolean().nullable().optional(),\n createdAt: z.string().nullable().optional(),\n updatedAt: z.string().nullable().optional(),\n product: z.record(z.string(), z.unknown()).nullable().optional(),\n prices: z.array(z.record(z.string(), z.unknown())).optional(),\n productChannelPrice: z.record(z.string(), z.unknown()).nullable().optional(),\n productDefaultPrices: z.array(z.record(z.string(), z.unknown())).optional(),\n})\n\nexport const openApi = createCatalogCrudOpenApi({\n resourceName: 'Offer',\n pluralName: 'Offers',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(offerListItemSchema),\n create: {\n schema: offerCreateSchema,\n description: 'Creates a new offer linking a product to a sales channel.',\n },\n update: {\n schema: offerUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates an existing offer by id.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes an offer by id.',\n },\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["productRef", "priority"]
|
|
7
7
|
}
|
|
@@ -11,6 +11,7 @@ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
|
11
11
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
12
12
|
import { SendObjectMessageDialog } from "@open-mercato/ui/backend/messages";
|
|
13
13
|
import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
|
|
14
|
+
import { RecordNotFoundState, ErrorMessage } from "@open-mercato/ui/backend/detail";
|
|
14
15
|
function EditCurrencyPage({ params }) {
|
|
15
16
|
const t = useT();
|
|
16
17
|
const router = useRouter();
|
|
@@ -18,14 +19,17 @@ function EditCurrencyPage({ params }) {
|
|
|
18
19
|
const [currency, setCurrency] = React.useState(null);
|
|
19
20
|
const [loading, setLoading] = React.useState(true);
|
|
20
21
|
const [error, setError] = React.useState(null);
|
|
22
|
+
const [isNotFound, setIsNotFound] = React.useState(false);
|
|
21
23
|
React.useEffect(() => {
|
|
22
24
|
async function loadCurrency() {
|
|
23
25
|
try {
|
|
24
26
|
const response = await apiCall(`/api/currencies/currencies?id=${params?.id}`);
|
|
25
27
|
if (response.ok && response.result && response.result.items.length > 0) {
|
|
26
28
|
setCurrency(response.result.items[0]);
|
|
29
|
+
} else if (!response.ok) {
|
|
30
|
+
setError(t("currencies.form.errors.load"));
|
|
27
31
|
} else {
|
|
28
|
-
|
|
32
|
+
setIsNotFound(true);
|
|
29
33
|
}
|
|
30
34
|
} catch (err) {
|
|
31
35
|
setError(t("currencies.form.errors.load"));
|
|
@@ -132,9 +136,22 @@ function EditCurrencyPage({ params }) {
|
|
|
132
136
|
ConfirmDialogElement
|
|
133
137
|
] });
|
|
134
138
|
}
|
|
139
|
+
if (isNotFound) {
|
|
140
|
+
return /* @__PURE__ */ jsxs(Page, { children: [
|
|
141
|
+
/* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
142
|
+
RecordNotFoundState,
|
|
143
|
+
{
|
|
144
|
+
label: t("currencies.form.errors.notFound", "Currency not found."),
|
|
145
|
+
backHref: "/backend/currencies",
|
|
146
|
+
backLabel: t("currencies.form.actions.backToList", "Back to currencies")
|
|
147
|
+
}
|
|
148
|
+
) }),
|
|
149
|
+
ConfirmDialogElement
|
|
150
|
+
] });
|
|
151
|
+
}
|
|
135
152
|
if (error || !currency) {
|
|
136
153
|
return /* @__PURE__ */ jsxs(Page, { children: [
|
|
137
|
-
/* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
154
|
+
/* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(ErrorMessage, { label: error ?? t("currencies.form.errors.notFound", "Currency not found.") }) }),
|
|
138
155
|
ConfirmDialogElement
|
|
139
156
|
] });
|
|
140
157
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/currencies/backend/currencies/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useRouter, useParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { DataLoader } from '@open-mercato/ui/primitives/DataLoader'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\n\ntype CurrencyData = {\n id: string\n code: string\n name: string\n symbol: string | null\n decimalPlaces: number\n thousandsSeparator: string | null\n decimalSeparator: string | null\n isBase: boolean\n isActive: boolean\n organizationId: string\n tenantId: string\n}\n\nexport default function EditCurrencyPage({ params }: { params?: { id?: string } }) {\n const t = useT()\n const router = useRouter()\n const { confirm: confirmDialog, ConfirmDialogElement } = useConfirmDialog()\n\n const [currency, setCurrency] = React.useState<CurrencyData | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n async function loadCurrency() {\n try {\n const response = await apiCall<{ items: CurrencyData[] }>(`/api/currencies/currencies?id=${params?.id}`)\n if (response.ok && response.result && response.result.items.length > 0) {\n setCurrency(response.result.items[0])\n } else {\n setError(t('currencies.form.errors.
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useRouter, useParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { DataLoader } from '@open-mercato/ui/primitives/DataLoader'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { RecordNotFoundState, ErrorMessage } from '@open-mercato/ui/backend/detail'\n\ntype CurrencyData = {\n id: string\n code: string\n name: string\n symbol: string | null\n decimalPlaces: number\n thousandsSeparator: string | null\n decimalSeparator: string | null\n isBase: boolean\n isActive: boolean\n organizationId: string\n tenantId: string\n}\n\nexport default function EditCurrencyPage({ params }: { params?: { id?: string } }) {\n const t = useT()\n const router = useRouter()\n const { confirm: confirmDialog, ConfirmDialogElement } = useConfirmDialog()\n\n const [currency, setCurrency] = React.useState<CurrencyData | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n\n React.useEffect(() => {\n async function loadCurrency() {\n try {\n const response = await apiCall<{ items: CurrencyData[] }>(`/api/currencies/currencies?id=${params?.id}`)\n if (response.ok && response.result && response.result.items.length > 0) {\n setCurrency(response.result.items[0])\n } else if (!response.ok) {\n setError(t('currencies.form.errors.load'))\n } else {\n setIsNotFound(true)\n }\n } catch (err) {\n setError(t('currencies.form.errors.load'))\n } finally {\n setLoading(false)\n }\n }\n loadCurrency()\n }, [params, t])\n\n const groups = React.useMemo<CrudFormGroup[]>(\n () => [\n {\n id: 'basic',\n column: 1,\n title: t('currencies.form.group.details'),\n fields: [\n {\n id: 'code',\n type: 'text',\n label: t('currencies.form.field.code'),\n placeholder: t('currencies.form.field.codePlaceholder'),\n required: true,\n maxLength: 3,\n helpText: t('currencies.form.field.codeHelp'),\n },\n {\n id: 'name',\n type: 'text',\n label: t('currencies.form.field.name'),\n placeholder: t('currencies.form.field.namePlaceholder'),\n required: true,\n },\n {\n id: 'symbol',\n type: 'text',\n label: t('currencies.form.field.symbol'),\n placeholder: t('currencies.form.field.symbolPlaceholder'),\n },\n ],\n },\n {\n id: 'formatting',\n column: 2,\n title: t('currencies.form.group.formatting'),\n fields: [\n {\n id: 'decimalPlaces',\n type: 'number',\n label: t('currencies.form.field.decimalPlaces'),\n min: 0,\n max: 8,\n },\n {\n id: 'thousandsSeparator',\n type: 'text',\n label: t('currencies.form.field.thousandsSeparator'),\n placeholder: ',',\n maxLength: 5,\n },\n {\n id: 'decimalSeparator',\n type: 'text',\n label: t('currencies.form.field.decimalSeparator'),\n placeholder: '.',\n maxLength: 5,\n },\n {\n id: 'isBase',\n type: 'checkbox',\n label: t('currencies.form.field.isBase'),\n },\n {\n id: 'isActive',\n type: 'checkbox',\n label: t('currencies.form.field.isActive'),\n },\n ],\n },\n ],\n [t]\n )\n\n const handleDelete = React.useCallback(async () => {\n if (!currency) return\n\n const confirmed = await confirmDialog({\n title: t('currencies.list.confirmDelete', { code: currency.code }),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n await apiCall('/api/currencies/currencies', {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id: currency.id, organizationId: currency.organizationId, tenantId: currency.tenantId }),\n })\n\n flash(t('currencies.flash.deleted'), 'success')\n router.push('/backend/currencies')\n } catch (error) {\n flash(t('currencies.flash.deleteError'), 'error')\n }\n }, [currency, t, router, confirmDialog])\n\n if (loading) {\n return (\n <Page>\n <PageBody>\n <div className=\"flex items-center justify-center p-8\">\n <div className=\"text-muted-foreground\">{t('currencies.form.loading')}</div>\n </div>\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('currencies.form.errors.notFound', 'Currency not found.')}\n backHref=\"/backend/currencies\"\n backLabel={t('currencies.form.actions.backToList', 'Back to currencies')}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n }\n\n if (error || !currency) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error ?? t('currencies.form.errors.notFound', 'Currency not found.')} />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n title={t('currencies.edit.title')}\n backHref=\"/backend/currencies\"\n versionHistory={{ resourceKind: 'currencies.currency', resourceId: currency.id }}\n extraActions={(\n <SendObjectMessageDialog\n object={{\n entityModule: 'currencies',\n entityType: 'currency',\n entityId: currency.id,\n previewData: {\n title: currency.name,\n subtitle: currency.code,\n metadata: {\n [t('currencies.form.field.code')]: currency.code,\n [t('currencies.form.field.name')]: currency.name,\n [t('currencies.form.field.symbol')]: currency.symbol || '-',\n },\n },\n }}\n viewHref={`/backend/currencies/${currency.id}`}\n />\n )}\n fields={[]}\n groups={groups}\n initialValues={{\n code: currency.code,\n name: currency.name,\n symbol: currency.symbol || '',\n decimalPlaces: currency.decimalPlaces,\n thousandsSeparator: currency.thousandsSeparator || '',\n decimalSeparator: currency.decimalSeparator || '',\n isBase: currency.isBase,\n isActive: currency.isActive,\n }}\n submitLabel={t('currencies.form.action.save')}\n cancelHref=\"/backend/currencies\"\n onSubmit={async (values) => {\n // Validate currency code\n const code = String(values.code || '').trim().toUpperCase()\n if (!/^[A-Z]{3}$/.test(code)) {\n throw createCrudFormError(t('currencies.form.errors.codeFormat'), {\n code: t('currencies.form.errors.codeFormat'),\n })\n }\n\n const payload = {\n id: currency.id,\n code,\n name: String(values.name || '').trim(),\n symbol: values.symbol ? String(values.symbol).trim() : null,\n decimalPlaces: values.decimalPlaces ? parseInt(String(values.decimalPlaces)) : 2,\n thousandsSeparator: values.thousandsSeparator ? String(values.thousandsSeparator) : null,\n decimalSeparator: values.decimalSeparator ? String(values.decimalSeparator) : null,\n isBase: !!values.isBase,\n isActive: values.isActive !== false,\n }\n\n await updateCrud('currencies/currencies', payload)\n\n flash(t('currencies.flash.updated'), 'success')\n router.push('/backend/currencies')\n }}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["error"]
|
|
7
7
|
}
|
|
@@ -10,6 +10,7 @@ import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
|
10
10
|
import { apiCall, readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
11
11
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
12
12
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
13
|
+
import { RecordNotFoundState, ErrorMessage } from "@open-mercato/ui/backend/detail";
|
|
13
14
|
const PORTAL_FEATURES = [
|
|
14
15
|
{ id: "portal.profile.view", labelKey: "customer_accounts.admin.portalFeatures.profile.view", fallback: "View profile", descriptionKey: "customer_accounts.admin.portalFeatures.profile.view.description", descriptionFallback: "Allows viewing own profile information and account details" },
|
|
15
16
|
{ id: "portal.profile.edit", labelKey: "customer_accounts.admin.portalFeatures.profile.edit", fallback: "Edit profile", descriptionKey: "customer_accounts.admin.portalFeatures.profile.edit.description", descriptionFallback: "Allows editing display name and other profile settings" },
|
|
@@ -121,9 +122,10 @@ function CustomerRoleDetailPage({ params }) {
|
|
|
121
122
|
const [data, setData] = React.useState(null);
|
|
122
123
|
const [isLoading, setIsLoading] = React.useState(true);
|
|
123
124
|
const [error, setError] = React.useState(null);
|
|
125
|
+
const [isNotFound, setIsNotFound] = React.useState(false);
|
|
124
126
|
React.useEffect(() => {
|
|
125
127
|
if (!id) {
|
|
126
|
-
|
|
128
|
+
setIsNotFound(true);
|
|
127
129
|
setIsLoading(false);
|
|
128
130
|
return;
|
|
129
131
|
}
|
|
@@ -131,6 +133,7 @@ function CustomerRoleDetailPage({ params }) {
|
|
|
131
133
|
async function load() {
|
|
132
134
|
setIsLoading(true);
|
|
133
135
|
setError(null);
|
|
136
|
+
setIsNotFound(false);
|
|
134
137
|
try {
|
|
135
138
|
const payload = await readApiResultOrThrow(
|
|
136
139
|
`/api/customer_accounts/admin/roles/${encodeURIComponent(id)}`,
|
|
@@ -141,8 +144,12 @@ function CustomerRoleDetailPage({ params }) {
|
|
|
141
144
|
setData(payload);
|
|
142
145
|
} catch (err) {
|
|
143
146
|
if (cancelled) return;
|
|
144
|
-
|
|
145
|
-
|
|
147
|
+
if (err.status === 404) {
|
|
148
|
+
setIsNotFound(true);
|
|
149
|
+
} else {
|
|
150
|
+
const message = err instanceof Error ? err.message : t("customer_accounts.admin.roleDetail.error.load", "Failed to load role");
|
|
151
|
+
setError(message);
|
|
152
|
+
}
|
|
146
153
|
} finally {
|
|
147
154
|
if (!cancelled) setIsLoading(false);
|
|
148
155
|
}
|
|
@@ -263,11 +270,24 @@ function CustomerRoleDetailPage({ params }) {
|
|
|
263
270
|
/* @__PURE__ */ jsx("span", { children: t("customer_accounts.admin.roleDetail.loading", "Loading role...") })
|
|
264
271
|
] }) }) });
|
|
265
272
|
}
|
|
273
|
+
if (isNotFound) {
|
|
274
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
275
|
+
RecordNotFoundState,
|
|
276
|
+
{
|
|
277
|
+
label: t("customer_accounts.admin.roleDetail.error.notFound", "Role not found"),
|
|
278
|
+
backHref: "/backend/customer_accounts/roles",
|
|
279
|
+
backLabel: t("customer_accounts.admin.roleDetail.actions.backToList", "Back to roles")
|
|
280
|
+
}
|
|
281
|
+
) }) });
|
|
282
|
+
}
|
|
266
283
|
if (error || !data) {
|
|
267
|
-
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
284
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
285
|
+
ErrorMessage,
|
|
286
|
+
{
|
|
287
|
+
label: error ?? t("customer_accounts.admin.roleDetail.error.notFound", "Role not found"),
|
|
288
|
+
action: /* @__PURE__ */ jsx(Button, { asChild: true, variant: "outline", size: "sm", children: /* @__PURE__ */ jsx(Link, { href: "/backend/customer_accounts/roles", children: t("customer_accounts.admin.roleDetail.actions.backToList", "Back to roles") }) })
|
|
289
|
+
}
|
|
290
|
+
) }) });
|
|
271
291
|
}
|
|
272
292
|
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
273
293
|
CrudForm,
|