@open-mercato/core 0.6.8-develop.7096.1.97319f09f6 → 0.6.8-develop.7100.1.fbf66fca35

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/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/api/users/route.js +13 -5
  3. package/dist/modules/auth/api/users/route.js.map +2 -2
  4. package/dist/modules/auth/lib/userIdFilter.js +16 -0
  5. package/dist/modules/auth/lib/userIdFilter.js.map +7 -0
  6. package/dist/modules/devices/acl.js +9 -1
  7. package/dist/modules/devices/acl.js.map +2 -2
  8. package/dist/modules/devices/backend/devices/[id]/page.js +4 -20
  9. package/dist/modules/devices/backend/devices/[id]/page.js.map +2 -2
  10. package/dist/modules/devices/backend/devices/create/page.js +17 -1
  11. package/dist/modules/devices/backend/devices/create/page.js.map +2 -2
  12. package/dist/modules/devices/backend/devices/page.js +13 -21
  13. package/dist/modules/devices/backend/devices/page.js.map +2 -2
  14. package/dist/modules/devices/backend/devices/useDeviceUserLabels.js +34 -0
  15. package/dist/modules/devices/backend/devices/useDeviceUserLabels.js.map +7 -0
  16. package/dist/modules/devices/backend/devices/userOptions.js +52 -0
  17. package/dist/modules/devices/backend/devices/userOptions.js.map +7 -0
  18. package/dist/modules/devices/setup.js +6 -2
  19. package/dist/modules/devices/setup.js.map +2 -2
  20. package/dist/modules/wms/api/warehouseSearch.js +21 -0
  21. package/dist/modules/wms/api/warehouseSearch.js.map +7 -0
  22. package/dist/modules/wms/api/warehouses/route.js +13 -9
  23. package/dist/modules/wms/api/warehouses/route.js.map +2 -2
  24. package/dist/modules/wms/commands/configuration.js +59 -5
  25. package/dist/modules/wms/commands/configuration.js.map +2 -2
  26. package/dist/modules/wms/commands/shared.js +4 -0
  27. package/dist/modules/wms/commands/shared.js.map +2 -2
  28. package/dist/modules/wms/components/backend/WarehouseEditDialog.js +158 -0
  29. package/dist/modules/wms/components/backend/WarehouseEditDialog.js.map +7 -0
  30. package/dist/modules/wms/components/backend/WmsConfigurationPage.js +29 -103
  31. package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +2 -2
  32. package/dist/modules/wms/components/backend/warehouseFormOptions.js +66 -0
  33. package/dist/modules/wms/components/backend/warehouseFormOptions.js.map +7 -0
  34. package/package.json +7 -7
  35. package/src/modules/auth/api/users/route.ts +18 -5
  36. package/src/modules/auth/lib/userIdFilter.ts +31 -0
  37. package/src/modules/devices/AGENTS.md +12 -1
  38. package/src/modules/devices/acl.ts +9 -1
  39. package/src/modules/devices/backend/devices/[id]/page.tsx +5 -19
  40. package/src/modules/devices/backend/devices/create/page.tsx +17 -1
  41. package/src/modules/devices/backend/devices/page.tsx +20 -24
  42. package/src/modules/devices/backend/devices/useDeviceUserLabels.ts +45 -0
  43. package/src/modules/devices/backend/devices/userOptions.ts +99 -0
  44. package/src/modules/devices/i18n/de.json +3 -2
  45. package/src/modules/devices/i18n/en.json +3 -2
  46. package/src/modules/devices/i18n/es.json +3 -2
  47. package/src/modules/devices/i18n/ko.json +3 -2
  48. package/src/modules/devices/i18n/pl.json +3 -2
  49. package/src/modules/devices/setup.ts +6 -2
  50. package/src/modules/wms/api/warehouseSearch.ts +23 -0
  51. package/src/modules/wms/api/warehouses/route.ts +13 -9
  52. package/src/modules/wms/commands/configuration.ts +73 -7
  53. package/src/modules/wms/commands/shared.ts +9 -0
  54. package/src/modules/wms/components/backend/WarehouseEditDialog.tsx +216 -0
  55. package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +31 -139
  56. package/src/modules/wms/components/backend/warehouseFormOptions.ts +72 -0
  57. package/src/modules/wms/i18n/de.json +2 -0
  58. package/src/modules/wms/i18n/en.json +2 -0
  59. package/src/modules/wms/i18n/es.json +2 -0
  60. package/src/modules/wms/i18n/ko.json +2 -0
  61. package/src/modules/wms/i18n/pl.json +2 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/wms/commands/shared.ts"],
4
- "sourcesContent": ["import type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { ensureOrganizationScope } from '@open-mercato/shared/lib/commands/scope'\nimport type { CrudIndexerConfig, CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n InventoryBalance,\n InventoryMovement,\n InventoryReservation,\n WarehouseZone,\n} from '../data/entities'\n\nexport function ensureTenantScope(ctx: CommandRuntimeContext, tenantId: string): void {\n const currentTenant = ctx.auth?.tenantId ?? null\n if (currentTenant && currentTenant !== tenantId) {\n throw new CrudHttpError(403, { error: 'Forbidden' })\n }\n}\n\nexport function requireId(id: string | null | undefined, label: string): string {\n if (typeof id === 'string' && id.trim().length > 0) return id.trim()\n throw new CrudHttpError(400, { error: `${label} id is required.` })\n}\n\nexport function normalizeOptionalString(value: string | null | undefined): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\nexport function toNumericString(value: number | string | null | undefined, fallback = '0'): string {\n if (typeof value === 'string' && value.trim().length > 0) return value.trim()\n if (typeof value === 'number' && Number.isFinite(value)) return String(value)\n return fallback\n}\n\n// CRUD indexer configs \u2014 used by emitCrudSideEffects to drive query_index updates.\n// Cache invalidation cross-aliases (e.g. balance lists invalidated when a reservation mutates)\n// are declared in command-level metadata.context.cacheAliases so the command bus picks them up.\nexport const inventoryBalanceCrudIndexer: CrudIndexerConfig<InventoryBalance> = {\n entityType: E.wms.inventory_balance,\n}\n\nexport const inventoryReservationCrudIndexer: CrudIndexerConfig<InventoryReservation> = {\n entityType: E.wms.inventory_reservation,\n}\n\nexport const inventoryMovementCrudIndexer: CrudIndexerConfig<InventoryMovement> = {\n entityType: E.wms.inventory_movement,\n}\n\n// Zones are read through the hybrid query engine, which projects `cf:*` out of\n// `entity_indexes` rather than joining `custom_field_values`. Without this indexer the\n// zone commands write custom field values that no read path can ever see (#5239).\nexport const warehouseZoneCrudIndexer: CrudIndexerConfig<WarehouseZone> = {\n entityType: E.wms.warehouse_zone,\n}\n\nexport const inventoryBalanceCrudEvents: CrudEventsConfig<InventoryBalance> = {\n module: 'wms',\n entity: 'inventory_balance',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nexport const inventoryReservationCrudEvents: CrudEventsConfig<InventoryReservation> = {\n module: 'wms',\n entity: 'inventory_reservation',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nexport const inventoryMovementCrudEvents: CrudEventsConfig<InventoryMovement> = {\n module: 'wms',\n entity: 'inventory_movement',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\n// Cache alias resource kinds used to extend command-level cache invalidation\n// when a single mutation impacts multiple list resources.\nexport const WMS_INVENTORY_BALANCE_RESOURCE = 'wms.inventoryBalance'\nexport const WMS_INVENTORY_RESERVATION_RESOURCE = 'wms.inventoryReservation'\nexport const WMS_INVENTORY_MOVEMENT_RESOURCE = 'wms.inventoryMovement'\n\nexport { ensureOrganizationScope }\n"],
5
- "mappings": "AACA,SAAS,qBAAqB;AAC9B,SAAS,+BAA+B;AAExC,SAAS,SAAS;AAQX,SAAS,kBAAkB,KAA4B,UAAwB;AACpF,QAAM,gBAAgB,IAAI,MAAM,YAAY;AAC5C,MAAI,iBAAiB,kBAAkB,UAAU;AAC/C,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,YAAY,CAAC;AAAA,EACrD;AACF;AAEO,SAAS,UAAU,IAA+B,OAAuB;AAC9E,MAAI,OAAO,OAAO,YAAY,GAAG,KAAK,EAAE,SAAS,EAAG,QAAO,GAAG,KAAK;AACnE,QAAM,IAAI,cAAc,KAAK,EAAE,OAAO,GAAG,KAAK,mBAAmB,CAAC;AACpE;AAEO,SAAS,wBAAwB,OAAiD;AACvF,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAEO,SAAS,gBAAgB,OAA2C,WAAW,KAAa;AACjG,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,EAAG,QAAO,MAAM,KAAK;AAC5E,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO,OAAO,KAAK;AAC5E,SAAO;AACT;AAKO,MAAM,8BAAmE;AAAA,EAC9E,YAAY,EAAE,IAAI;AACpB;AAEO,MAAM,kCAA2E;AAAA,EACtF,YAAY,EAAE,IAAI;AACpB;AAEO,MAAM,+BAAqE;AAAA,EAChF,YAAY,EAAE,IAAI;AACpB;AAKO,MAAM,2BAA6D;AAAA,EACxE,YAAY,EAAE,IAAI;AACpB;AAEO,MAAM,6BAAiE;AAAA,EAC5E,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc,CAAC,SAAS;AAAA,IACtB,IAAI,IAAI,YAAY;AAAA,IACpB,gBAAgB,IAAI,YAAY;AAAA,IAChC,UAAU,IAAI,YAAY;AAAA,EAC5B;AACF;AAEO,MAAM,iCAAyE;AAAA,EACpF,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc,CAAC,SAAS;AAAA,IACtB,IAAI,IAAI,YAAY;AAAA,IACpB,gBAAgB,IAAI,YAAY;AAAA,IAChC,UAAU,IAAI,YAAY;AAAA,EAC5B;AACF;AAEO,MAAM,8BAAmE;AAAA,EAC9E,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc,CAAC,SAAS;AAAA,IACtB,IAAI,IAAI,YAAY;AAAA,IACpB,gBAAgB,IAAI,YAAY;AAAA,IAChC,UAAU,IAAI,YAAY;AAAA,EAC5B;AACF;AAIO,MAAM,iCAAiC;AACvC,MAAM,qCAAqC;AAC3C,MAAM,kCAAkC;",
4
+ "sourcesContent": ["import type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { ensureOrganizationScope } from '@open-mercato/shared/lib/commands/scope'\nimport type { CrudIndexerConfig, CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n InventoryBalance,\n InventoryMovement,\n InventoryReservation,\n Warehouse,\n WarehouseZone,\n} from '../data/entities'\n\nexport function ensureTenantScope(ctx: CommandRuntimeContext, tenantId: string): void {\n const currentTenant = ctx.auth?.tenantId ?? null\n if (currentTenant && currentTenant !== tenantId) {\n throw new CrudHttpError(403, { error: 'Forbidden' })\n }\n}\n\nexport function requireId(id: string | null | undefined, label: string): string {\n if (typeof id === 'string' && id.trim().length > 0) return id.trim()\n throw new CrudHttpError(400, { error: `${label} id is required.` })\n}\n\nexport function normalizeOptionalString(value: string | null | undefined): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\nexport function toNumericString(value: number | string | null | undefined, fallback = '0'): string {\n if (typeof value === 'string' && value.trim().length > 0) return value.trim()\n if (typeof value === 'number' && Number.isFinite(value)) return String(value)\n return fallback\n}\n\n// CRUD indexer configs \u2014 used by emitCrudSideEffects to drive query_index updates.\n// Cache invalidation cross-aliases (e.g. balance lists invalidated when a reservation mutates)\n// are declared in command-level metadata.context.cacheAliases so the command bus picks them up.\nexport const inventoryBalanceCrudIndexer: CrudIndexerConfig<InventoryBalance> = {\n entityType: E.wms.inventory_balance,\n}\n\nexport const inventoryReservationCrudIndexer: CrudIndexerConfig<InventoryReservation> = {\n entityType: E.wms.inventory_reservation,\n}\n\nexport const inventoryMovementCrudIndexer: CrudIndexerConfig<InventoryMovement> = {\n entityType: E.wms.inventory_movement,\n}\n\n// Zones are read through the hybrid query engine, which projects `cf:*` out of\n// `entity_indexes` rather than joining `custom_field_values`. Without this indexer the\n// zone commands write custom field values that no read path can ever see (#5239).\nexport const warehouseZoneCrudIndexer: CrudIndexerConfig<WarehouseZone> = {\n entityType: E.wms.warehouse_zone,\n}\n\n// Warehouse list/search goes through the hybrid query engine. Writing a custom\n// field upserts `entity_indexes`; delete must tombstone that row. A leftover\n// live index document makes `indexAnyRows` true and hides later warehouses that\n// were never indexed (empty combobox on the same integration shard).\nexport const warehouseCrudIndexer: CrudIndexerConfig<Warehouse> = {\n entityType: E.wms.warehouse,\n}\n\nexport const inventoryBalanceCrudEvents: CrudEventsConfig<InventoryBalance> = {\n module: 'wms',\n entity: 'inventory_balance',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nexport const inventoryReservationCrudEvents: CrudEventsConfig<InventoryReservation> = {\n module: 'wms',\n entity: 'inventory_reservation',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nexport const inventoryMovementCrudEvents: CrudEventsConfig<InventoryMovement> = {\n module: 'wms',\n entity: 'inventory_movement',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\n// Cache alias resource kinds used to extend command-level cache invalidation\n// when a single mutation impacts multiple list resources.\nexport const WMS_INVENTORY_BALANCE_RESOURCE = 'wms.inventoryBalance'\nexport const WMS_INVENTORY_RESERVATION_RESOURCE = 'wms.inventoryReservation'\nexport const WMS_INVENTORY_MOVEMENT_RESOURCE = 'wms.inventoryMovement'\n\nexport { ensureOrganizationScope }\n"],
5
+ "mappings": "AACA,SAAS,qBAAqB;AAC9B,SAAS,+BAA+B;AAExC,SAAS,SAAS;AASX,SAAS,kBAAkB,KAA4B,UAAwB;AACpF,QAAM,gBAAgB,IAAI,MAAM,YAAY;AAC5C,MAAI,iBAAiB,kBAAkB,UAAU;AAC/C,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,YAAY,CAAC;AAAA,EACrD;AACF;AAEO,SAAS,UAAU,IAA+B,OAAuB;AAC9E,MAAI,OAAO,OAAO,YAAY,GAAG,KAAK,EAAE,SAAS,EAAG,QAAO,GAAG,KAAK;AACnE,QAAM,IAAI,cAAc,KAAK,EAAE,OAAO,GAAG,KAAK,mBAAmB,CAAC;AACpE;AAEO,SAAS,wBAAwB,OAAiD;AACvF,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAEO,SAAS,gBAAgB,OAA2C,WAAW,KAAa;AACjG,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,EAAG,QAAO,MAAM,KAAK;AAC5E,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO,OAAO,KAAK;AAC5E,SAAO;AACT;AAKO,MAAM,8BAAmE;AAAA,EAC9E,YAAY,EAAE,IAAI;AACpB;AAEO,MAAM,kCAA2E;AAAA,EACtF,YAAY,EAAE,IAAI;AACpB;AAEO,MAAM,+BAAqE;AAAA,EAChF,YAAY,EAAE,IAAI;AACpB;AAKO,MAAM,2BAA6D;AAAA,EACxE,YAAY,EAAE,IAAI;AACpB;AAMO,MAAM,uBAAqD;AAAA,EAChE,YAAY,EAAE,IAAI;AACpB;AAEO,MAAM,6BAAiE;AAAA,EAC5E,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc,CAAC,SAAS;AAAA,IACtB,IAAI,IAAI,YAAY;AAAA,IACpB,gBAAgB,IAAI,YAAY;AAAA,IAChC,UAAU,IAAI,YAAY;AAAA,EAC5B;AACF;AAEO,MAAM,iCAAyE;AAAA,EACpF,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc,CAAC,SAAS;AAAA,IACtB,IAAI,IAAI,YAAY;AAAA,IACpB,gBAAgB,IAAI,YAAY;AAAA,IAChC,UAAU,IAAI,YAAY;AAAA,EAC5B;AACF;AAEO,MAAM,8BAAmE;AAAA,EAC9E,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc,CAAC,SAAS;AAAA,IACtB,IAAI,IAAI,YAAY;AAAA,IACpB,gBAAgB,IAAI,YAAY;AAAA,IAChC,UAAU,IAAI,YAAY;AAAA,EAC5B;AACF;AAIO,MAAM,iCAAiC;AACvC,MAAM,qCAAqC;AAC3C,MAAM,kCAAkC;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,158 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { z } from "zod";
5
+ import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
6
+ import { flash } from "@open-mercato/ui/backend/FlashMessages";
7
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
8
+ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
9
+ import { raiseCrudError } from "@open-mercato/ui/backend/utils/serverErrors";
10
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@open-mercato/ui/primitives/dialog";
11
+ import { extractCustomFieldEntries } from "@open-mercato/shared/lib/crud/custom-fields-client";
12
+ import { useLocale, useT } from "@open-mercato/shared/lib/i18n/context";
13
+ import { E } from "../../../../generated/entities.ids.generated.js";
14
+ import { flashMutationError } from "../../lib/flashMutationError.js";
15
+ import {
16
+ countryOptionFromStored,
17
+ loadCountryOptions,
18
+ loadTimezoneOptions,
19
+ timezoneOptionFromStored
20
+ } from "./warehouseFormOptions.js";
21
+ const warehouseFormSchema = z.object({
22
+ name: z.string().trim().min(1),
23
+ code: z.string().trim().min(1),
24
+ city: z.string().trim().optional(),
25
+ country: z.string().trim().optional(),
26
+ timezone: z.string().trim().optional(),
27
+ isActive: z.boolean().default(true),
28
+ isPrimary: z.boolean().default(false)
29
+ }).passthrough();
30
+ function WarehouseEditDialog({ open, onOpenChange, mode, row, onSaved }) {
31
+ const t = useT();
32
+ const locale = useLocale();
33
+ const { runMutation } = useGuardedMutation({ contextId: "wms-config-warehouses" });
34
+ const [submitting, setSubmitting] = React.useState(false);
35
+ const countrySeedOptions = React.useMemo(() => {
36
+ if (mode !== "edit" || !row?.country?.trim()) return void 0;
37
+ return [countryOptionFromStored(row.country, locale)];
38
+ }, [locale, mode, row]);
39
+ const timezoneSeedOptions = React.useMemo(() => {
40
+ if (mode !== "edit" || !row?.timezone?.trim()) return void 0;
41
+ return [timezoneOptionFromStored(row.timezone)];
42
+ }, [mode, row]);
43
+ const fields = React.useMemo(() => [
44
+ { id: "name", type: "text", label: t("wms.backend.config.warehouses.form.name", "Name"), required: true },
45
+ { id: "code", type: "text", label: t("wms.backend.config.warehouses.form.code", "Code"), required: true },
46
+ { id: "city", type: "text", label: t("wms.backend.config.warehouses.form.city", "City") },
47
+ {
48
+ id: "country",
49
+ type: "combobox",
50
+ label: t("wms.backend.config.warehouses.form.country", "Country"),
51
+ placeholder: t("wms.backend.config.warehouses.form.countryPlaceholder", "Search country"),
52
+ loadOptions: (query) => loadCountryOptions(query, locale),
53
+ allowCustomValues: false,
54
+ seedOptions: countrySeedOptions,
55
+ resolveLabel: (value) => countryOptionFromStored(value, locale).label
56
+ },
57
+ {
58
+ id: "timezone",
59
+ type: "combobox",
60
+ label: t("wms.backend.config.warehouses.form.timezone", "Timezone"),
61
+ placeholder: t("wms.backend.config.warehouses.form.timezonePlaceholder", "Search timezone"),
62
+ loadOptions: loadTimezoneOptions,
63
+ allowCustomValues: false,
64
+ seedOptions: timezoneSeedOptions
65
+ },
66
+ { id: "isPrimary", type: "checkbox", label: t("wms.backend.config.warehouses.form.primary", "Primary warehouse") },
67
+ { id: "isActive", type: "checkbox", label: t("wms.backend.config.warehouses.form.active", "Active") }
68
+ ], [countrySeedOptions, locale, t, timezoneSeedOptions]);
69
+ const initialValues = React.useMemo(() => {
70
+ if (mode === "edit" && row) {
71
+ return {
72
+ name: row.name || "",
73
+ code: row.code || "",
74
+ city: row.city || "",
75
+ country: row.country || "",
76
+ timezone: row.timezone || "",
77
+ isActive: row.is_active !== false,
78
+ isPrimary: row.is_primary === true,
79
+ ...extractCustomFieldEntries(row)
80
+ };
81
+ }
82
+ return {
83
+ name: "",
84
+ code: "",
85
+ city: "",
86
+ country: "",
87
+ timezone: "",
88
+ isActive: true,
89
+ isPrimary: false
90
+ };
91
+ }, [mode, row]);
92
+ const handleSubmit = React.useCallback(async (values) => {
93
+ setSubmitting(true);
94
+ try {
95
+ const payload = mode === "edit" && row ? { id: row.id, ...values } : values;
96
+ const call = await runMutation({
97
+ operation: async () => {
98
+ const result = await apiCall(
99
+ "/api/wms/warehouses",
100
+ {
101
+ method: mode === "edit" ? "PUT" : "POST",
102
+ headers: { "x-om-forbidden-redirect": "0" },
103
+ body: JSON.stringify(payload)
104
+ }
105
+ );
106
+ if (!result.ok) {
107
+ await raiseCrudError(result.response, t("wms.backend.config.warehouses.errors.save", "Failed to save warehouse."));
108
+ }
109
+ return result;
110
+ },
111
+ context: {},
112
+ mutationPayload: payload
113
+ });
114
+ flash(
115
+ mode === "edit" ? t("wms.backend.config.warehouses.flash.updated", "Warehouse updated") : t("wms.backend.config.warehouses.flash.created", "Warehouse created"),
116
+ "success"
117
+ );
118
+ const resultBody = call?.result && typeof call.result === "object" ? call.result : null;
119
+ const createdId = mode === "create" && typeof resultBody?.id === "string" ? resultBody.id.trim() : void 0;
120
+ const updatedAt = typeof resultBody?.updatedAt === "string" && resultBody.updatedAt.trim() || typeof resultBody?.updated_at === "string" && resultBody.updated_at.trim() || void 0;
121
+ onOpenChange(false);
122
+ await onSaved?.({
123
+ mode,
124
+ id: createdId || row?.id,
125
+ values,
126
+ updatedAt: updatedAt || null
127
+ });
128
+ } catch (error) {
129
+ flashMutationError(error, t("wms.backend.config.warehouses.errors.save", "Failed to save warehouse."), t);
130
+ } finally {
131
+ setSubmitting(false);
132
+ }
133
+ }, [mode, onOpenChange, onSaved, row, runMutation, t]);
134
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-3xl", children: [
135
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: mode === "edit" ? t("wms.backend.config.warehouses.dialog.edit", "Edit warehouse") : t("wms.backend.config.warehouses.dialog.create", "Create warehouse") }) }),
136
+ /* @__PURE__ */ jsx(
137
+ CrudForm,
138
+ {
139
+ schema: warehouseFormSchema,
140
+ fields,
141
+ entityId: E.wms.warehouse,
142
+ initialValues,
143
+ submitLabel: t("common.save", "Save"),
144
+ onSubmit: handleSubmit,
145
+ embedded: true,
146
+ disableInitialFocus: true,
147
+ isLoading: submitting,
148
+ twoColumn: true,
149
+ optimisticLockUpdatedAt: mode === "edit" ? row?.updatedAt ?? row?.updated_at ?? null : void 0
150
+ }
151
+ )
152
+ ] }) });
153
+ }
154
+ export {
155
+ WarehouseEditDialog,
156
+ warehouseFormSchema
157
+ };
158
+ //# sourceMappingURL=WarehouseEditDialog.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/wms/components/backend/WarehouseEditDialog.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { E } from '#generated/entities.ids.generated'\nimport { flashMutationError } from '../../lib/flashMutationError'\nimport {\n countryOptionFromStored,\n loadCountryOptions,\n loadTimezoneOptions,\n timezoneOptionFromStored,\n} from './warehouseFormOptions'\n\nexport type WarehouseDialogRow = {\n id: string\n name?: string | null\n code?: string | null\n city?: string | null\n country?: string | null\n timezone?: string | null\n is_active?: boolean | null\n is_primary?: boolean | null\n updated_at?: string | null\n updatedAt?: string | null\n customValues?: Record<string, unknown> | null\n}\n\nexport type WarehouseFormValues = {\n name: string\n code: string\n city?: string\n country?: string\n timezone?: string\n isActive: boolean\n isPrimary: boolean\n}\n\nexport const warehouseFormSchema = z.object({\n name: z.string().trim().min(1),\n code: z.string().trim().min(1),\n city: z.string().trim().optional(),\n country: z.string().trim().optional(),\n timezone: z.string().trim().optional(),\n isActive: z.boolean().default(true),\n isPrimary: z.boolean().default(false),\n}).passthrough()\n\ntype WarehouseEditDialogProps = {\n open: boolean\n onOpenChange: (open: boolean) => void\n mode: 'create' | 'edit'\n row?: WarehouseDialogRow | null\n onSaved?: (info: {\n mode: 'create' | 'edit'\n id?: string\n values: WarehouseFormValues\n updatedAt?: string | null\n }) => void | Promise<void>\n}\n\nexport function WarehouseEditDialog({ open, onOpenChange, mode, row, onSaved }: WarehouseEditDialogProps) {\n const t = useT()\n const locale = useLocale()\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({ contextId: 'wms-config-warehouses' })\n const [submitting, setSubmitting] = React.useState(false)\n\n const countrySeedOptions = React.useMemo(() => {\n if (mode !== 'edit' || !row?.country?.trim()) return undefined\n return [countryOptionFromStored(row.country, locale)]\n }, [locale, mode, row])\n\n const timezoneSeedOptions = React.useMemo(() => {\n if (mode !== 'edit' || !row?.timezone?.trim()) return undefined\n return [timezoneOptionFromStored(row.timezone)]\n }, [mode, row])\n\n const fields = React.useMemo<CrudField[]>(() => [\n { id: 'name', type: 'text', label: t('wms.backend.config.warehouses.form.name', 'Name'), required: true },\n { id: 'code', type: 'text', label: t('wms.backend.config.warehouses.form.code', 'Code'), required: true },\n { id: 'city', type: 'text', label: t('wms.backend.config.warehouses.form.city', 'City') },\n {\n id: 'country',\n type: 'combobox',\n label: t('wms.backend.config.warehouses.form.country', 'Country'),\n placeholder: t('wms.backend.config.warehouses.form.countryPlaceholder', 'Search country'),\n loadOptions: (query) => loadCountryOptions(query, locale),\n allowCustomValues: false,\n seedOptions: countrySeedOptions,\n resolveLabel: (value) => countryOptionFromStored(value, locale).label,\n },\n {\n id: 'timezone',\n type: 'combobox',\n label: t('wms.backend.config.warehouses.form.timezone', 'Timezone'),\n placeholder: t('wms.backend.config.warehouses.form.timezonePlaceholder', 'Search timezone'),\n loadOptions: loadTimezoneOptions,\n allowCustomValues: false,\n seedOptions: timezoneSeedOptions,\n },\n { id: 'isPrimary', type: 'checkbox', label: t('wms.backend.config.warehouses.form.primary', 'Primary warehouse') },\n { id: 'isActive', type: 'checkbox', label: t('wms.backend.config.warehouses.form.active', 'Active') },\n ], [countrySeedOptions, locale, t, timezoneSeedOptions])\n\n const initialValues = React.useMemo<WarehouseFormValues>(() => {\n if (mode === 'edit' && row) {\n return {\n name: row.name || '',\n code: row.code || '',\n city: row.city || '',\n country: row.country || '',\n timezone: row.timezone || '',\n isActive: row.is_active !== false,\n isPrimary: row.is_primary === true,\n ...extractCustomFieldEntries(row as Record<string, unknown>),\n } as WarehouseFormValues\n }\n return {\n name: '',\n code: '',\n city: '',\n country: '',\n timezone: '',\n isActive: true,\n isPrimary: false,\n }\n }, [mode, row])\n\n const handleSubmit = React.useCallback(async (values: WarehouseFormValues) => {\n setSubmitting(true)\n try {\n const payload = mode === 'edit' && row ? { id: row.id, ...values } : values\n const call = await runMutation({\n operation: async () => {\n const result = await apiCall<{ id?: string | null; updatedAt?: string | null; updated_at?: string | null }>(\n '/api/wms/warehouses',\n {\n method: mode === 'edit' ? 'PUT' : 'POST',\n headers: { 'x-om-forbidden-redirect': '0' },\n body: JSON.stringify(payload),\n },\n )\n if (!result.ok) {\n await raiseCrudError(result.response, t('wms.backend.config.warehouses.errors.save', 'Failed to save warehouse.'))\n }\n return result\n },\n context: {},\n mutationPayload: payload,\n })\n flash(\n mode === 'edit'\n ? t('wms.backend.config.warehouses.flash.updated', 'Warehouse updated')\n : t('wms.backend.config.warehouses.flash.created', 'Warehouse created'),\n 'success',\n )\n const resultBody =\n call?.result && typeof call.result === 'object'\n ? (call.result as { id?: unknown; updatedAt?: unknown; updated_at?: unknown })\n : null\n const createdId =\n mode === 'create' && typeof resultBody?.id === 'string'\n ? resultBody.id.trim()\n : undefined\n const updatedAt =\n (typeof resultBody?.updatedAt === 'string' && resultBody.updatedAt.trim()) ||\n (typeof resultBody?.updated_at === 'string' && resultBody.updated_at.trim()) ||\n undefined\n onOpenChange(false)\n await onSaved?.({\n mode,\n id: createdId || row?.id,\n values,\n updatedAt: updatedAt || null,\n })\n } catch (error) {\n flashMutationError(error, t('wms.backend.config.warehouses.errors.save', 'Failed to save warehouse.'), t)\n } finally {\n setSubmitting(false)\n }\n }, [mode, onOpenChange, onSaved, row, runMutation, t])\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-3xl\">\n <DialogHeader>\n <DialogTitle>\n {mode === 'edit'\n ? t('wms.backend.config.warehouses.dialog.edit', 'Edit warehouse')\n : t('wms.backend.config.warehouses.dialog.create', 'Create warehouse')}\n </DialogTitle>\n </DialogHeader>\n <CrudForm<WarehouseFormValues>\n schema={warehouseFormSchema}\n fields={fields}\n entityId={E.wms.warehouse}\n initialValues={initialValues}\n submitLabel={t('common.save', 'Save')}\n onSubmit={handleSubmit}\n embedded\n disableInitialFocus\n isLoading={submitting}\n twoColumn\n optimisticLockUpdatedAt={mode === 'edit' ? (row?.updatedAt ?? row?.updated_at ?? null) : undefined}\n />\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -36,17 +36,10 @@ import {
36
36
  inventoryRotationStrategyLabel
37
37
  } from "../../lib/inventoryDisplayUi.js";
38
38
  import { LocationEditDialog } from "./LocationEditDialog.js";
39
+ import { WarehouseEditDialog } from "./WarehouseEditDialog.js";
40
+ import { formatWarehouseCountryLabel } from "./warehouseFormOptions.js";
39
41
  import { useWmsInventoryMutationAccess } from "./useWmsInventoryMutationAccess.js";
40
42
  import { flashMutationError } from "../../lib/flashMutationError.js";
41
- const warehouseFormSchema = z.object({
42
- name: z.string().trim().min(1),
43
- code: z.string().trim().min(1),
44
- city: z.string().trim().optional(),
45
- country: z.string().trim().optional(),
46
- timezone: z.string().trim().optional(),
47
- isActive: z.boolean().default(true),
48
- isPrimary: z.boolean().default(false)
49
- });
50
43
  const zoneFormSchema = z.object({
51
44
  warehouseId: z.string().uuid(),
52
45
  code: z.string().trim().min(1).max(80),
@@ -79,7 +72,7 @@ const STRATEGY_OPTIONS = [
79
72
  { value: "lifo", label: "LIFO" },
80
73
  { value: "fefo", label: "FEFO" }
81
74
  ];
82
- function mergeCreatedWarehouseIntoWarehousesCaches(queryClient, newId, values) {
75
+ function mergeCreatedWarehouseIntoWarehousesCaches(queryClient, newId, values, updatedAt) {
83
76
  const row = {
84
77
  id: newId,
85
78
  name: values.name,
@@ -88,7 +81,9 @@ function mergeCreatedWarehouseIntoWarehousesCaches(queryClient, newId, values) {
88
81
  country: values.country || null,
89
82
  timezone: values.timezone || null,
90
83
  is_active: values.isActive,
91
- is_primary: values.isPrimary
84
+ is_primary: values.isPrimary,
85
+ updated_at: updatedAt ?? null,
86
+ updatedAt: updatedAt ?? null
92
87
  };
93
88
  const haystack = `${values.name}
94
89
  ${values.code}`.toLowerCase();
@@ -148,6 +143,7 @@ function SectionCard({
148
143
  }
149
144
  function WarehouseSection({ viewAllHref } = {}) {
150
145
  const t = useT();
146
+ const locale = useLocale();
151
147
  const queryClient = useQueryClient();
152
148
  const { confirm, ConfirmDialogElement } = useConfirmDialog();
153
149
  const { runMutation } = useGuardedMutation({ contextId: "wms-config-warehouses" });
@@ -155,7 +151,6 @@ function WarehouseSection({ viewAllHref } = {}) {
155
151
  const [page, setPage] = React.useState(1);
156
152
  const [search, setSearch] = React.useState("");
157
153
  const [sorting, setSorting] = React.useState([{ id: "updatedAt", desc: true }]);
158
- const [submitting, setSubmitting] = React.useState(false);
159
154
  const [dialog, setDialog] = React.useState(null);
160
155
  const handleSortingChange = React.useCallback((nextSorting) => {
161
156
  setSorting(nextSorting);
@@ -183,15 +178,6 @@ function WarehouseSection({ viewAllHref } = {}) {
183
178
  return call.result ?? { items: [], total: 0, totalPages: 1 };
184
179
  }
185
180
  });
186
- const fields = React.useMemo(() => [
187
- { id: "name", type: "text", label: t("wms.backend.config.warehouses.form.name", "Name"), required: true },
188
- { id: "code", type: "text", label: t("wms.backend.config.warehouses.form.code", "Code"), required: true },
189
- { id: "city", type: "text", label: t("wms.backend.config.warehouses.form.city", "City") },
190
- { id: "country", type: "text", label: t("wms.backend.config.warehouses.form.country", "Country") },
191
- { id: "timezone", type: "text", label: t("wms.backend.config.warehouses.form.timezone", "Timezone") },
192
- { id: "isPrimary", type: "checkbox", label: t("wms.backend.config.warehouses.form.primary", "Primary warehouse") },
193
- { id: "isActive", type: "checkbox", label: t("wms.backend.config.warehouses.form.active", "Active") }
194
- ], [t]);
195
181
  const columns = React.useMemo(() => [
196
182
  {
197
183
  accessorKey: "name",
@@ -201,7 +187,7 @@ function WarehouseSection({ viewAllHref } = {}) {
201
187
  },
202
188
  { accessorKey: "code", header: t("wms.backend.config.warehouses.columns.code", "Code"), enableSorting: true },
203
189
  { accessorKey: "city", header: t("wms.backend.config.warehouses.columns.city", "City"), cell: ({ row }) => row.original.city || "\u2014" },
204
- { accessorKey: "country", header: t("wms.backend.config.warehouses.columns.country", "Country"), cell: ({ row }) => row.original.country || "\u2014" },
190
+ { accessorKey: "country", header: t("wms.backend.config.warehouses.columns.country", "Country"), cell: ({ row }) => formatWarehouseCountryLabel(row.original.country, locale) },
205
191
  {
206
192
  accessorKey: "is_primary",
207
193
  header: t("wms.backend.config.warehouses.columns.primary", "Primary"),
@@ -212,79 +198,24 @@ function WarehouseSection({ viewAllHref } = {}) {
212
198
  header: t("wms.backend.config.warehouses.columns.status", "Status"),
213
199
  cell: ({ row }) => row.original.is_active === false ? t("wms.common.inactive", "Inactive") : t("wms.common.active", "Active")
214
200
  }
215
- ], [t]);
216
- const initialValues = React.useMemo(() => {
217
- if (dialog?.mode === "edit") {
218
- return {
219
- name: dialog.row.name || "",
220
- code: dialog.row.code || "",
221
- city: dialog.row.city || "",
222
- country: dialog.row.country || "",
223
- timezone: dialog.row.timezone || "",
224
- isActive: dialog.row.is_active !== false,
225
- isPrimary: dialog.row.is_primary === true
226
- };
227
- }
228
- return {
229
- name: "",
230
- code: "",
231
- city: "",
232
- country: "",
233
- timezone: "",
234
- isActive: true,
235
- isPrimary: false
236
- };
237
- }, [dialog]);
201
+ ], [locale, t]);
238
202
  const closeDialog = React.useCallback(() => {
239
203
  setDialog(null);
240
- setSubmitting(false);
241
204
  }, []);
242
205
  const refresh = React.useCallback(async () => {
243
206
  await queryClient.cancelQueries({ queryKey: ["wms-config", "warehouses"] });
244
207
  await queryClient.invalidateQueries({ queryKey: ["wms-config", "warehouses"] });
245
208
  await queryClient.refetchQueries({ queryKey: ["wms-config", "warehouses"], type: "all" });
246
209
  }, [queryClient]);
247
- const handleSubmit = React.useCallback(async (values) => {
248
- if (!dialog) return;
249
- const submitMode = dialog.mode;
250
- setSubmitting(true);
251
- try {
252
- const call = await runMutation({
253
- operation: async () => {
254
- const result = await apiCall(
255
- "/api/wms/warehouses",
256
- {
257
- method: submitMode === "edit" ? "PUT" : "POST",
258
- body: JSON.stringify(
259
- submitMode === "edit" ? { id: dialog.row.id, ...values } : values
260
- )
261
- }
262
- );
263
- if (!result.ok) {
264
- await raiseCrudError(result.response, t("wms.backend.config.warehouses.errors.save", "Failed to save warehouse."));
265
- }
266
- return result;
267
- },
268
- context: {},
269
- mutationPayload: submitMode === "edit" ? { id: dialog.row.id, ...values } : values
270
- });
271
- flash(
272
- submitMode === "edit" ? t("wms.backend.config.warehouses.flash.updated", "Warehouse updated") : t("wms.backend.config.warehouses.flash.created", "Warehouse created"),
273
- "success"
274
- );
275
- const createdId = submitMode === "create" && call?.result && typeof call.result === "object" && typeof call.result.id === "string" ? call.result.id.trim() : null;
276
- closeDialog();
277
- if (submitMode === "create" && createdId) {
278
- mergeCreatedWarehouseIntoWarehousesCaches(queryClient, createdId, values);
279
- setSearch("");
280
- setPage(1);
281
- }
282
- await refresh();
283
- } catch (error) {
284
- flashMutationError(error, t("wms.backend.config.warehouses.errors.save", "Failed to save warehouse."), t);
285
- setSubmitting(false);
210
+ const handleWarehouseSaved = React.useCallback(async (info) => {
211
+ closeDialog();
212
+ if (info.mode === "create" && info.id) {
213
+ mergeCreatedWarehouseIntoWarehousesCaches(queryClient, info.id, info.values, info.updatedAt);
214
+ setSearch("");
215
+ setPage(1);
286
216
  }
287
- }, [closeDialog, dialog, queryClient, refresh, runMutation, t]);
217
+ await refresh();
218
+ }, [closeDialog, queryClient, refresh]);
288
219
  const handleDelete = React.useCallback(async (row) => {
289
220
  const confirmed = await confirm({
290
221
  title: t("wms.backend.config.warehouses.confirmDelete", 'Archive warehouse "{name}"?', {
@@ -373,23 +304,18 @@ function WarehouseSection({ viewAllHref } = {}) {
373
304
  )
374
305
  }
375
306
  ),
376
- /* @__PURE__ */ jsx(Dialog, { open: dialog !== null, onOpenChange: (next) => !next && closeDialog(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-3xl", children: [
377
- /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: dialog?.mode === "edit" ? t("wms.backend.config.warehouses.dialog.edit", "Edit warehouse") : t("wms.backend.config.warehouses.dialog.create", "Create warehouse") }) }),
378
- /* @__PURE__ */ jsx(
379
- CrudForm,
380
- {
381
- schema: warehouseFormSchema,
382
- fields,
383
- entityId: E.wms.warehouse,
384
- initialValues,
385
- submitLabel: t("common.save", "Save"),
386
- onSubmit: handleSubmit,
387
- embedded: true,
388
- isLoading: submitting,
389
- twoColumn: true
390
- }
391
- )
392
- ] }) }),
307
+ /* @__PURE__ */ jsx(
308
+ WarehouseEditDialog,
309
+ {
310
+ open: dialog !== null,
311
+ onOpenChange: (next) => {
312
+ if (!next) closeDialog();
313
+ },
314
+ mode: dialog?.mode === "edit" ? "edit" : "create",
315
+ row: dialog?.mode === "edit" ? dialog.row : null,
316
+ onSaved: handleWarehouseSaved
317
+ }
318
+ ),
393
319
  ConfirmDialogElement
394
320
  ] });
395
321
  }