@open-mercato/core 0.6.4-develop.4360.1.568bf6e32b → 0.6.4-develop.4368.1.2f7e9a7002

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 (61) hide show
  1. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js +66 -44
  2. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
  3. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +124 -103
  4. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
  5. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +26 -4
  6. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
  7. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js +24 -4
  8. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js.map +2 -2
  9. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js +35 -6
  10. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js.map +3 -3
  11. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +67 -47
  12. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
  13. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +29 -6
  14. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  15. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +29 -6
  16. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  17. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +33 -4
  18. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  19. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +33 -4
  20. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
  21. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +37 -8
  22. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +3 -3
  23. package/dist/modules/workflows/backend/definitions/[id]/page.js +24 -6
  24. package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
  25. package/package.json +7 -7
  26. package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +39 -8
  27. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +38 -6
  28. package/src/modules/catalog/i18n/de.json +2 -0
  29. package/src/modules/catalog/i18n/en.json +2 -0
  30. package/src/modules/catalog/i18n/es.json +2 -0
  31. package/src/modules/catalog/i18n/pl.json +2 -0
  32. package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +30 -4
  33. package/src/modules/directory/backend/directory/tenants/[id]/edit/page.tsx +28 -6
  34. package/src/modules/directory/i18n/de.json +2 -0
  35. package/src/modules/directory/i18n/en.json +2 -0
  36. package/src/modules/directory/i18n/es.json +2 -0
  37. package/src/modules/directory/i18n/pl.json +2 -0
  38. package/src/modules/planner/backend/planner/availability-rulesets/[id]/page.tsx +44 -4
  39. package/src/modules/planner/i18n/de.json +1 -0
  40. package/src/modules/planner/i18n/en.json +1 -0
  41. package/src/modules/planner/i18n/es.json +1 -0
  42. package/src/modules/planner/i18n/pl.json +1 -0
  43. package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +36 -7
  44. package/src/modules/sales/i18n/de.json +1 -0
  45. package/src/modules/sales/i18n/en.json +1 -0
  46. package/src/modules/sales/i18n/es.json +1 -0
  47. package/src/modules/sales/i18n/pl.json +1 -0
  48. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +33 -6
  49. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +33 -6
  50. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +44 -4
  51. package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +44 -4
  52. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +44 -4
  53. package/src/modules/staff/i18n/de.json +5 -0
  54. package/src/modules/staff/i18n/en.json +5 -0
  55. package/src/modules/staff/i18n/es.json +5 -0
  56. package/src/modules/staff/i18n/pl.json +5 -0
  57. package/src/modules/workflows/backend/definitions/[id]/page.tsx +26 -3
  58. package/src/modules/workflows/i18n/de.json +1 -0
  59. package/src/modules/workflows/i18n/en.json +1 -0
  60. package/src/modules/workflows/i18n/es.json +1 -0
  61. package/src/modules/workflows/i18n/pl.json +1 -0
@@ -4,6 +4,7 @@ import * as React from "react";
4
4
  import Link from "next/link";
5
5
  import { useRouter, useSearchParams } from "next/navigation";
6
6
  import { Page, PageBody } from "@open-mercato/ui/backend/Page";
7
+ import { ErrorMessage, RecordNotFoundState } from "@open-mercato/ui/backend/detail";
7
8
  import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
8
9
  import { updateCrud, deleteCrud } from "@open-mercato/ui/backend/utils/crud";
9
10
  import { DataTable, withDataTableNamespaces } from "@open-mercato/ui/backend/DataTable";
@@ -26,6 +27,8 @@ function StaffTeamEditPage({ params }) {
26
27
  const searchParams = useSearchParams();
27
28
  const scopeVersion = useOrganizationScopeVersion();
28
29
  const [initialValues, setInitialValues] = React.useState(null);
30
+ const [error, setError] = React.useState(null);
31
+ const [isNotFound, setIsNotFound] = React.useState(false);
29
32
  const [activeTab, setActiveTab] = React.useState("details");
30
33
  const [memberRows, setMemberRows] = React.useState([]);
31
34
  const [memberPage, setMemberPage] = React.useState(1);
@@ -120,6 +123,10 @@ function StaffTeamEditPage({ params }) {
120
123
  const teamIdValue = teamId;
121
124
  let cancelled = false;
122
125
  async function loadTeam() {
126
+ if (!cancelled) {
127
+ setError(null);
128
+ setIsNotFound(false);
129
+ }
123
130
  try {
124
131
  const params2 = new URLSearchParams({ page: "1", pageSize: "1", ids: teamIdValue });
125
132
  const payload = await readApiResultOrThrow(
@@ -128,7 +135,10 @@ function StaffTeamEditPage({ params }) {
128
135
  { errorMessage: t("staff.teams.errors.load", "Failed to load team.") }
129
136
  );
130
137
  const record = Array.isArray(payload.items) ? payload.items[0] : null;
131
- if (!record) throw new Error(t("staff.teams.errors.notFound", "Team not found."));
138
+ if (!record) {
139
+ if (!cancelled) setIsNotFound(true);
140
+ return;
141
+ }
132
142
  const customFields = extractCustomFieldEntries(record);
133
143
  const isActive = typeof record.isActive === "boolean" ? record.isActive : typeof record.is_active === "boolean" ? record.is_active : true;
134
144
  if (!cancelled) {
@@ -140,9 +150,15 @@ function StaffTeamEditPage({ params }) {
140
150
  ...customFields
141
151
  });
142
152
  }
143
- } catch (error) {
144
- const message = error instanceof Error ? error.message : t("staff.teams.errors.load", "Failed to load team.");
145
- flash(message, "error");
153
+ } catch (err) {
154
+ if (!cancelled) {
155
+ if (err.status === 404) {
156
+ setIsNotFound(true);
157
+ } else {
158
+ const message = err instanceof Error ? err.message : t("staff.teams.errors.load", "Failed to load team.");
159
+ setError(message);
160
+ }
161
+ }
146
162
  }
147
163
  }
148
164
  loadTeam();
@@ -183,8 +199,8 @@ function StaffTeamEditPage({ params }) {
183
199
  setMemberTotalPages(
184
200
  typeof payload.totalPages === "number" ? payload.totalPages : Math.max(1, Math.ceil(items.length / TEAM_MEMBERS_PAGE_SIZE))
185
201
  );
186
- } catch (error) {
187
- console.error("staff.teams.team-members.list", error);
202
+ } catch (error2) {
203
+ console.error("staff.teams.team-members.list", error2);
188
204
  flash(memberLabels.errors.load, "error");
189
205
  } finally {
190
206
  setMembersLoading(false);
@@ -207,8 +223,8 @@ function StaffTeamEditPage({ params }) {
207
223
  await updateCrud("staff/team-members", { id: entry.id, teamId: null }, { errorMessage: memberLabels.errors.unassign });
208
224
  flash(memberLabels.messages.unassigned, "success");
209
225
  handleMemberRefresh();
210
- } catch (error) {
211
- console.error("staff.teams.team-members.unassign", error);
226
+ } catch (error2) {
227
+ console.error("staff.teams.team-members.unassign", error2);
212
228
  flash(memberLabels.errors.unassign, "error");
213
229
  }
214
230
  }, [handleMemberRefresh, memberLabels.errors.unassign, memberLabels.messages.unassigned, teamId]);
@@ -227,6 +243,19 @@ function StaffTeamEditPage({ params }) {
227
243
  });
228
244
  router.push("/backend/staff/teams");
229
245
  }, [teamId, router, t]);
246
+ if (isNotFound) {
247
+ return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
248
+ RecordNotFoundState,
249
+ {
250
+ label: t("staff.teams.errors.notFound", "Team not found."),
251
+ backHref: "/backend/staff/teams",
252
+ backLabel: t("staff.teams.actions.backToList", "Back to teams")
253
+ }
254
+ ) }) });
255
+ }
256
+ if (error && !initialValues) {
257
+ return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(ErrorMessage, { label: error }) }) });
258
+ }
230
259
  return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
231
260
  /* @__PURE__ */ jsx("div", { className: "border-b", children: /* @__PURE__ */ jsx("nav", { className: "flex flex-wrap items-center gap-5 text-sm", "aria-label": memberLabels.tabs.label, children: [
232
261
  { id: "details", label: memberLabels.tabs.details },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/staff/backend/staff/teams/%5Bid%5D/edit/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { DataTable, withDataTableNamespaces } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { TeamForm, type TeamFormValues, buildTeamPayload } from '@open-mercato/core/modules/staff/components/TeamForm'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { Plus } from 'lucide-react'\nimport { formatDateTime } from '@open-mercato/shared/lib/time'\n\nconst TEAM_MEMBERS_PAGE_SIZE = 50\n\ntype TeamRecord = {\n id: string\n name: string\n description?: string | null\n isActive?: boolean\n is_active?: boolean\n} & Record<string, unknown>\n\ntype TeamResponse = {\n items?: TeamRecord[]\n}\n\ntype TeamMemberRow = {\n id: string\n displayName: string\n description: string | null\n userEmail: string | null\n roleNames: string[]\n tags: string[]\n isActive: boolean\n updatedAt: string | null\n teamId: string | null\n}\n\ntype TeamMembersResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n totalPages?: number\n}\n\nexport default function StaffTeamEditPage({ params }: { params?: { id?: string } }) {\n const teamId = params?.id\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const scopeVersion = useOrganizationScopeVersion()\n const [initialValues, setInitialValues] = React.useState<TeamFormValues | null>(null)\n const [activeTab, setActiveTab] = React.useState<'details' | 'members'>('details')\n const [memberRows, setMemberRows] = React.useState<TeamMemberRow[]>([])\n const [memberPage, setMemberPage] = React.useState(1)\n const [memberTotal, setMemberTotal] = React.useState(0)\n const [memberTotalPages, setMemberTotalPages] = React.useState(1)\n const [memberSorting, setMemberSorting] = React.useState<SortingState>([{ id: 'displayName', desc: false }])\n const [memberSearch, setMemberSearch] = React.useState('')\n const [membersLoading, setMembersLoading] = React.useState(false)\n const [memberReloadToken, setMemberReloadToken] = React.useState(0)\n\n const memberLabels = React.useMemo(() => ({\n title: t('staff.teams.tabs.members', 'Team members'),\n description: t('staff.teamMembers.page.description', 'Manage employees and their team assignments.'),\n table: {\n name: t('staff.teamMembers.table.name', 'Name'),\n user: t('staff.teamMembers.table.user', 'User'),\n roles: t('staff.teamMembers.table.roles', 'Roles'),\n tags: t('staff.teamMembers.table.tags', 'Tags'),\n active: t('staff.teamMembers.table.active', 'Active'),\n updatedAt: t('staff.teamMembers.table.updatedAt', 'Updated'),\n empty: t('staff.teamMembers.table.empty', 'No team members yet.'),\n search: t('staff.teamMembers.table.search', 'Search team members...'),\n },\n actions: {\n add: t('staff.teamMembers.actions.add', 'Add team member'),\n edit: t('staff.teamMembers.actions.edit', 'Edit'),\n unassign: t('staff.teamMembers.actions.unassign', 'Unassign'),\n refresh: t('staff.teamMembers.actions.refresh', 'Refresh'),\n },\n messages: {\n unassigned: t('staff.teamMembers.messages.unassigned', 'Team member unassigned.'),\n },\n errors: {\n load: t('staff.teamMembers.errors.load', 'Failed to load team members.'),\n unassign: t('staff.teamMembers.errors.unassign', 'Failed to unassign team member.'),\n },\n tabs: {\n details: t('staff.teams.tabs.details', 'Details'),\n members: t('staff.teams.tabs.members', 'Team members'),\n label: t('staff.teams.tabs.label', 'Team sections'),\n },\n }), [t])\n\n const memberColumns = React.useMemo<ColumnDef<TeamMemberRow>[]>(() => [\n {\n accessorKey: 'displayName',\n header: memberLabels.table.name,\n meta: { priority: 1, sticky: true },\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.original.displayName}</span>\n {row.original.description ? (\n <span className=\"text-xs text-muted-foreground line-clamp-2\">{row.original.description}</span>\n ) : null}\n </div>\n ),\n },\n {\n accessorKey: 'userEmail',\n header: memberLabels.table.user,\n meta: { priority: 2 },\n cell: ({ row }) => row.original.userEmail\n ? <span className=\"text-sm\">{row.original.userEmail}</span>\n : <span className=\"text-xs text-muted-foreground\">-</span>,\n },\n {\n accessorKey: 'roleNames',\n header: memberLabels.table.roles,\n meta: { priority: 3 },\n cell: ({ row }) => row.original.roleNames.length\n ? <span className=\"text-sm\">{row.original.roleNames.join(', ')}</span>\n : <span className=\"text-xs text-muted-foreground\">-</span>,\n },\n {\n accessorKey: 'tags',\n header: memberLabels.table.tags,\n meta: { priority: 4 },\n cell: ({ row }) => row.original.tags.length\n ? <span className=\"text-xs text-muted-foreground\">{row.original.tags.join(', ')}</span>\n : <span className=\"text-xs text-muted-foreground\">-</span>,\n },\n {\n accessorKey: 'isActive',\n header: memberLabels.table.active,\n meta: { priority: 5 },\n cell: ({ row }) => <BooleanIcon value={row.original.isActive} />,\n },\n {\n accessorKey: 'updatedAt',\n header: memberLabels.table.updatedAt,\n meta: { priority: 6 },\n cell: ({ row }) => row.original.updatedAt\n ? <span className=\"text-xs text-muted-foreground\">{formatDateTime(row.original.updatedAt)}</span>\n : <span className=\"text-xs text-muted-foreground\">-</span>,\n },\n ], [\n memberLabels.table.active,\n memberLabels.table.name,\n memberLabels.table.roles,\n memberLabels.table.tags,\n memberLabels.table.updatedAt,\n memberLabels.table.user,\n ])\n\n React.useEffect(() => {\n if (!teamId) return\n const teamIdValue = teamId\n let cancelled = false\n async function loadTeam() {\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '1', ids: teamIdValue })\n const payload = await readApiResultOrThrow<TeamResponse>(\n `/api/staff/teams?${params.toString()}`,\n undefined,\n { errorMessage: t('staff.teams.errors.load', 'Failed to load team.') },\n )\n const record = Array.isArray(payload.items) ? payload.items[0] : null\n if (!record) throw new Error(t('staff.teams.errors.notFound', 'Team not found.'))\n const customFields = extractCustomFieldEntries(record)\n const isActive = typeof record.isActive === 'boolean'\n ? record.isActive\n : typeof record.is_active === 'boolean'\n ? record.is_active\n : true\n if (!cancelled) {\n setInitialValues({\n id: record.id,\n name: record.name ?? '',\n description: record.description ?? '',\n isActive,\n ...customFields,\n })\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('staff.teams.errors.load', 'Failed to load team.')\n flash(message, 'error')\n }\n }\n loadTeam()\n return () => { cancelled = true }\n }, [teamId, t])\n\n React.useEffect(() => {\n if (!searchParams) return\n const tabParam = searchParams.get('tab')\n if (tabParam === 'members') {\n setActiveTab('members')\n }\n }, [searchParams])\n\n const loadTeamMembers = React.useCallback(async () => {\n if (!teamId) return\n setMembersLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(memberPage),\n pageSize: String(TEAM_MEMBERS_PAGE_SIZE),\n teamId,\n })\n const sort = memberSorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n if (memberSearch.trim()) params.set('search', memberSearch.trim())\n const payload = await readApiResultOrThrow<TeamMembersResponse>(\n `/api/staff/team-members?${params.toString()}`,\n undefined,\n { errorMessage: memberLabels.errors.load, fallback: { items: [], total: 0, totalPages: 1 } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setMemberRows(items.map(mapApiTeamMember))\n setMemberTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setMemberTotalPages(\n typeof payload.totalPages === 'number'\n ? payload.totalPages\n : Math.max(1, Math.ceil(items.length / TEAM_MEMBERS_PAGE_SIZE)),\n )\n } catch (error) {\n console.error('staff.teams.team-members.list', error)\n flash(memberLabels.errors.load, 'error')\n } finally {\n setMembersLoading(false)\n }\n }, [memberLabels.errors.load, memberPage, memberSearch, memberSorting, teamId])\n\n React.useEffect(() => {\n if (activeTab !== 'members') return\n void loadTeamMembers()\n }, [activeTab, loadTeamMembers, memberReloadToken, scopeVersion])\n\n const handleMemberSearchChange = React.useCallback((value: string) => {\n setMemberSearch(value)\n setMemberPage(1)\n }, [])\n\n const handleMemberRefresh = React.useCallback(() => {\n setMemberReloadToken((token) => token + 1)\n }, [])\n\n const handleUnassignMember = React.useCallback(async (entry: TeamMemberRow) => {\n if (!teamId || entry.teamId !== teamId) return\n try {\n await updateCrud('staff/team-members', { id: entry.id, teamId: null }, { errorMessage: memberLabels.errors.unassign })\n flash(memberLabels.messages.unassigned, 'success')\n handleMemberRefresh()\n } catch (error) {\n console.error('staff.teams.team-members.unassign', error)\n flash(memberLabels.errors.unassign, 'error')\n }\n }, [handleMemberRefresh, memberLabels.errors.unassign, memberLabels.messages.unassigned, teamId])\n\n const handleSubmit = React.useCallback(async (values: TeamFormValues) => {\n if (!teamId) return\n const payload = buildTeamPayload(values, { id: teamId })\n await updateCrud('staff/teams', payload, {\n errorMessage: t('staff.teams.errors.save', 'Failed to save team.'),\n })\n flash(t('staff.teams.messages.saved', 'Team saved.'), 'success')\n }, [teamId, t])\n\n const handleDelete = React.useCallback(async () => {\n if (!teamId) return\n await deleteCrud('staff/teams', teamId, {\n errorMessage: t('staff.teams.errors.delete', 'Failed to delete team.'),\n })\n router.push('/backend/staff/teams')\n }, [teamId, router, t])\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <div className=\"border-b\">\n <nav className=\"flex flex-wrap items-center gap-5 text-sm\" aria-label={memberLabels.tabs.label}>\n {[\n { id: 'details', label: memberLabels.tabs.details },\n { id: 'members', label: memberLabels.tabs.members },\n ].map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === tab.id}\n onClick={() => setActiveTab(tab.id as 'details' | 'members')}\n className={`relative -mb-px border-b-2 px-0 py-2 text-sm font-medium transition-colors ${\n activeTab === tab.id\n ? 'border-accent-indigo text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </nav>\n </div>\n\n {activeTab === 'details' ? (\n <TeamForm\n title={t('staff.teams.form.editTitle', 'Edit team')}\n backHref=\"/backend/staff/teams\"\n cancelHref=\"/backend/staff/teams\"\n initialValues={initialValues ?? { name: '', description: '', isActive: true }}\n onSubmit={handleSubmit}\n onDelete={handleDelete}\n isLoading={!initialValues}\n loadingMessage={t('staff.teams.form.loading', 'Loading team...')}\n extraActions={teamId ? (\n <SendObjectMessageDialog\n object={{\n entityModule: 'staff',\n entityType: 'team',\n entityId: teamId,\n previewData: { title: initialValues?.name ?? ''},\n }}\n viewHref={`/backend/staff/teams/${teamId}/edit`}\n />\n ) : undefined}\n />\n ) : (\n <DataTable<TeamMemberRow>\n title={memberLabels.title}\n data={memberRows}\n columns={memberColumns}\n isLoading={membersLoading}\n searchValue={memberSearch}\n onSearchChange={handleMemberSearchChange}\n searchPlaceholder={memberLabels.table.search}\n emptyState={<p className=\"py-8 text-center text-sm text-muted-foreground\">{memberLabels.table.empty}</p>}\n actions={(\n <Button asChild size=\"sm\">\n <Link href={`/backend/staff/team-members/create?teamId=${encodeURIComponent(teamId ?? '')}`}>\n <Plus className=\"mr-2 h-4 w-4\" aria-hidden />\n {memberLabels.actions.add}\n </Link>\n </Button>\n )}\n refreshButton={{\n label: memberLabels.actions.refresh,\n onRefresh: handleMemberRefresh,\n isRefreshing: membersLoading,\n }}\n sortable\n sorting={memberSorting}\n onSortingChange={setMemberSorting}\n pagination={{\n page: memberPage,\n pageSize: TEAM_MEMBERS_PAGE_SIZE,\n total: memberTotal,\n totalPages: memberTotalPages,\n onPageChange: setMemberPage,\n }}\n rowActions={(row) => (\n <RowActions\n items={[\n { id: 'edit', label: memberLabels.actions.edit, onSelect: () => { router.push(`/backend/staff/team-members/${row.id}`) } },\n { id: 'unassign', label: memberLabels.actions.unassign, onSelect: () => { void handleUnassignMember(row) } },\n ]}\n />\n )}\n />\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n\nfunction mapApiTeamMember(item: Record<string, unknown>): TeamMemberRow {\n const id = typeof item.id === 'string' ? item.id : ''\n const displayName = typeof item.displayName === 'string'\n ? item.displayName\n : typeof item.display_name === 'string'\n ? item.display_name\n : id\n const description = typeof item.description === 'string' && item.description.trim().length\n ? item.description.trim()\n : null\n const user = item.user && typeof item.user === 'object' ? item.user as { email?: unknown } : null\n const userEmail = user && typeof user.email === 'string' && user.email.length ? user.email : null\n const roleNames = Array.isArray(item.roleNames) ? item.roleNames.filter((value): value is string => typeof value === 'string') : []\n const tags = Array.isArray(item.tags) ? item.tags.filter((value): value is string => typeof value === 'string') : []\n const updatedAt = typeof item.updatedAt === 'string'\n ? item.updatedAt\n : typeof item.updated_at === 'string'\n ? item.updated_at\n : null\n const isActive = typeof item.isActive === 'boolean'\n ? item.isActive\n : typeof item.is_active === 'boolean'\n ? item.is_active\n : true\n const teamId = typeof item.teamId === 'string'\n ? item.teamId\n : typeof item.team_id === 'string'\n ? item.team_id\n : null\n return withDataTableNamespaces({\n id,\n displayName,\n description,\n userEmail,\n roleNames,\n tags,\n isActive,\n updatedAt,\n teamId,\n }, item)\n}\n\n"],
5
- "mappings": ";AA8GQ,SACE,KADF;AA5GR,YAAY,WAAW;AACvB,OAAO,UAAU;AACjB,SAAS,WAAW,uBAAuB;AAE3C,SAAS,MAAM,gBAAgB;AAC/B,SAAS,4BAA4B;AACrC,SAAS,YAAY,kBAAkB;AACvC,SAAS,WAAW,+BAA+B;AACnD,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAC5B,SAAS,aAAa;AACtB,SAAS,YAAY;AACrB,SAAS,UAA+B,wBAAwB;AAChE,SAAS,+BAA+B;AACxC,SAAS,iCAAiC;AAC1C,SAAS,mCAAmC;AAC5C,SAAS,YAAY;AACrB,SAAS,sBAAsB;AAE/B,MAAM,yBAAyB;AAgChB,SAAR,kBAAmC,EAAE,OAAO,GAAiC;AAClF,QAAM,SAAS,QAAQ;AACvB,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,eAAe,gBAAgB;AACrC,QAAM,eAAe,4BAA4B;AACjD,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAgC,IAAI;AACpF,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAgC,SAAS;AACjF,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAA0B,CAAC,CAAC;AACtE,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,CAAC;AACpD,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,CAAC;AACtD,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAS,CAAC;AAChE,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAuB,CAAC,EAAE,IAAI,eAAe,MAAM,MAAM,CAAC,CAAC;AAC3G,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,EAAE;AACzD,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAS,KAAK;AAChE,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAS,CAAC;AAElE,QAAM,eAAe,MAAM,QAAQ,OAAO;AAAA,IACxC,OAAO,EAAE,4BAA4B,cAAc;AAAA,IACnD,aAAa,EAAE,sCAAsC,8CAA8C;AAAA,IACnG,OAAO;AAAA,MACL,MAAM,EAAE,gCAAgC,MAAM;AAAA,MAC9C,MAAM,EAAE,gCAAgC,MAAM;AAAA,MAC9C,OAAO,EAAE,iCAAiC,OAAO;AAAA,MACjD,MAAM,EAAE,gCAAgC,MAAM;AAAA,MAC9C,QAAQ,EAAE,kCAAkC,QAAQ;AAAA,MACpD,WAAW,EAAE,qCAAqC,SAAS;AAAA,MAC3D,OAAO,EAAE,iCAAiC,sBAAsB;AAAA,MAChE,QAAQ,EAAE,kCAAkC,wBAAwB;AAAA,IACtE;AAAA,IACA,SAAS;AAAA,MACP,KAAK,EAAE,iCAAiC,iBAAiB;AAAA,MACzD,MAAM,EAAE,kCAAkC,MAAM;AAAA,MAChD,UAAU,EAAE,sCAAsC,UAAU;AAAA,MAC5D,SAAS,EAAE,qCAAqC,SAAS;AAAA,IAC3D;AAAA,IACA,UAAU;AAAA,MACR,YAAY,EAAE,yCAAyC,yBAAyB;AAAA,IAClF;AAAA,IACA,QAAQ;AAAA,MACN,MAAM,EAAE,iCAAiC,8BAA8B;AAAA,MACvE,UAAU,EAAE,qCAAqC,iCAAiC;AAAA,IACpF;AAAA,IACA,MAAM;AAAA,MACJ,SAAS,EAAE,4BAA4B,SAAS;AAAA,MAChD,SAAS,EAAE,4BAA4B,cAAc;AAAA,MACrD,OAAO,EAAE,0BAA0B,eAAe;AAAA,IACpD;AAAA,EACF,IAAI,CAAC,CAAC,CAAC;AAEP,QAAM,gBAAgB,MAAM,QAAoC,MAAM;AAAA,IACpE;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,aAAa,MAAM;AAAA,MAC3B,MAAM,EAAE,UAAU,GAAG,QAAQ,KAAK;AAAA,MAClC,MAAM,CAAC,EAAE,IAAI,MACX,qBAAC,SAAI,WAAU,iBACb;AAAA,4BAAC,UAAK,WAAU,eAAe,cAAI,SAAS,aAAY;AAAA,QACvD,IAAI,SAAS,cACZ,oBAAC,UAAK,WAAU,8CAA8C,cAAI,SAAS,aAAY,IACrF;AAAA,SACN;AAAA,IAEJ;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,aAAa,MAAM;AAAA,MAC3B,MAAM,EAAE,UAAU,EAAE;AAAA,MACpB,MAAM,CAAC,EAAE,IAAI,MAAM,IAAI,SAAS,YAC5B,oBAAC,UAAK,WAAU,WAAW,cAAI,SAAS,WAAU,IAClD,oBAAC,UAAK,WAAU,iCAAgC,eAAC;AAAA,IACvD;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,aAAa,MAAM;AAAA,MAC3B,MAAM,EAAE,UAAU,EAAE;AAAA,MACpB,MAAM,CAAC,EAAE,IAAI,MAAM,IAAI,SAAS,UAAU,SACtC,oBAAC,UAAK,WAAU,WAAW,cAAI,SAAS,UAAU,KAAK,IAAI,GAAE,IAC7D,oBAAC,UAAK,WAAU,iCAAgC,eAAC;AAAA,IACvD;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,aAAa,MAAM;AAAA,MAC3B,MAAM,EAAE,UAAU,EAAE;AAAA,MACpB,MAAM,CAAC,EAAE,IAAI,MAAM,IAAI,SAAS,KAAK,SACjC,oBAAC,UAAK,WAAU,iCAAiC,cAAI,SAAS,KAAK,KAAK,IAAI,GAAE,IAC9E,oBAAC,UAAK,WAAU,iCAAgC,eAAC;AAAA,IACvD;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,aAAa,MAAM;AAAA,MAC3B,MAAM,EAAE,UAAU,EAAE;AAAA,MACpB,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,eAAY,OAAO,IAAI,SAAS,UAAU;AAAA,IAChE;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,aAAa,MAAM;AAAA,MAC3B,MAAM,EAAE,UAAU,EAAE;AAAA,MACpB,MAAM,CAAC,EAAE,IAAI,MAAM,IAAI,SAAS,YAC5B,oBAAC,UAAK,WAAU,iCAAiC,yBAAe,IAAI,SAAS,SAAS,GAAE,IACxF,oBAAC,UAAK,WAAU,iCAAgC,eAAC;AAAA,IACvD;AAAA,EACF,GAAG;AAAA,IACD,aAAa,MAAM;AAAA,IACnB,aAAa,MAAM;AAAA,IACnB,aAAa,MAAM;AAAA,IACnB,aAAa,MAAM;AAAA,IACnB,aAAa,MAAM;AAAA,IACnB,aAAa,MAAM;AAAA,EACrB,CAAC;AAED,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,OAAQ;AACb,UAAM,cAAc;AACpB,QAAI,YAAY;AAChB,mBAAe,WAAW;AACxB,UAAI;AACF,cAAMA,UAAS,IAAI,gBAAgB,EAAE,MAAM,KAAK,UAAU,KAAK,KAAK,YAAY,CAAC;AACjF,cAAM,UAAU,MAAM;AAAA,UACpB,oBAAoBA,QAAO,SAAS,CAAC;AAAA,UACrC;AAAA,UACA,EAAE,cAAc,EAAE,2BAA2B,sBAAsB,EAAE;AAAA,QACvE;AACA,cAAM,SAAS,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,MAAM,CAAC,IAAI;AACjE,YAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,EAAE,+BAA+B,iBAAiB,CAAC;AAChF,cAAM,eAAe,0BAA0B,MAAM;AACrD,cAAM,WAAW,OAAO,OAAO,aAAa,YACxC,OAAO,WACP,OAAO,OAAO,cAAc,YAC1B,OAAO,YACP;AACN,YAAI,CAAC,WAAW;AACd,2BAAiB;AAAA,YACf,IAAI,OAAO;AAAA,YACX,MAAM,OAAO,QAAQ;AAAA,YACrB,aAAa,OAAO,eAAe;AAAA,YACnC;AAAA,YACA,GAAG;AAAA,UACL,CAAC;AAAA,QACH;AAAA,MACF,SAAS,OAAO;AACd,cAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,EAAE,2BAA2B,sBAAsB;AAC5G,cAAM,SAAS,OAAO;AAAA,MACxB;AAAA,IACF;AACA,aAAS;AACT,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAK;AAAA,EAClC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAEd,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,aAAc;AACnB,UAAM,WAAW,aAAa,IAAI,KAAK;AACvC,QAAI,aAAa,WAAW;AAC1B,mBAAa,SAAS;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,kBAAkB,MAAM,YAAY,YAAY;AACpD,QAAI,CAAC,OAAQ;AACb,sBAAkB,IAAI;AACtB,QAAI;AACF,YAAMA,UAAS,IAAI,gBAAgB;AAAA,QACjC,MAAM,OAAO,UAAU;AAAA,QACvB,UAAU,OAAO,sBAAsB;AAAA,QACvC;AAAA,MACF,CAAC;AACD,YAAM,OAAO,cAAc,CAAC;AAC5B,UAAI,MAAM,IAAI;AACZ,QAAAA,QAAO,IAAI,aAAa,KAAK,EAAE;AAC/B,QAAAA,QAAO,IAAI,WAAW,KAAK,OAAO,SAAS,KAAK;AAAA,MAClD;AACA,UAAI,aAAa,KAAK,EAAG,CAAAA,QAAO,IAAI,UAAU,aAAa,KAAK,CAAC;AACjE,YAAM,UAAU,MAAM;AAAA,QACpB,2BAA2BA,QAAO,SAAS,CAAC;AAAA,QAC5C;AAAA,QACA,EAAE,cAAc,aAAa,OAAO,MAAM,UAAU,EAAE,OAAO,CAAC,GAAG,OAAO,GAAG,YAAY,EAAE,EAAE;AAAA,MAC7F;AACA,YAAM,QAAQ,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC9D,oBAAc,MAAM,IAAI,gBAAgB,CAAC;AACzC,qBAAe,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ,MAAM,MAAM;AAC/E;AAAA,QACE,OAAO,QAAQ,eAAe,WAC1B,QAAQ,aACR,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,SAAS,sBAAsB,CAAC;AAAA,MAClE;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,iCAAiC,KAAK;AACpD,YAAM,aAAa,OAAO,MAAM,OAAO;AAAA,IACzC,UAAE;AACA,wBAAkB,KAAK;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,aAAa,OAAO,MAAM,YAAY,cAAc,eAAe,MAAM,CAAC;AAE9E,QAAM,UAAU,MAAM;AACpB,QAAI,cAAc,UAAW;AAC7B,SAAK,gBAAgB;AAAA,EACvB,GAAG,CAAC,WAAW,iBAAiB,mBAAmB,YAAY,CAAC;AAEhE,QAAM,2BAA2B,MAAM,YAAY,CAAC,UAAkB;AACpE,oBAAgB,KAAK;AACrB,kBAAc,CAAC;AAAA,EACjB,GAAG,CAAC,CAAC;AAEL,QAAM,sBAAsB,MAAM,YAAY,MAAM;AAClD,yBAAqB,CAAC,UAAU,QAAQ,CAAC;AAAA,EAC3C,GAAG,CAAC,CAAC;AAEL,QAAM,uBAAuB,MAAM,YAAY,OAAO,UAAyB;AAC7E,QAAI,CAAC,UAAU,MAAM,WAAW,OAAQ;AACxC,QAAI;AACF,YAAM,WAAW,sBAAsB,EAAE,IAAI,MAAM,IAAI,QAAQ,KAAK,GAAG,EAAE,cAAc,aAAa,OAAO,SAAS,CAAC;AACrH,YAAM,aAAa,SAAS,YAAY,SAAS;AACjD,0BAAoB;AAAA,IACtB,SAAS,OAAO;AACd,cAAQ,MAAM,qCAAqC,KAAK;AACxD,YAAM,aAAa,OAAO,UAAU,OAAO;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,qBAAqB,aAAa,OAAO,UAAU,aAAa,SAAS,YAAY,MAAM,CAAC;AAEhG,QAAM,eAAe,MAAM,YAAY,OAAO,WAA2B;AACvE,QAAI,CAAC,OAAQ;AACb,UAAM,UAAU,iBAAiB,QAAQ,EAAE,IAAI,OAAO,CAAC;AACvD,UAAM,WAAW,eAAe,SAAS;AAAA,MACvC,cAAc,EAAE,2BAA2B,sBAAsB;AAAA,IACnE,CAAC;AACD,UAAM,EAAE,8BAA8B,aAAa,GAAG,SAAS;AAAA,EACjE,GAAG,CAAC,QAAQ,CAAC,CAAC;AAEd,QAAM,eAAe,MAAM,YAAY,YAAY;AACjD,QAAI,CAAC,OAAQ;AACb,UAAM,WAAW,eAAe,QAAQ;AAAA,MACtC,cAAc,EAAE,6BAA6B,wBAAwB;AAAA,IACvE,CAAC;AACD,WAAO,KAAK,sBAAsB;AAAA,EACpC,GAAG,CAAC,QAAQ,QAAQ,CAAC,CAAC;AAEtB,SACE,oBAAC,QACC,8BAAC,YACC,+BAAC,SAAI,WAAU,aACb;AAAA,wBAAC,SAAI,WAAU,YACb,8BAAC,SAAI,WAAU,6CAA4C,cAAY,aAAa,KAAK,OACtF;AAAA,MACC,EAAE,IAAI,WAAW,OAAO,aAAa,KAAK,QAAQ;AAAA,MAClD,EAAE,IAAI,WAAW,OAAO,aAAa,KAAK,QAAQ;AAAA,IACpD,EAAE,IAAI,CAAC,QACL;AAAA,MAAC;AAAA;AAAA,QAEC,MAAK;AAAA,QACL,MAAK;AAAA,QACL,iBAAe,cAAc,IAAI;AAAA,QACjC,SAAS,MAAM,aAAa,IAAI,EAA2B;AAAA,QAC3D,WAAW,8EACT,cAAc,IAAI,KACd,yCACA,gEACN;AAAA,QAEC,cAAI;AAAA;AAAA,MAXA,IAAI;AAAA,IAYX,CACD,GACH,GACF;AAAA,IAEC,cAAc,YACb;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,8BAA8B,WAAW;AAAA,QAClD,UAAS;AAAA,QACT,YAAW;AAAA,QACX,eAAe,iBAAiB,EAAE,MAAM,IAAI,aAAa,IAAI,UAAU,KAAK;AAAA,QAC5E,UAAU;AAAA,QACV,UAAU;AAAA,QACV,WAAW,CAAC;AAAA,QACZ,gBAAgB,EAAE,4BAA4B,iBAAiB;AAAA,QAC/D,cAAc,SACZ;AAAA,UAAC;AAAA;AAAA,YACC,QAAQ;AAAA,cACN,cAAc;AAAA,cACd,YAAY;AAAA,cACZ,UAAU;AAAA,cACV,aAAa,EAAE,OAAO,eAAe,QAAQ,GAAE;AAAA,YACjD;AAAA,YACA,UAAU,wBAAwB,MAAM;AAAA;AAAA,QAC1C,IACE;AAAA;AAAA,IACN,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,aAAa;AAAA,QACpB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,QACX,aAAa;AAAA,QACb,gBAAgB;AAAA,QAChB,mBAAmB,aAAa,MAAM;AAAA,QACtC,YAAY,oBAAC,OAAE,WAAU,kDAAkD,uBAAa,MAAM,OAAM;AAAA,QACpG,SACE,oBAAC,UAAO,SAAO,MAAC,MAAK,MACnB,+BAAC,QAAK,MAAM,6CAA6C,mBAAmB,UAAU,EAAE,CAAC,IACvF;AAAA,8BAAC,QAAK,WAAU,gBAAe,eAAW,MAAC;AAAA,UAC1C,aAAa,QAAQ;AAAA,WACxB,GACF;AAAA,QAEF,eAAe;AAAA,UACb,OAAO,aAAa,QAAQ;AAAA,UAC5B,WAAW;AAAA,UACX,cAAc;AAAA,QAChB;AAAA,QACA,UAAQ;AAAA,QACR,SAAS;AAAA,QACT,iBAAiB;AAAA,QACjB,YAAY;AAAA,UACV,MAAM;AAAA,UACN,UAAU;AAAA,UACV,OAAO;AAAA,UACP,YAAY;AAAA,UACZ,cAAc;AAAA,QAChB;AAAA,QACA,YAAY,CAAC,QACX;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,EAAE,IAAI,QAAQ,OAAO,aAAa,QAAQ,MAAM,UAAU,MAAM;AAAE,uBAAO,KAAK,+BAA+B,IAAI,EAAE,EAAE;AAAA,cAAE,EAAE;AAAA,cACzH,EAAE,IAAI,YAAY,OAAO,aAAa,QAAQ,UAAU,UAAU,MAAM;AAAE,qBAAK,qBAAqB,GAAG;AAAA,cAAE,EAAE;AAAA,YAC7G;AAAA;AAAA,QACF;AAAA;AAAA,IAEJ;AAAA,KAEJ,GACF,GACF;AAEJ;AAEA,SAAS,iBAAiB,MAA8C;AACtE,QAAM,KAAK,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;AACnD,QAAM,cAAc,OAAO,KAAK,gBAAgB,WAC5C,KAAK,cACL,OAAO,KAAK,iBAAiB,WAC3B,KAAK,eACL;AACN,QAAM,cAAc,OAAO,KAAK,gBAAgB,YAAY,KAAK,YAAY,KAAK,EAAE,SAChF,KAAK,YAAY,KAAK,IACtB;AACJ,QAAM,OAAO,KAAK,QAAQ,OAAO,KAAK,SAAS,WAAW,KAAK,OAA8B;AAC7F,QAAM,YAAY,QAAQ,OAAO,KAAK,UAAU,YAAY,KAAK,MAAM,SAAS,KAAK,QAAQ;AAC7F,QAAM,YAAY,MAAM,QAAQ,KAAK,SAAS,IAAI,KAAK,UAAU,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ,IAAI,CAAC;AAClI,QAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,KAAK,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ,IAAI,CAAC;AACnH,QAAM,YAAY,OAAO,KAAK,cAAc,WACxC,KAAK,YACL,OAAO,KAAK,eAAe,WACzB,KAAK,aACL;AACN,QAAM,WAAW,OAAO,KAAK,aAAa,YACtC,KAAK,WACL,OAAO,KAAK,cAAc,YACxB,KAAK,YACL;AACN,QAAM,SAAS,OAAO,KAAK,WAAW,WAClC,KAAK,SACL,OAAO,KAAK,YAAY,WACtB,KAAK,UACL;AACN,SAAO,wBAAwB;AAAA,IAC7B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAAG,IAAI;AACT;",
6
- "names": ["params"]
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { DataTable, withDataTableNamespaces } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { TeamForm, type TeamFormValues, buildTeamPayload } from '@open-mercato/core/modules/staff/components/TeamForm'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { Plus } from 'lucide-react'\nimport { formatDateTime } from '@open-mercato/shared/lib/time'\n\nconst TEAM_MEMBERS_PAGE_SIZE = 50\n\ntype TeamRecord = {\n id: string\n name: string\n description?: string | null\n isActive?: boolean\n is_active?: boolean\n} & Record<string, unknown>\n\ntype TeamResponse = {\n items?: TeamRecord[]\n}\n\ntype TeamMemberRow = {\n id: string\n displayName: string\n description: string | null\n userEmail: string | null\n roleNames: string[]\n tags: string[]\n isActive: boolean\n updatedAt: string | null\n teamId: string | null\n}\n\ntype TeamMembersResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n totalPages?: number\n}\n\nexport default function StaffTeamEditPage({ params }: { params?: { id?: string } }) {\n const teamId = params?.id\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const scopeVersion = useOrganizationScopeVersion()\n const [initialValues, setInitialValues] = React.useState<TeamFormValues | null>(null)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n const [activeTab, setActiveTab] = React.useState<'details' | 'members'>('details')\n const [memberRows, setMemberRows] = React.useState<TeamMemberRow[]>([])\n const [memberPage, setMemberPage] = React.useState(1)\n const [memberTotal, setMemberTotal] = React.useState(0)\n const [memberTotalPages, setMemberTotalPages] = React.useState(1)\n const [memberSorting, setMemberSorting] = React.useState<SortingState>([{ id: 'displayName', desc: false }])\n const [memberSearch, setMemberSearch] = React.useState('')\n const [membersLoading, setMembersLoading] = React.useState(false)\n const [memberReloadToken, setMemberReloadToken] = React.useState(0)\n\n const memberLabels = React.useMemo(() => ({\n title: t('staff.teams.tabs.members', 'Team members'),\n description: t('staff.teamMembers.page.description', 'Manage employees and their team assignments.'),\n table: {\n name: t('staff.teamMembers.table.name', 'Name'),\n user: t('staff.teamMembers.table.user', 'User'),\n roles: t('staff.teamMembers.table.roles', 'Roles'),\n tags: t('staff.teamMembers.table.tags', 'Tags'),\n active: t('staff.teamMembers.table.active', 'Active'),\n updatedAt: t('staff.teamMembers.table.updatedAt', 'Updated'),\n empty: t('staff.teamMembers.table.empty', 'No team members yet.'),\n search: t('staff.teamMembers.table.search', 'Search team members...'),\n },\n actions: {\n add: t('staff.teamMembers.actions.add', 'Add team member'),\n edit: t('staff.teamMembers.actions.edit', 'Edit'),\n unassign: t('staff.teamMembers.actions.unassign', 'Unassign'),\n refresh: t('staff.teamMembers.actions.refresh', 'Refresh'),\n },\n messages: {\n unassigned: t('staff.teamMembers.messages.unassigned', 'Team member unassigned.'),\n },\n errors: {\n load: t('staff.teamMembers.errors.load', 'Failed to load team members.'),\n unassign: t('staff.teamMembers.errors.unassign', 'Failed to unassign team member.'),\n },\n tabs: {\n details: t('staff.teams.tabs.details', 'Details'),\n members: t('staff.teams.tabs.members', 'Team members'),\n label: t('staff.teams.tabs.label', 'Team sections'),\n },\n }), [t])\n\n const memberColumns = React.useMemo<ColumnDef<TeamMemberRow>[]>(() => [\n {\n accessorKey: 'displayName',\n header: memberLabels.table.name,\n meta: { priority: 1, sticky: true },\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.original.displayName}</span>\n {row.original.description ? (\n <span className=\"text-xs text-muted-foreground line-clamp-2\">{row.original.description}</span>\n ) : null}\n </div>\n ),\n },\n {\n accessorKey: 'userEmail',\n header: memberLabels.table.user,\n meta: { priority: 2 },\n cell: ({ row }) => row.original.userEmail\n ? <span className=\"text-sm\">{row.original.userEmail}</span>\n : <span className=\"text-xs text-muted-foreground\">-</span>,\n },\n {\n accessorKey: 'roleNames',\n header: memberLabels.table.roles,\n meta: { priority: 3 },\n cell: ({ row }) => row.original.roleNames.length\n ? <span className=\"text-sm\">{row.original.roleNames.join(', ')}</span>\n : <span className=\"text-xs text-muted-foreground\">-</span>,\n },\n {\n accessorKey: 'tags',\n header: memberLabels.table.tags,\n meta: { priority: 4 },\n cell: ({ row }) => row.original.tags.length\n ? <span className=\"text-xs text-muted-foreground\">{row.original.tags.join(', ')}</span>\n : <span className=\"text-xs text-muted-foreground\">-</span>,\n },\n {\n accessorKey: 'isActive',\n header: memberLabels.table.active,\n meta: { priority: 5 },\n cell: ({ row }) => <BooleanIcon value={row.original.isActive} />,\n },\n {\n accessorKey: 'updatedAt',\n header: memberLabels.table.updatedAt,\n meta: { priority: 6 },\n cell: ({ row }) => row.original.updatedAt\n ? <span className=\"text-xs text-muted-foreground\">{formatDateTime(row.original.updatedAt)}</span>\n : <span className=\"text-xs text-muted-foreground\">-</span>,\n },\n ], [\n memberLabels.table.active,\n memberLabels.table.name,\n memberLabels.table.roles,\n memberLabels.table.tags,\n memberLabels.table.updatedAt,\n memberLabels.table.user,\n ])\n\n React.useEffect(() => {\n if (!teamId) return\n const teamIdValue = teamId\n let cancelled = false\n async function loadTeam() {\n if (!cancelled) {\n setError(null)\n setIsNotFound(false)\n }\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '1', ids: teamIdValue })\n const payload = await readApiResultOrThrow<TeamResponse>(\n `/api/staff/teams?${params.toString()}`,\n undefined,\n { errorMessage: t('staff.teams.errors.load', 'Failed to load team.') },\n )\n const record = Array.isArray(payload.items) ? payload.items[0] : null\n if (!record) {\n if (!cancelled) setIsNotFound(true)\n return\n }\n const customFields = extractCustomFieldEntries(record)\n const isActive = typeof record.isActive === 'boolean'\n ? record.isActive\n : typeof record.is_active === 'boolean'\n ? record.is_active\n : true\n if (!cancelled) {\n setInitialValues({\n id: record.id,\n name: record.name ?? '',\n description: record.description ?? '',\n isActive,\n ...customFields,\n })\n }\n } catch (err) {\n if (!cancelled) {\n if ((err as { status?: number }).status === 404) {\n setIsNotFound(true)\n } else {\n const message = err instanceof Error ? err.message : t('staff.teams.errors.load', 'Failed to load team.')\n setError(message)\n }\n }\n }\n }\n loadTeam()\n return () => { cancelled = true }\n }, [teamId, t])\n\n React.useEffect(() => {\n if (!searchParams) return\n const tabParam = searchParams.get('tab')\n if (tabParam === 'members') {\n setActiveTab('members')\n }\n }, [searchParams])\n\n const loadTeamMembers = React.useCallback(async () => {\n if (!teamId) return\n setMembersLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(memberPage),\n pageSize: String(TEAM_MEMBERS_PAGE_SIZE),\n teamId,\n })\n const sort = memberSorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n if (memberSearch.trim()) params.set('search', memberSearch.trim())\n const payload = await readApiResultOrThrow<TeamMembersResponse>(\n `/api/staff/team-members?${params.toString()}`,\n undefined,\n { errorMessage: memberLabels.errors.load, fallback: { items: [], total: 0, totalPages: 1 } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setMemberRows(items.map(mapApiTeamMember))\n setMemberTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setMemberTotalPages(\n typeof payload.totalPages === 'number'\n ? payload.totalPages\n : Math.max(1, Math.ceil(items.length / TEAM_MEMBERS_PAGE_SIZE)),\n )\n } catch (error) {\n console.error('staff.teams.team-members.list', error)\n flash(memberLabels.errors.load, 'error')\n } finally {\n setMembersLoading(false)\n }\n }, [memberLabels.errors.load, memberPage, memberSearch, memberSorting, teamId])\n\n React.useEffect(() => {\n if (activeTab !== 'members') return\n void loadTeamMembers()\n }, [activeTab, loadTeamMembers, memberReloadToken, scopeVersion])\n\n const handleMemberSearchChange = React.useCallback((value: string) => {\n setMemberSearch(value)\n setMemberPage(1)\n }, [])\n\n const handleMemberRefresh = React.useCallback(() => {\n setMemberReloadToken((token) => token + 1)\n }, [])\n\n const handleUnassignMember = React.useCallback(async (entry: TeamMemberRow) => {\n if (!teamId || entry.teamId !== teamId) return\n try {\n await updateCrud('staff/team-members', { id: entry.id, teamId: null }, { errorMessage: memberLabels.errors.unassign })\n flash(memberLabels.messages.unassigned, 'success')\n handleMemberRefresh()\n } catch (error) {\n console.error('staff.teams.team-members.unassign', error)\n flash(memberLabels.errors.unassign, 'error')\n }\n }, [handleMemberRefresh, memberLabels.errors.unassign, memberLabels.messages.unassigned, teamId])\n\n const handleSubmit = React.useCallback(async (values: TeamFormValues) => {\n if (!teamId) return\n const payload = buildTeamPayload(values, { id: teamId })\n await updateCrud('staff/teams', payload, {\n errorMessage: t('staff.teams.errors.save', 'Failed to save team.'),\n })\n flash(t('staff.teams.messages.saved', 'Team saved.'), 'success')\n }, [teamId, t])\n\n const handleDelete = React.useCallback(async () => {\n if (!teamId) return\n await deleteCrud('staff/teams', teamId, {\n errorMessage: t('staff.teams.errors.delete', 'Failed to delete team.'),\n })\n router.push('/backend/staff/teams')\n }, [teamId, router, t])\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('staff.teams.errors.notFound', 'Team not found.')}\n backHref=\"/backend/staff/teams\"\n backLabel={t('staff.teams.actions.backToList', 'Back to teams')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error && !initialValues) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <div className=\"border-b\">\n <nav className=\"flex flex-wrap items-center gap-5 text-sm\" aria-label={memberLabels.tabs.label}>\n {[\n { id: 'details', label: memberLabels.tabs.details },\n { id: 'members', label: memberLabels.tabs.members },\n ].map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === tab.id}\n onClick={() => setActiveTab(tab.id as 'details' | 'members')}\n className={`relative -mb-px border-b-2 px-0 py-2 text-sm font-medium transition-colors ${\n activeTab === tab.id\n ? 'border-accent-indigo text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </nav>\n </div>\n\n {activeTab === 'details' ? (\n <TeamForm\n title={t('staff.teams.form.editTitle', 'Edit team')}\n backHref=\"/backend/staff/teams\"\n cancelHref=\"/backend/staff/teams\"\n initialValues={initialValues ?? { name: '', description: '', isActive: true }}\n onSubmit={handleSubmit}\n onDelete={handleDelete}\n isLoading={!initialValues}\n loadingMessage={t('staff.teams.form.loading', 'Loading team...')}\n extraActions={teamId ? (\n <SendObjectMessageDialog\n object={{\n entityModule: 'staff',\n entityType: 'team',\n entityId: teamId,\n previewData: { title: initialValues?.name ?? ''},\n }}\n viewHref={`/backend/staff/teams/${teamId}/edit`}\n />\n ) : undefined}\n />\n ) : (\n <DataTable<TeamMemberRow>\n title={memberLabels.title}\n data={memberRows}\n columns={memberColumns}\n isLoading={membersLoading}\n searchValue={memberSearch}\n onSearchChange={handleMemberSearchChange}\n searchPlaceholder={memberLabels.table.search}\n emptyState={<p className=\"py-8 text-center text-sm text-muted-foreground\">{memberLabels.table.empty}</p>}\n actions={(\n <Button asChild size=\"sm\">\n <Link href={`/backend/staff/team-members/create?teamId=${encodeURIComponent(teamId ?? '')}`}>\n <Plus className=\"mr-2 h-4 w-4\" aria-hidden />\n {memberLabels.actions.add}\n </Link>\n </Button>\n )}\n refreshButton={{\n label: memberLabels.actions.refresh,\n onRefresh: handleMemberRefresh,\n isRefreshing: membersLoading,\n }}\n sortable\n sorting={memberSorting}\n onSortingChange={setMemberSorting}\n pagination={{\n page: memberPage,\n pageSize: TEAM_MEMBERS_PAGE_SIZE,\n total: memberTotal,\n totalPages: memberTotalPages,\n onPageChange: setMemberPage,\n }}\n rowActions={(row) => (\n <RowActions\n items={[\n { id: 'edit', label: memberLabels.actions.edit, onSelect: () => { router.push(`/backend/staff/team-members/${row.id}`) } },\n { id: 'unassign', label: memberLabels.actions.unassign, onSelect: () => { void handleUnassignMember(row) } },\n ]}\n />\n )}\n />\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n\nfunction mapApiTeamMember(item: Record<string, unknown>): TeamMemberRow {\n const id = typeof item.id === 'string' ? item.id : ''\n const displayName = typeof item.displayName === 'string'\n ? item.displayName\n : typeof item.display_name === 'string'\n ? item.display_name\n : id\n const description = typeof item.description === 'string' && item.description.trim().length\n ? item.description.trim()\n : null\n const user = item.user && typeof item.user === 'object' ? item.user as { email?: unknown } : null\n const userEmail = user && typeof user.email === 'string' && user.email.length ? user.email : null\n const roleNames = Array.isArray(item.roleNames) ? item.roleNames.filter((value): value is string => typeof value === 'string') : []\n const tags = Array.isArray(item.tags) ? item.tags.filter((value): value is string => typeof value === 'string') : []\n const updatedAt = typeof item.updatedAt === 'string'\n ? item.updatedAt\n : typeof item.updated_at === 'string'\n ? item.updated_at\n : null\n const isActive = typeof item.isActive === 'boolean'\n ? item.isActive\n : typeof item.is_active === 'boolean'\n ? item.is_active\n : true\n const teamId = typeof item.teamId === 'string'\n ? item.teamId\n : typeof item.team_id === 'string'\n ? item.team_id\n : null\n return withDataTableNamespaces({\n id,\n displayName,\n description,\n userEmail,\n roleNames,\n tags,\n isActive,\n updatedAt,\n teamId,\n }, item)\n}\n\n"],
5
+ "mappings": "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",
6
+ "names": ["params", "error"]
7
7
  }
@@ -8,6 +8,7 @@ import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
8
8
  import { Spinner } from "@open-mercato/ui/primitives/spinner";
9
9
  import { Button } from "@open-mercato/ui/primitives/button";
10
10
  import { Alert, AlertDescription } from "@open-mercato/ui/primitives/alert";
11
+ import { ErrorMessage, RecordNotFoundState } from "@open-mercato/ui/backend/detail";
11
12
  import { apiFetch } from "@open-mercato/ui/backend/utils/api";
12
13
  import { readJsonSafe } from "@open-mercato/ui/backend/utils/serverErrors";
13
14
  import { formatWorkflowValidationError } from "../../../lib/format-validation-error.js";
@@ -43,13 +44,20 @@ function EditWorkflowDefinitionPage() {
43
44
  queryFn: async () => {
44
45
  const response = await apiFetch(`/api/workflows/definitions/${definitionId}`);
45
46
  if (!response.ok) {
46
- throw new Error(t("workflows.errors.fetchFailed"));
47
+ const err = new Error(t("workflows.errors.fetchFailed"));
48
+ err.status = response.status;
49
+ throw err;
47
50
  }
48
51
  const result = await response.json();
49
52
  return result.data;
50
53
  },
51
- enabled: !!definitionId
54
+ enabled: !!definitionId,
55
+ retry: (failureCount, err) => {
56
+ if (err.status === 404) return false;
57
+ return failureCount < 2;
58
+ }
52
59
  });
60
+ const isNotFound = error?.status === 404;
53
61
  const initialValues = React.useMemo(() => {
54
62
  if (definition) {
55
63
  return parseWorkflowToFormValues(definition);
@@ -180,11 +188,21 @@ function EditWorkflowDefinitionPage() {
180
188
  /* @__PURE__ */ jsx("span", { children: t("workflows.edit.loading") })
181
189
  ] }) }) });
182
190
  }
191
+ if (isNotFound) {
192
+ return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
193
+ RecordNotFoundState,
194
+ {
195
+ label: t("workflows.errors.notFound", t("workflows.errors.loadFailed")),
196
+ backHref: "/backend/definitions",
197
+ backLabel: t("workflows.backToList")
198
+ }
199
+ ) }) });
200
+ }
183
201
  if (error || !definition) {
184
- return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsxs("div", { className: "flex h-[50vh] flex-col items-center justify-center gap-2 text-muted-foreground", children: [
185
- /* @__PURE__ */ jsx("p", { children: t("workflows.errors.loadFailed") }),
186
- /* @__PURE__ */ jsx(Button, { asChild: true, variant: "outline", children: /* @__PURE__ */ jsx("a", { href: "/backend/definitions", children: t("workflows.backToList") }) })
187
- ] }) }) });
202
+ return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsxs(PageBody, { children: [
203
+ /* @__PURE__ */ jsx(ErrorMessage, { label: t("workflows.errors.loadFailed") }),
204
+ /* @__PURE__ */ jsx("div", { className: "mt-4 flex justify-center", children: /* @__PURE__ */ jsx(Button, { asChild: true, variant: "outline", children: /* @__PURE__ */ jsx("a", { href: "/backend/definitions", children: t("workflows.backToList") }) }) })
205
+ ] }) });
188
206
  }
189
207
  if (!initialValues) {
190
208
  return null;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/workflows/backend/definitions/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter, useParams } from 'next/navigation'\nimport { useQuery } from '@tanstack/react-query'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { apiFetch } from '@open-mercato/ui/backend/utils/api'\nimport { readJsonSafe } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { formatWorkflowValidationError } from '../../../lib/format-validation-error'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport {\n workflowDefinitionFormSchema,\n createFormGroups,\n createFieldDefinitions,\n parseWorkflowToFormValues,\n buildWorkflowPayload,\n type WorkflowDefinitionFormValues,\n} from '../../../components/formConfig'\nimport { StepsEditor } from '../../../components/StepsEditor'\nimport { TransitionsEditor } from '../../../components/TransitionsEditor'\nimport { DefinitionTriggersEditor } from '../../../components/DefinitionTriggersEditor'\nimport { MobileDefinitionDetail } from '../../../components/mobile/MobileDefinitionDetail'\nimport { useIsMobile } from '@open-mercato/ui/hooks/useIsMobile'\nimport type { WorkflowDefinitionTrigger } from '../../../data/entities'\n\nexport default function EditWorkflowDefinitionPage() {\n const router = useRouter()\n const params = useParams()\n const t = useT()\n const isMobile = useIsMobile()\n\n // Handle catch-all route: params.slug = ['definitions', 'uuid']\n let definitionId: string | undefined\n if (params?.slug && Array.isArray(params.slug)) {\n definitionId = params.slug[1] // Second element is the ID\n } else if (params?.id) {\n definitionId = Array.isArray(params.id) ? params.id[0] : params.id\n }\n\n const { data: definition, isLoading, error } = useQuery({\n queryKey: ['workflow-definition', definitionId],\n queryFn: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}`)\n if (!response.ok) {\n throw new Error(t('workflows.errors.fetchFailed'))\n }\n const result = await response.json()\n return result.data\n },\n enabled: !!definitionId,\n })\n\n const initialValues = React.useMemo(() => {\n if (definition) {\n return parseWorkflowToFormValues(definition)\n }\n return null\n }, [definition])\n\n const [triggers, setTriggers] = React.useState<WorkflowDefinitionTrigger[]>([])\n\n React.useEffect(() => {\n setTriggers(initialValues?.triggers ?? [])\n }, [initialValues])\n\n const source = definition?.source as 'code' | 'code_override' | 'user' | undefined\n const isCodeOnly = source === 'code'\n const isCodeOverride = source === 'code_override'\n\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const mutationContextId = React.useMemo(\n () => `workflows.definitions.detail:${definitionId ?? 'unknown'}`,\n [definitionId],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: mutationContextId,\n })\n\n const handleSubmit = async (values: WorkflowDefinitionFormValues) => {\n const payload = buildWorkflowPayload({ ...values, triggers })\n\n await runMutation({\n operation: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!response.ok) {\n const errorBody = await readJsonSafe<{ error?: string; details?: Array<{ path?: Array<string | number>; message?: string }> }>(response, null)\n throw new Error(formatWorkflowValidationError(errorBody, t('workflows.errors.updateFailed')))\n }\n return response\n },\n mutationPayload: { resourceId: definitionId, operation: 'update' },\n context: {\n formId: mutationContextId,\n resourceKind: 'workflows.definition',\n resourceId: definitionId,\n operation: 'update',\n retryLastMutation,\n },\n })\n\n router.push('/backend/definitions')\n router.refresh()\n }\n\n const handleCustomize = async () => {\n try {\n const result = await runMutation({\n operation: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}/customize`, {\n method: 'POST',\n })\n if (!response.ok) {\n const errorBody = await readJsonSafe<{ error?: string }>(response, null)\n throw new Error(errorBody?.error || t('workflows.errors.updateFailed'))\n }\n return readJsonSafe<{ data?: { id?: string } }>(response, null)\n },\n mutationPayload: { resourceId: definitionId, operation: 'customize' },\n context: {\n formId: mutationContextId,\n resourceKind: 'workflows.definition',\n resourceId: definitionId,\n operation: 'customize',\n retryLastMutation,\n },\n })\n if (result?.data?.id) {\n router.push(`/backend/definitions/${result.data.id}`)\n router.refresh()\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('workflows.errors.updateFailed')\n flash(message, 'error')\n }\n }\n\n const handleResetToCode = async () => {\n const confirmed = await confirm({\n title: t('workflows.actions.resetToCode'),\n description: t('workflows.actions.resetConfirm'),\n confirmText: t('workflows.actions.resetToCode'),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n const result = await runMutation({\n operation: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}/reset-to-code`, {\n method: 'POST',\n })\n if (!response.ok) {\n const errorBody = await readJsonSafe<{ error?: string }>(response, null)\n throw new Error(errorBody?.error || t('workflows.messages.updateFailed'))\n }\n return readJsonSafe<{ data?: { id?: string } }>(response, null)\n },\n mutationPayload: { resourceId: definitionId, operation: 'reset-to-code' },\n context: {\n formId: mutationContextId,\n resourceKind: 'workflows.definition',\n resourceId: definitionId,\n operation: 'reset-to-code',\n retryLastMutation,\n },\n })\n flash(t('workflows.messages.updated'), 'success')\n const codeId = result?.data?.id || `code:${definition?.workflowId}`\n router.push(`/backend/definitions/${codeId}`)\n router.refresh()\n } catch {\n flash(t('workflows.messages.updateFailed'), 'error')\n }\n }\n\n const fields = React.useMemo(() => createFieldDefinitions(t), [t])\n\n const formGroups = React.useMemo(\n () => isMobile ? [] : createFormGroups(t, StepsEditor, TransitionsEditor),\n [t, isMobile]\n )\n\n const navigateToVisualEditor = React.useCallback(() => {\n router.push(`/backend/definitions/visual-editor?id=${definitionId}`)\n }, [router, definitionId])\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('workflows.edit.loading')}</span>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (error || !definition) {\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>{t('workflows.errors.loadFailed')}</p>\n <Button asChild variant=\"outline\">\n <a href=\"/backend/definitions\">{t('workflows.backToList')}</a>\n </Button>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (!initialValues) {\n return null\n }\n\n return (\n <Page>\n <PageBody>\n {isCodeOnly && (\n <Alert variant=\"info\" className=\"mb-4\">\n <AlertDescription className=\"flex items-center justify-between\">\n <span>{t('workflows.source.code.readonlyBanner')}</span>\n <Button variant=\"outline\" size=\"sm\" onClick={handleCustomize}>\n {t('workflows.actions.customize')}\n </Button>\n </AlertDescription>\n </Alert>\n )}\n {isCodeOverride && (\n <Alert variant=\"warning\" className=\"mb-4\">\n <AlertDescription className=\"flex items-center justify-between\">\n <span>{t('workflows.source.code_override.banner')}</span>\n <Button variant=\"outline\" size=\"sm\" onClick={handleResetToCode}>\n {t('workflows.actions.resetToCode')}\n </Button>\n </AlertDescription>\n </Alert>\n )}\n <div className=\"mb-4 p-4 bg-status-info-bg border border-status-info-border rounded-lg flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <svg className=\"w-5 h-5 text-status-info-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 10V3L4 14h7v7l9-11h-7z\" />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-status-info-text\">\n {t('workflows.edit.visualEditorAvailable')}\n </p>\n <p className=\"text-xs text-status-info-text mt-0.5\">\n {t('workflows.edit.visualEditorDescription')}\n </p>\n </div>\n </div>\n <Button asChild variant=\"outline\" size=\"sm\" className=\"w-full sm:w-auto border-status-info-border text-status-info-text hover:bg-status-info-bg\">\n <a href={`/backend/definitions/visual-editor?id=${definitionId}`}>\n {t('workflows.actions.openVisualEditor')}\n </a>\n </Button>\n </div>\n <CrudForm\n key={definitionId}\n title={isCodeOnly ? definition?.workflowName || t('workflows.edit.title') : t('workflows.edit.title')}\n backHref=\"/backend/definitions\"\n schema={workflowDefinitionFormSchema}\n fields={fields}\n initialValues={initialValues}\n onSubmit={handleSubmit}\n cancelHref=\"/backend/definitions\"\n groups={formGroups}\n submitLabel={isCodeOnly ? t('workflows.actions.customize') : t('workflows.form.update')}\n {...(isCodeOnly ? { readOnly: true } : {})}\n />\n\n {/* Mobile Steps & Transitions View */}\n {isMobile && initialValues && (\n <div className=\"mt-4\">\n <MobileDefinitionDetail\n values={initialValues}\n onEditStep={navigateToVisualEditor}\n onDeleteStep={navigateToVisualEditor}\n onAddStep={navigateToVisualEditor}\n onEditTransition={navigateToVisualEditor}\n onDeleteTransition={navigateToVisualEditor}\n onAddTransition={navigateToVisualEditor}\n />\n </div>\n )}\n\n {/* Event Triggers Section */}\n <div className=\"mt-8\">\n <DefinitionTriggersEditor\n value={triggers}\n onChange={setTriggers}\n />\n </div>\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter, useParams } from 'next/navigation'\nimport { useQuery } from '@tanstack/react-query'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { apiFetch } from '@open-mercato/ui/backend/utils/api'\nimport { readJsonSafe } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { formatWorkflowValidationError } from '../../../lib/format-validation-error'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport {\n workflowDefinitionFormSchema,\n createFormGroups,\n createFieldDefinitions,\n parseWorkflowToFormValues,\n buildWorkflowPayload,\n type WorkflowDefinitionFormValues,\n} from '../../../components/formConfig'\nimport { StepsEditor } from '../../../components/StepsEditor'\nimport { TransitionsEditor } from '../../../components/TransitionsEditor'\nimport { DefinitionTriggersEditor } from '../../../components/DefinitionTriggersEditor'\nimport { MobileDefinitionDetail } from '../../../components/mobile/MobileDefinitionDetail'\nimport { useIsMobile } from '@open-mercato/ui/hooks/useIsMobile'\nimport type { WorkflowDefinitionTrigger } from '../../../data/entities'\n\nexport default function EditWorkflowDefinitionPage() {\n const router = useRouter()\n const params = useParams()\n const t = useT()\n const isMobile = useIsMobile()\n\n // Handle catch-all route: params.slug = ['definitions', 'uuid']\n let definitionId: string | undefined\n if (params?.slug && Array.isArray(params.slug)) {\n definitionId = params.slug[1] // Second element is the ID\n } else if (params?.id) {\n definitionId = Array.isArray(params.id) ? params.id[0] : params.id\n }\n\n const { data: definition, isLoading, error } = useQuery({\n queryKey: ['workflow-definition', definitionId],\n queryFn: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}`)\n if (!response.ok) {\n const err = new Error(t('workflows.errors.fetchFailed')) as Error & { status?: number }\n err.status = response.status\n throw err\n }\n const result = await response.json()\n return result.data\n },\n enabled: !!definitionId,\n retry: (failureCount, err) => {\n if ((err as { status?: number }).status === 404) return false\n return failureCount < 2\n },\n })\n\n const isNotFound = (error as { status?: number } | null)?.status === 404\n\n const initialValues = React.useMemo(() => {\n if (definition) {\n return parseWorkflowToFormValues(definition)\n }\n return null\n }, [definition])\n\n const [triggers, setTriggers] = React.useState<WorkflowDefinitionTrigger[]>([])\n\n React.useEffect(() => {\n setTriggers(initialValues?.triggers ?? [])\n }, [initialValues])\n\n const source = definition?.source as 'code' | 'code_override' | 'user' | undefined\n const isCodeOnly = source === 'code'\n const isCodeOverride = source === 'code_override'\n\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const mutationContextId = React.useMemo(\n () => `workflows.definitions.detail:${definitionId ?? 'unknown'}`,\n [definitionId],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: mutationContextId,\n })\n\n const handleSubmit = async (values: WorkflowDefinitionFormValues) => {\n const payload = buildWorkflowPayload({ ...values, triggers })\n\n await runMutation({\n operation: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!response.ok) {\n const errorBody = await readJsonSafe<{ error?: string; details?: Array<{ path?: Array<string | number>; message?: string }> }>(response, null)\n throw new Error(formatWorkflowValidationError(errorBody, t('workflows.errors.updateFailed')))\n }\n return response\n },\n mutationPayload: { resourceId: definitionId, operation: 'update' },\n context: {\n formId: mutationContextId,\n resourceKind: 'workflows.definition',\n resourceId: definitionId,\n operation: 'update',\n retryLastMutation,\n },\n })\n\n router.push('/backend/definitions')\n router.refresh()\n }\n\n const handleCustomize = async () => {\n try {\n const result = await runMutation({\n operation: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}/customize`, {\n method: 'POST',\n })\n if (!response.ok) {\n const errorBody = await readJsonSafe<{ error?: string }>(response, null)\n throw new Error(errorBody?.error || t('workflows.errors.updateFailed'))\n }\n return readJsonSafe<{ data?: { id?: string } }>(response, null)\n },\n mutationPayload: { resourceId: definitionId, operation: 'customize' },\n context: {\n formId: mutationContextId,\n resourceKind: 'workflows.definition',\n resourceId: definitionId,\n operation: 'customize',\n retryLastMutation,\n },\n })\n if (result?.data?.id) {\n router.push(`/backend/definitions/${result.data.id}`)\n router.refresh()\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('workflows.errors.updateFailed')\n flash(message, 'error')\n }\n }\n\n const handleResetToCode = async () => {\n const confirmed = await confirm({\n title: t('workflows.actions.resetToCode'),\n description: t('workflows.actions.resetConfirm'),\n confirmText: t('workflows.actions.resetToCode'),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n const result = await runMutation({\n operation: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}/reset-to-code`, {\n method: 'POST',\n })\n if (!response.ok) {\n const errorBody = await readJsonSafe<{ error?: string }>(response, null)\n throw new Error(errorBody?.error || t('workflows.messages.updateFailed'))\n }\n return readJsonSafe<{ data?: { id?: string } }>(response, null)\n },\n mutationPayload: { resourceId: definitionId, operation: 'reset-to-code' },\n context: {\n formId: mutationContextId,\n resourceKind: 'workflows.definition',\n resourceId: definitionId,\n operation: 'reset-to-code',\n retryLastMutation,\n },\n })\n flash(t('workflows.messages.updated'), 'success')\n const codeId = result?.data?.id || `code:${definition?.workflowId}`\n router.push(`/backend/definitions/${codeId}`)\n router.refresh()\n } catch {\n flash(t('workflows.messages.updateFailed'), 'error')\n }\n }\n\n const fields = React.useMemo(() => createFieldDefinitions(t), [t])\n\n const formGroups = React.useMemo(\n () => isMobile ? [] : createFormGroups(t, StepsEditor, TransitionsEditor),\n [t, isMobile]\n )\n\n const navigateToVisualEditor = React.useCallback(() => {\n router.push(`/backend/definitions/visual-editor?id=${definitionId}`)\n }, [router, definitionId])\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('workflows.edit.loading')}</span>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('workflows.errors.notFound', t('workflows.errors.loadFailed'))}\n backHref=\"/backend/definitions\"\n backLabel={t('workflows.backToList')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !definition) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={t('workflows.errors.loadFailed')} />\n <div className=\"mt-4 flex justify-center\">\n <Button asChild variant=\"outline\">\n <a href=\"/backend/definitions\">{t('workflows.backToList')}</a>\n </Button>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (!initialValues) {\n return null\n }\n\n return (\n <Page>\n <PageBody>\n {isCodeOnly && (\n <Alert variant=\"info\" className=\"mb-4\">\n <AlertDescription className=\"flex items-center justify-between\">\n <span>{t('workflows.source.code.readonlyBanner')}</span>\n <Button variant=\"outline\" size=\"sm\" onClick={handleCustomize}>\n {t('workflows.actions.customize')}\n </Button>\n </AlertDescription>\n </Alert>\n )}\n {isCodeOverride && (\n <Alert variant=\"warning\" className=\"mb-4\">\n <AlertDescription className=\"flex items-center justify-between\">\n <span>{t('workflows.source.code_override.banner')}</span>\n <Button variant=\"outline\" size=\"sm\" onClick={handleResetToCode}>\n {t('workflows.actions.resetToCode')}\n </Button>\n </AlertDescription>\n </Alert>\n )}\n <div className=\"mb-4 p-4 bg-status-info-bg border border-status-info-border rounded-lg flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <svg className=\"w-5 h-5 text-status-info-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 10V3L4 14h7v7l9-11h-7z\" />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-status-info-text\">\n {t('workflows.edit.visualEditorAvailable')}\n </p>\n <p className=\"text-xs text-status-info-text mt-0.5\">\n {t('workflows.edit.visualEditorDescription')}\n </p>\n </div>\n </div>\n <Button asChild variant=\"outline\" size=\"sm\" className=\"w-full sm:w-auto border-status-info-border text-status-info-text hover:bg-status-info-bg\">\n <a href={`/backend/definitions/visual-editor?id=${definitionId}`}>\n {t('workflows.actions.openVisualEditor')}\n </a>\n </Button>\n </div>\n <CrudForm\n key={definitionId}\n title={isCodeOnly ? definition?.workflowName || t('workflows.edit.title') : t('workflows.edit.title')}\n backHref=\"/backend/definitions\"\n schema={workflowDefinitionFormSchema}\n fields={fields}\n initialValues={initialValues}\n onSubmit={handleSubmit}\n cancelHref=\"/backend/definitions\"\n groups={formGroups}\n submitLabel={isCodeOnly ? t('workflows.actions.customize') : t('workflows.form.update')}\n {...(isCodeOnly ? { readOnly: true } : {})}\n />\n\n {/* Mobile Steps & Transitions View */}\n {isMobile && initialValues && (\n <div className=\"mt-4\">\n <MobileDefinitionDetail\n values={initialValues}\n onEditStep={navigateToVisualEditor}\n onDeleteStep={navigateToVisualEditor}\n onAddStep={navigateToVisualEditor}\n onEditTransition={navigateToVisualEditor}\n onDeleteTransition={navigateToVisualEditor}\n onAddTransition={navigateToVisualEditor}\n />\n </div>\n )}\n\n {/* Event Triggers Section */}\n <div className=\"mt-8\">\n <DefinitionTriggersEditor\n value={triggers}\n onChange={setTriggers}\n />\n </div>\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["error"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.4-develop.4360.1.568bf6e32b",
3
+ "version": "0.6.4-develop.4368.1.2f7e9a7002",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -243,16 +243,16 @@
243
243
  "zod": "^4.4.3"
244
244
  },
245
245
  "peerDependencies": {
246
- "@open-mercato/ai-assistant": "0.6.4-develop.4360.1.568bf6e32b",
247
- "@open-mercato/shared": "0.6.4-develop.4360.1.568bf6e32b",
248
- "@open-mercato/ui": "0.6.4-develop.4360.1.568bf6e32b",
246
+ "@open-mercato/ai-assistant": "0.6.4-develop.4368.1.2f7e9a7002",
247
+ "@open-mercato/shared": "0.6.4-develop.4368.1.2f7e9a7002",
248
+ "@open-mercato/ui": "0.6.4-develop.4368.1.2f7e9a7002",
249
249
  "react": "^19.0.0",
250
250
  "react-dom": "^19.0.0"
251
251
  },
252
252
  "devDependencies": {
253
- "@open-mercato/ai-assistant": "0.6.4-develop.4360.1.568bf6e32b",
254
- "@open-mercato/shared": "0.6.4-develop.4360.1.568bf6e32b",
255
- "@open-mercato/ui": "0.6.4-develop.4360.1.568bf6e32b",
253
+ "@open-mercato/ai-assistant": "0.6.4-develop.4368.1.2f7e9a7002",
254
+ "@open-mercato/shared": "0.6.4-develop.4368.1.2f7e9a7002",
255
+ "@open-mercato/ui": "0.6.4-develop.4368.1.2f7e9a7002",
256
256
  "@testing-library/dom": "^10.4.1",
257
257
  "@testing-library/jest-dom": "^6.9.1",
258
258
  "@testing-library/react": "^16.3.1",
@@ -7,6 +7,7 @@ import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
7
7
  import { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
8
8
  import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
9
9
  import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
10
+ import { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'
10
11
  import { useT } from '@open-mercato/shared/lib/i18n/context'
11
12
  import { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'
12
13
  import { E } from '#generated/entities.ids.generated'
@@ -81,6 +82,7 @@ export default function EditCatalogCategoryPage({ params }: { params?: { id?: st
81
82
  const [pathLabel, setPathLabel] = React.useState<string>('')
82
83
  const [loading, setLoading] = React.useState<boolean>(true)
83
84
  const [error, setError] = React.useState<string | null>(null)
85
+ const [isNotFound, setIsNotFound] = React.useState<boolean>(false)
84
86
 
85
87
  React.useEffect(() => {
86
88
  if (!categoryId) return
@@ -88,13 +90,23 @@ export default function EditCatalogCategoryPage({ params }: { params?: { id?: st
88
90
  async function load() {
89
91
  setLoading(true)
90
92
  setError(null)
93
+ setIsNotFound(false)
91
94
  try {
92
- const { ok, result } = await apiCall<CategoryResponse>(
95
+ const { ok, status, result } = await apiCall<CategoryResponse>(
93
96
  `/api/catalog/categories?view=manage&ids=${encodeURIComponent(categoryId)}&status=all&page=1&pageSize=1`,
94
97
  )
95
- if (!ok) throw new Error(t('catalog.categories.form.errors.load', 'Failed to load category'))
98
+ if (!ok) {
99
+ if (status === 404) {
100
+ if (!cancelled) setIsNotFound(true)
101
+ return
102
+ }
103
+ throw new Error(t('catalog.categories.form.errors.load', 'Failed to load category'))
104
+ }
96
105
  const record = Array.isArray(result?.items) ? result.items?.[0] : null
97
- if (!record) throw new Error(t('catalog.categories.form.errors.notFound', 'Category not found'))
106
+ if (!record) {
107
+ if (!cancelled) setIsNotFound(true)
108
+ return
109
+ }
98
110
  if (cancelled) return
99
111
  const customValues = extractCustomFieldEntries(record as Record<string, unknown>)
100
112
  setInitialValues({
@@ -191,14 +203,33 @@ export default function EditCatalogCategoryPage({ params }: { params?: { id?: st
191
203
  )
192
204
  }
193
205
 
206
+ if (isNotFound) {
207
+ return (
208
+ <Page>
209
+ <PageBody>
210
+ <RecordNotFoundState
211
+ label={t('catalog.categories.form.errors.notFound', 'Category not found')}
212
+ backHref="/backend/catalog/categories"
213
+ backLabel={t('catalog.categories.form.actions.backToList', 'Back to categories')}
214
+ />
215
+ </PageBody>
216
+ </Page>
217
+ )
218
+ }
219
+
220
+ if (error && !loading) {
221
+ return (
222
+ <Page>
223
+ <PageBody>
224
+ <ErrorMessage label={error} />
225
+ </PageBody>
226
+ </Page>
227
+ )
228
+ }
229
+
194
230
  return (
195
231
  <Page>
196
232
  <PageBody>
197
- {error ? (
198
- <div className="mb-4 rounded border border-destructive/50 bg-destructive/10 px-3 py-2 text-sm text-destructive">
199
- {error}
200
- </div>
201
- ) : null}
202
233
  <CrudForm<CategoryFormValues>
203
234
  title={t('catalog.categories.form.editTitle', 'Edit category')}
204
235
  backHref="/backend/catalog/categories"
@@ -9,7 +9,7 @@ import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors
9
9
  import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
10
10
  import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
11
11
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
12
- import { ErrorMessage } from '@open-mercato/ui/backend/detail'
12
+ import { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'
13
13
  import { useT } from '@open-mercato/shared/lib/i18n/context'
14
14
  import { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'
15
15
  import { E } from '#generated/entities.ids.generated'
@@ -95,6 +95,7 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
95
95
  const [existingPriceIds, setExistingPriceIds] = React.useState<Record<string, string>>({})
96
96
  const [loading, setLoading] = React.useState(true)
97
97
  const [error, setError] = React.useState<string | null>(null)
98
+ const [isNotFound, setIsNotFound] = React.useState(false)
98
99
  const [currentProductId, setCurrentProductId] = React.useState<string | null>(productId)
99
100
  const [productTitle, setProductTitle] = React.useState<string>('')
100
101
  const [productTaxRateId, setProductTaxRateId] = React.useState<string | null>(null)
@@ -162,13 +163,23 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
162
163
  async function load() {
163
164
  setLoading(true)
164
165
  setError(null)
166
+ setIsNotFound(false)
165
167
  try {
166
168
  const variantRes = await apiCall<VariantResponse>(
167
169
  `/api/catalog/variants?id=${encodeURIComponent(variantId!)}&page=1&pageSize=1`,
168
170
  )
169
- if (!variantRes.ok) throw new Error(t('catalog.variants.form.errors.load', 'Failed to load variant.'))
171
+ if (!variantRes.ok) {
172
+ if (variantRes.status === 404) {
173
+ if (!cancelled) setIsNotFound(true)
174
+ return
175
+ }
176
+ throw new Error(t('catalog.variants.form.errors.load', 'Failed to load variant.'))
177
+ }
170
178
  const record = Array.isArray(variantRes.result?.items) ? variantRes.result?.items?.[0] : undefined
171
- if (!record) throw new Error(t('catalog.variants.form.errors.notFound', 'Variant not found.'))
179
+ if (!record) {
180
+ if (!cancelled) setIsNotFound(true)
181
+ return
182
+ }
172
183
  const resolvedProductId =
173
184
  typeof record.product_id === 'string'
174
185
  ? record.product_id
@@ -418,12 +429,33 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
418
429
  : t('catalog.variants.form.editTitle', 'Edit variant')
419
430
  const productVariantsHref = `/backend/catalog/products/${currentProductId}#variants`
420
431
 
432
+ if (isNotFound) {
433
+ return (
434
+ <Page>
435
+ <PageBody>
436
+ <RecordNotFoundState
437
+ label={t('catalog.variants.form.errors.notFound', 'Variant not found.')}
438
+ backHref={productVariantsHref}
439
+ backLabel={t('catalog.variants.form.actions.backToProduct', 'Back to product variants')}
440
+ />
441
+ </PageBody>
442
+ </Page>
443
+ )
444
+ }
445
+
446
+ if (error && !loading) {
447
+ return (
448
+ <Page>
449
+ <PageBody>
450
+ <ErrorMessage label={error} />
451
+ </PageBody>
452
+ </Page>
453
+ )
454
+ }
455
+
421
456
  return (
422
457
  <Page>
423
458
  <PageBody>
424
- {error ? (
425
- <ErrorMessage label={error} className="mb-4" />
426
- ) : null}
427
459
  <CrudForm<VariantFormValues>
428
460
  title={formTitle}
429
461
  backHref={productVariantsHref}
@@ -40,6 +40,7 @@
40
40
  "catalog.categories.flash.updated": "Kategorie aktualisiert",
41
41
  "catalog.categories.form.action.create": "Erstellen",
42
42
  "catalog.categories.form.action.save": "Speichern",
43
+ "catalog.categories.form.actions.backToList": "Zurück zu Kategorien",
43
44
  "catalog.categories.form.createTitle": "Kategorie erstellen",
44
45
  "catalog.categories.form.editTitle": "Kategorie bearbeiten",
45
46
  "catalog.categories.form.errors.delete": "Kategorie konnte nicht gelöscht werden",
@@ -559,6 +560,7 @@
559
560
  "catalog.stats.tags": "Tags",
560
561
  "catalog.stats.title": "Katalogübersicht",
561
562
  "catalog.variants.errors.skuExists": "SKU wird bereits verwendet.",
563
+ "catalog.variants.form.actions.backToProduct": "Zurück zu Produktvarianten",
562
564
  "catalog.variants.form.barcodeLabel": "Barcode",
563
565
  "catalog.variants.form.barcodePlaceholder": "EAN, UPC usw.",
564
566
  "catalog.variants.form.createAction": "Variante erstellen",