@open-mercato/core 0.10.1-develop.7870.1.63e4c252d3 → 0.10.1-develop.7879.1.80d3740285

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.
@@ -1,4 +1,4 @@
1
- [build:core] found 6249 entry points
1
+ [build:core] found 6250 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 271 entry points
4
4
  [build:core:generated] built successfully
@@ -16,18 +16,16 @@ import { applyCustomFieldVisibility } from "@open-mercato/ui/backend/utils/custo
16
16
  import { BooleanIcon } from "@open-mercato/ui/backend/ValueIcons";
17
17
  import { markdownToPlainText } from "@open-mercato/ui/backend/markdown/markdownToPlainText";
18
18
  import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
19
- import { useT } from "@open-mercato/shared/lib/i18n/context";
19
+ import { useT, useLocale } from "@open-mercato/shared/lib/i18n/context";
20
+ import { formatDisplayDateTime } from "@open-mercato/ui/primitives/date-format";
20
21
  import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
21
22
  import { useAppEvent } from "@open-mercato/ui/backend/injection/useAppEvent";
22
23
  import { E } from "../../../../generated/entities.ids.generated.js";
23
24
  import { ProductImageCell } from "./ProductImageCell.js";
24
25
  const PAGE_SIZE = 25;
25
26
  const ENTITY_ID = E.catalog.catalog_product;
26
- function formatDate(value) {
27
- if (!value) return "\u2014";
28
- const date = new Date(value);
29
- if (Number.isNaN(date.getTime())) return "\u2014";
30
- return date.toLocaleDateString();
27
+ function formatDate(value, locale) {
28
+ return formatDisplayDateTime(value, locale) ?? "\u2014";
31
29
  }
32
30
  function renderOffers(offers) {
33
31
  if (!offers || offers.length === 0) return /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "\u2014" });
@@ -68,6 +66,7 @@ function ProductsDataTable({
68
66
  onSnapshotChange
69
67
  } = {}) {
70
68
  const t = useT();
69
+ const dateLocale = useLocale();
71
70
  const { confirm, ConfirmDialogElement } = useConfirmDialog();
72
71
  const scopeVersion = useOrganizationScopeVersion();
73
72
  const [rows, setRows] = React.useState([]);
@@ -328,11 +327,11 @@ function ProductsDataTable({
328
327
  {
329
328
  accessorKey: "updated_at",
330
329
  header: t("catalog.products.table.updatedAt"),
331
- cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(row.original.updated_at) })
330
+ cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(row.original.updated_at, dateLocale) })
332
331
  }
333
332
  ];
334
333
  return applyCustomFieldVisibility(base, customFieldDefs);
335
- }, [customFieldDefs, productTypeLabelMap, t]);
334
+ }, [customFieldDefs, productTypeLabelMap, t, dateLocale]);
336
335
  const handleSearchChange = React.useCallback((value) => {
337
336
  setSearch(value);
338
337
  setPage(1);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/catalog/components/products/ProductsDataTable.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/catalog/extension-points'\nimport Link from 'next/link'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport type { SortingState } from '@tanstack/react-table'\nimport { DataTable, type DataTableExportFormat } from '@open-mercato/ui/backend/DataTable'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { deleteCrud, buildCrudExportUrl } from '@open-mercato/ui/backend/utils/crud'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { applyCustomFieldVisibility } from '@open-mercato/ui/backend/utils/customFieldColumns'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport type { FilterOption } from '@open-mercato/ui/backend/FilterOverlay'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { markdownToPlainText } from '@open-mercato/ui/backend/markdown/markdownToPlainText'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { E } from '#generated/entities.ids.generated'\nimport { ProductImageCell } from './ProductImageCell'\n\ntype PricingScope = {\n variant_id?: string | null\n offer_id?: string | null\n channel_id?: string | null\n user_id?: string | null\n user_group_id?: string | null\n customer_id?: string | null\n customer_group_id?: string | null\n}\n\ntype PricingInfo = {\n kind?: string | null\n price_kind_id?: string | null\n price_kind_code?: string | null\n currency_code?: string | null\n unit_price_net?: string | null\n unit_price_gross?: string | null\n min_quantity?: number | null\n max_quantity?: number | null\n tax_rate?: string | null\n scope?: PricingScope | null\n} | null\n\ntype OfferInfo = {\n id: string\n channelId: string\n channelName?: string | null\n channelCode?: string | null\n title: string\n description?: string | null\n isActive: boolean\n}\n\nexport type ProductRow = {\n id: string\n title: string\n subtitle?: string | null\n description?: string | null\n sku?: string | null\n handle?: string | null\n product_type?: string | null\n status_entry_id?: string | null\n primary_currency_code?: string | null\n default_unit?: string | null\n default_media_id?: string | null\n default_media_url?: string | null\n is_configurable?: boolean\n is_active?: boolean\n metadata?: Record<string, unknown> | null\n custom_fieldset_code?: string | null\n created_at?: string\n updated_at?: string\n offers?: OfferInfo[]\n pricing?: PricingInfo\n} & Record<string, unknown>\n\ntype ProductsResponse = {\n items?: ProductRow[]\n total?: number\n totalPages?: number\n totalIsCapped?: boolean\n}\n\nconst PAGE_SIZE = 25\nconst ENTITY_ID = E.catalog.catalog_product\n\nfunction formatDate(value?: string): string {\n if (!value) return '\u2014'\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return '\u2014'\n return date.toLocaleDateString()\n}\n\nfunction renderOffers(offers: OfferInfo[] | undefined): React.ReactNode {\n if (!offers || offers.length === 0) return <span className=\"text-xs text-muted-foreground\">\u2014</span>\n const visible = offers.slice(0, 3)\n return (\n <div className=\"flex flex-wrap gap-1\">\n {visible.map((offer) => {\n const label =\n typeof offer.channelName === 'string' && offer.channelName.trim().length\n ? offer.channelName.trim()\n : typeof offer.title === 'string' && offer.title.trim().length\n ? offer.title.trim()\n : offer.channelId\n const badgeTitle =\n typeof offer.channelCode === 'string' && offer.channelCode.trim().length ? offer.channelCode : undefined\n return (\n <span\n key={offer.id}\n className={`inline-flex items-center rounded-full border px-2 py-0.5 text-xs ${\n offer.isActive ? 'bg-secondary/80 text-secondary-foreground' : 'bg-muted text-muted-foreground'\n }`}\n title={badgeTitle}\n >\n {label}\n </span>\n )\n })}\n {offers.length > visible.length ? (\n <span className=\"text-xs text-muted-foreground\">+{offers.length - visible.length}</span>\n ) : null}\n </div>\n )\n}\n\nfunction renderPrice(pricing: PricingInfo | undefined, currency?: string | null, fallback = '\u2014'): React.ReactNode {\n if (!pricing) return <span className=\"text-xs text-muted-foreground\">{fallback}</span>\n const unit = pricing.unit_price_net ?? pricing.unit_price_gross\n if (unit == null) return <span className=\"text-xs text-muted-foreground\">{fallback}</span>\n const formatted = `${currency ?? pricing.currency_code ?? ''} ${unit}`\n const kind = pricing.kind ?? 'list'\n return (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{formatted.trim()}</span>\n <span className=\"text-xs text-muted-foreground\">{kind}</span>\n </div>\n )\n}\n\nexport type ProductsDataTableSnapshot = {\n search: string\n filterValues: FilterValues\n total: number\n}\n\nexport type ProductsDataTableProps = {\n /**\n * Extra actions rendered alongside the built-in Create button in the\n * DataTable header. Used by the Step 4.9 AI merchandising sheet\n * trigger without coupling DataTable to the AI module.\n */\n extraActions?: React.ReactNode\n /**\n * Optional callback invoked whenever the table's search / filter /\n * total-matching snapshot changes. Used by the Step 4.9 AI merchandising\n * sheet to form a selection-aware pageContext per spec \u00A710.1.\n */\n onSnapshotChange?: (snapshot: ProductsDataTableSnapshot) => void\n}\n\nexport default function ProductsDataTable({\n extraActions,\n onSnapshotChange,\n}: ProductsDataTableProps = {}) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const scopeVersion = useOrganizationScopeVersion()\n const [rows, setRows] = React.useState<ProductRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [totalIsCapped, setTotalIsCapped] = React.useState(false)\n const [cacheStatus, setCacheStatus] = React.useState<'hit' | 'miss' | null>(null)\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'title', desc: false }])\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(false)\n const [reloadToken, setReloadToken] = React.useState(0)\n // Step 5.18 (spec \u00A710 line 836, D18 demo): refresh the list when a\n // catalog.product.* event arrives via the DOM event bridge. Confirmed\n // AI bulk mutations (one `ai.action.confirmed` + one\n // `catalog.product.updated` per record) and direct API writes both\n // surface here so the table reflects the new state without a manual\n // reload.\n useAppEvent('catalog.product.*', () => {\n setReloadToken((token) => token + 1)\n })\n const [customFieldsetFilter, setCustomFieldsetFilter] = React.useState<string | null>(null)\n const { data: customFieldDefs = [], isLoading: customFieldDefsLoading, refetch: refetchCustomFieldDefs } = useCustomFieldDefs(ENTITY_ID, {\n keyExtras: [scopeVersion],\n })\n const [channelOptionsCache, setChannelOptionsCache] = React.useState<Record<string, FilterOption>>({})\n const [categoryOptionsCache, setCategoryOptionsCache] = React.useState<Record<string, FilterOption>>({})\n const [tagOptionsCache, setTagOptionsCache] = React.useState<Record<string, FilterOption>>({})\n\n const registerOptions = React.useCallback(\n (\n setter: React.Dispatch<React.SetStateAction<Record<string, FilterOption>>>,\n options: FilterOption[]\n ) => {\n setter((prev) => {\n const next = { ...prev }\n options.forEach((opt) => {\n if (opt.value) next[opt.value] = opt\n })\n return next\n })\n },\n []\n )\n\n const registerChannelOptions = React.useCallback(\n (options: FilterOption[]) => registerOptions(setChannelOptionsCache, options),\n [registerOptions]\n )\n const registerCategoryOptions = React.useCallback(\n (options: FilterOption[]) => registerOptions(setCategoryOptionsCache, options),\n [registerOptions]\n )\n const registerTagOptions = React.useCallback(\n (options: FilterOption[]) => registerOptions(setTagOptionsCache, options),\n [registerOptions]\n )\n\n const channelOptions = React.useMemo(() => Object.values(channelOptionsCache), [channelOptionsCache])\n const categoryOptions = React.useMemo(() => Object.values(categoryOptionsCache), [categoryOptionsCache])\n const tagOptions = React.useMemo(() => Object.values(tagOptionsCache), [tagOptionsCache])\n\n const loadChannelOptions = React.useCallback(\n async (term?: string): Promise<FilterOption[]> => {\n try {\n const params = new URLSearchParams({ pageSize: '100', isActive: 'true' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; name?: string; code?: string }> }>(\n `/api/sales/channels?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.channelsLoadError', 'Failed to load channels') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label =\n typeof entry.name === 'string'\n ? entry.name\n : typeof entry.code === 'string'\n ? entry.code\n : value\n return { value, label, description: typeof entry.code === 'string' ? entry.code : undefined }\n })\n .filter((option) => !!option) as FilterOption[]\n registerChannelOptions(options)\n return options\n } catch {\n return []\n }\n },\n [registerChannelOptions, t],\n )\n\n const loadCategoryOptions = React.useCallback(\n async (term?: string): Promise<FilterOption[]> => {\n try {\n const params = new URLSearchParams({ pageSize: '200', view: 'manage' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; name?: string; parentName?: string | null }> }>(\n `/api/catalog/categories?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.categoriesLoadError', 'Failed to load categories') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label = typeof entry.name === 'string' && entry.name.trim().length ? entry.name : value\n const description =\n typeof entry.parentName === 'string' && entry.parentName.trim().length ? entry.parentName : null\n return { value, label, description }\n })\n .filter((option) => !!option) as FilterOption[]\n registerCategoryOptions(options)\n return options\n } catch {\n return []\n }\n },\n [registerCategoryOptions, t],\n )\n\n const loadTagOptions = React.useCallback(\n async (term?: string): Promise<FilterOption[]> => {\n try {\n const params = new URLSearchParams({ pageSize: '100' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; label?: string }> }>(\n `/api/catalog/tags?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.tagsLoadError', 'Failed to load tags') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label = typeof entry.label === 'string' && entry.label.trim().length ? entry.label : value\n return { value, label }\n })\n .filter((option) => !!option) as FilterOption[]\n registerTagOptions(options)\n return options\n } catch {\n return []\n }\n },\n [registerTagOptions, t],\n )\n\n const productTypeOptions = React.useMemo<FilterOption[]>(() => [\n { value: 'simple', label: t('catalog.products.types.simple', 'Simple') },\n { value: 'configurable', label: t('catalog.products.types.configurable', 'Configurable') },\n { value: 'virtual', label: t('catalog.products.types.virtual', 'Virtual') },\n { value: 'downloadable', label: t('catalog.products.types.downloadable', 'Downloadable') },\n {\n value: 'bundle',\n label: `${t('catalog.products.types.bundle', 'Bundle')} (${t('common.comingSoon', 'Coming soon')})`,\n },\n {\n value: 'grouped',\n label: `${t('catalog.products.types.grouped', 'Grouped')} (${t('common.comingSoon', 'Coming soon')})`,\n },\n ], [t])\n\n const productTypeLabelMap = React.useMemo(() => {\n const map = new Map<string, string>()\n productTypeOptions.forEach((opt) => map.set(opt.value, opt.label))\n return map\n }, [productTypeOptions])\n\n const filters = React.useMemo<FilterDef[]>(() => [\n { id: 'status', label: t('catalog.products.filters.status'), type: 'text' },\n { id: 'isActive', label: t('catalog.products.filters.active'), type: 'checkbox' },\n { id: 'configurable', label: t('catalog.products.filters.configurable'), type: 'checkbox' },\n { id: 'productType', label: t('catalog.products.filters.productType', 'Type'), type: 'select', options: productTypeOptions },\n {\n id: 'channelIds',\n label: t('catalog.products.filters.channels'),\n type: 'tags',\n loadOptions: loadChannelOptions,\n options: channelOptions,\n formatValue: (val) => channelOptionsCache[val]?.label ?? val,\n formatDescription: (val) => channelOptionsCache[val]?.description ?? null,\n },\n {\n id: 'categoryIds',\n label: t('catalog.products.filters.categories', 'Categories'),\n type: 'tags',\n loadOptions: loadCategoryOptions,\n options: categoryOptions,\n formatValue: (val) => categoryOptionsCache[val]?.label ?? val,\n formatDescription: (val) => categoryOptionsCache[val]?.description ?? null,\n },\n {\n id: 'tagIds',\n label: t('catalog.products.filters.tags', 'Tags'),\n type: 'tags',\n loadOptions: loadTagOptions,\n options: tagOptions,\n formatValue: (val) => tagOptionsCache[val]?.label ?? val,\n },\n ], [\n categoryOptions,\n categoryOptionsCache,\n channelOptions,\n channelOptionsCache,\n loadCategoryOptions,\n loadChannelOptions,\n loadTagOptions,\n productTypeOptions,\n tagOptions,\n tagOptionsCache,\n t,\n ])\n\n const columns = React.useMemo<ColumnDef<ProductRow>[]>(() => {\n const base: ColumnDef<ProductRow>[] = [\n {\n id: 'media',\n header: '',\n size: 80,\n cell: ({ row }) => (\n <ProductImageCell\n mediaId={row.original.default_media_id}\n mediaUrl={row.original.default_media_url}\n title={row.original.title}\n cropType=\"contain\"\n />\n ),\n meta: { sticky: true },\n },\n {\n accessorKey: 'title',\n header: t('catalog.products.table.title', 'Title'),\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.original.title || '\u2014'}</span>\n {row.original.subtitle ? (\n <span className=\"text-xs text-muted-foreground\">{row.original.subtitle}</span>\n ) : null}\n {row.original.handle ? (\n <span className=\"text-xs text-muted-foreground\">/{row.original.handle}</span>\n ) : null}\n {row.original.description ? (\n <span className=\"text-xs text-muted-foreground\">{markdownToPlainText(row.original.description)}</span>\n ) : null}\n </div>\n ),\n meta: { sticky: true },\n },\n {\n accessorKey: 'sku',\n header: t('catalog.products.table.sku', 'SKU'),\n cell: ({ getValue }) => {\n const value = getValue()\n return value ? <span className=\"font-mono text-xs\">{String(value)}</span> : <span className=\"text-xs text-muted-foreground\">\u2014</span>\n },\n },\n {\n accessorKey: 'product_type',\n header: t('catalog.products.table.type'),\n cell: ({ row }) => {\n const type = typeof row.original.product_type === 'string' ? row.original.product_type : 'simple'\n const label = productTypeLabelMap.get(type) ?? type\n return <span className=\"text-xs text-muted-foreground\">{label}</span>\n },\n },\n {\n accessorKey: 'is_configurable',\n header: t('catalog.products.table.configurable'),\n cell: ({ row }) => <BooleanIcon value={!!row.original.is_configurable} />,\n },\n {\n accessorKey: 'is_active',\n header: t('catalog.products.table.active'),\n cell: ({ row }) => <BooleanIcon value={!!row.original.is_active} />,\n },\n {\n accessorKey: 'pricing',\n header: t('catalog.products.table.price'),\n cell: ({ row }) => renderPrice(row.original.pricing, row.original.primary_currency_code),\n },\n {\n accessorKey: 'offers',\n header: t('catalog.products.table.channels'),\n cell: ({ row }) => renderOffers(row.original.offers),\n },\n {\n accessorKey: 'updated_at',\n header: t('catalog.products.table.updatedAt'),\n cell: ({ row }) => <span className=\"text-xs text-muted-foreground\">{formatDate(row.original.updated_at)}</span>,\n },\n ]\n return applyCustomFieldVisibility(base, customFieldDefs)\n }, [customFieldDefs, productTypeLabelMap, t])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value)\n setPage(1)\n }, [])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n setFilterValues(values)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const handleCustomFieldsetFilterChange = React.useCallback(\n (value: string | null) => {\n if (value === customFieldsetFilter) return\n setCustomFieldsetFilter(value)\n setFilterValues((prev) => {\n const entries = Object.entries(prev)\n if (!entries.some(([key]) => key.startsWith('cf_'))) return prev\n const next: FilterValues = {}\n entries.forEach(([key, val]) => {\n if (!key.startsWith('cf_')) next[key] = val\n })\n return next\n })\n setPage(1)\n },\n [customFieldsetFilter],\n )\n\n const handleRefresh = React.useCallback(() => {\n void refetchCustomFieldDefs()\n setReloadToken((token) => token + 1)\n }, [refetchCustomFieldDefs])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(PAGE_SIZE))\n if (search.trim()) params.set('search', search.trim())\n const sort = sorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n const status = filterValues.status\n if (typeof status === 'string' && status.trim()) {\n params.set('status', status.trim())\n }\n if (filterValues.isActive === true) params.set('isActive', 'true')\n if (filterValues.isActive === false) params.set('isActive', 'false')\n if (filterValues.configurable === true) params.set('configurable', 'true')\n if (filterValues.configurable === false) params.set('configurable', 'false')\n if (typeof filterValues.productType === 'string' && filterValues.productType.trim()) {\n params.set('productType', filterValues.productType.trim())\n }\n if (Array.isArray(filterValues.channelIds) && filterValues.channelIds.length) {\n const values = filterValues.channelIds\n .map((value) => (typeof value === 'string' ? value : null))\n .filter((value): value is string => !!value)\n if (values.length) params.set('channelIds', values.join(','))\n }\n if (Array.isArray(filterValues.categoryIds) && filterValues.categoryIds.length) {\n const values = filterValues.categoryIds\n .map((value) => (typeof value === 'string' ? value : null))\n .filter((value): value is string => !!value)\n if (values.length) params.set('categoryIds', values.join(','))\n }\n if (Array.isArray(filterValues.tagIds) && filterValues.tagIds.length) {\n const values = filterValues.tagIds\n .map((value) => (typeof value === 'string' ? value : null))\n .filter((value): value is string => !!value)\n if (values.length) params.set('tagIds', values.join(','))\n }\n Object.entries(filterValues).forEach(([key, value]) => {\n if (!key.startsWith('cf_') || value == null) return\n if (Array.isArray(value)) {\n const entries = value\n .map((entry) => (typeof entry === 'string' ? entry.trim() : String(entry || '').trim()))\n .filter((entry) => entry.length > 0)\n if (entries.length) params.set(key, entries.join(','))\n } else if (typeof value === 'object' && value !== null && ('from' in (value as Record<string, unknown>) || 'to' in (value as Record<string, unknown>))) {\n const range = value as { from?: string; to?: string }\n if (typeof range.from === 'string' && range.from.trim().length) {\n params.set(`${key}:from`, range.from.trim())\n }\n if (typeof range.to === 'string' && range.to.trim().length) {\n params.set(`${key}:to`, range.to.trim())\n }\n } else if (typeof value === 'string' && value.trim()) {\n params.set(key, value.trim())\n }\n })\n if (typeof customFieldsetFilter === 'string' && customFieldsetFilter.trim().length > 0) {\n params.set('customFieldset', customFieldsetFilter.trim())\n }\n return params.toString()\n }, [customFieldsetFilter, filterValues, page, search, sorting])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setCacheStatus(null)\n try {\n const fallback: ProductsResponse = { items: [], total: 0, totalPages: 1 }\n const call = await apiCall<ProductsResponse>(\n `/api/catalog/products?${queryParams}`,\n undefined,\n { fallback },\n )\n if (!call.ok) {\n const message = t('catalog.products.list.error.load', 'Failed to load products')\n flash(message, 'error')\n if (!cancelled) setCacheStatus(null)\n return\n }\n const payload = call.result ?? fallback\n if (cancelled) return\n setCacheStatus(call.cacheStatus ?? null)\n const items = Array.isArray(payload.items) ? payload.items : []\n const normalized = items.filter((item): item is ProductRow => typeof item?.id === 'string')\n setRows(normalized)\n setTotal(typeof payload.total === 'number' ? payload.total : normalized.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)\n setTotalIsCapped(payload.totalIsCapped === true)\n } catch (error) {\n if (!cancelled) {\n setCacheStatus(null)\n const message =\n error instanceof Error\n ? error.message\n : t('catalog.products.list.error.load', 'Failed to load products')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [queryParams, reloadToken, scopeVersion, t])\n\n const handleDelete = React.useCallback(async (row: ProductRow) => {\n const confirmed = await confirm({\n title: t('catalog.products.list.deleteConfirm', 'Delete this product?'),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const headers = buildOptimisticLockHeader(typeof row.updated_at === 'string' ? row.updated_at : null)\n await withScopedApiRequestHeaders(headers, () => (\n deleteCrud('catalog/products', row.id, {\n errorMessage: t('catalog.products.list.error.delete', 'Failed to delete product'),\n })\n ))\n flash(t('catalog.products.flash.deleted', 'Product deleted'), 'success')\n setReloadToken((token) => token + 1)\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : t('catalog.products.list.error.delete', 'Failed to delete product')\n flash(message, 'error')\n }\n }, [confirm, t])\n\n React.useEffect(() => {\n if (!onSnapshotChange) return\n onSnapshotChange({ search, filterValues, total })\n }, [onSnapshotChange, search, filterValues, total])\n\n const currentParams = React.useMemo(() => Object.fromEntries(new URLSearchParams(queryParams)), [queryParams])\n\n const exportConfig = React.useMemo(() => ({\n view: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('catalog/products', { ...currentParams, exportScope: 'view' }, format),\n },\n full: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('catalog/products', { ...currentParams, exportScope: 'full', all: 'true' }, format),\n },\n }), [currentParams])\n\n return (\n <>\n <DataTable<ProductRow>\n title={t('catalog.products.page.title', 'Products & services')}\n titleHeadingLevel={1}\n entityId={ENTITY_ID}\n customFieldFilterKeyExtras={[scopeVersion]}\n refreshButton={{\n label: t('catalog.products.actions.refresh', 'Refresh'),\n onRefresh: handleRefresh,\n isRefreshing: isLoading,\n }}\n actions={(\n <div className=\"flex items-center gap-2\">\n {extraActions}\n <Button asChild>\n <Link href=\"/backend/catalog/products/create\">\n {t('catalog.products.actions.create', 'Create')}\n </Link>\n </Button>\n </div>\n )}\n columns={columns}\n data={rows}\n emptyState={(\n <ListEmptyState\n entityName={t('catalog.products.page.title', 'Products & services')}\n createHref=\"/backend/catalog/products/create\"\n createLabel={t('catalog.products.actions.create', 'Create')}\n />\n )}\n searchValue={search}\n onSearchChange={handleSearchChange}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n onCustomFieldFilterFieldsetChange={handleCustomFieldsetFilterChange}\n sorting={sorting}\n onSortingChange={setSorting}\n injectionSpotId={extensionPoints.hosts.productsTable.baseSpotId}\n injectionContext={{\n search,\n filters: filterValues,\n customFieldset: customFieldsetFilter,\n page,\n sorting,\n scopeVersion,\n // Step 5.15: surface `total` so the merchandising AI widget\n // (rendered in `data-table:catalog.products:header`) can build\n // a selection-aware pageContext per spec \u00A710.1 without taking a\n // dependency on the host page.\n total,\n totalMatching: total,\n // `total` is a floor when the server capped the count, so the\n // merchandising widget must not present it as an exact match count.\n totalIsCapped,\n }}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n totalIsCapped,\n onPageChange: setPage,\n cacheStatus,\n }}\n exporter={exportConfig}\n isLoading={isLoading || customFieldDefsLoading}\n perspective={{ tableId: extensionPoints.hosts.productsTable.tableId }}\n stickyActionsColumn\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('catalog.products.table.actions.edit', 'Edit'),\n href: `/backend/catalog/products/${row.id}`,\n },\n {\n id: 'delete',\n label: t('catalog.products.table.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => {\n void handleDelete(row)\n },\n },\n ]}\n />\n )}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/catalog/extension-points'\nimport Link from 'next/link'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport type { SortingState } from '@tanstack/react-table'\nimport { DataTable, type DataTableExportFormat } from '@open-mercato/ui/backend/DataTable'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { deleteCrud, buildCrudExportUrl } from '@open-mercato/ui/backend/utils/crud'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { applyCustomFieldVisibility } from '@open-mercato/ui/backend/utils/customFieldColumns'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport type { FilterOption } from '@open-mercato/ui/backend/FilterOverlay'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { markdownToPlainText } from '@open-mercato/ui/backend/markdown/markdownToPlainText'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'\nimport { formatDisplayDateTime } from '@open-mercato/ui/primitives/date-format'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { E } from '#generated/entities.ids.generated'\nimport { ProductImageCell } from './ProductImageCell'\n\ntype PricingScope = {\n variant_id?: string | null\n offer_id?: string | null\n channel_id?: string | null\n user_id?: string | null\n user_group_id?: string | null\n customer_id?: string | null\n customer_group_id?: string | null\n}\n\ntype PricingInfo = {\n kind?: string | null\n price_kind_id?: string | null\n price_kind_code?: string | null\n currency_code?: string | null\n unit_price_net?: string | null\n unit_price_gross?: string | null\n min_quantity?: number | null\n max_quantity?: number | null\n tax_rate?: string | null\n scope?: PricingScope | null\n} | null\n\ntype OfferInfo = {\n id: string\n channelId: string\n channelName?: string | null\n channelCode?: string | null\n title: string\n description?: string | null\n isActive: boolean\n}\n\nexport type ProductRow = {\n id: string\n title: string\n subtitle?: string | null\n description?: string | null\n sku?: string | null\n handle?: string | null\n product_type?: string | null\n status_entry_id?: string | null\n primary_currency_code?: string | null\n default_unit?: string | null\n default_media_id?: string | null\n default_media_url?: string | null\n is_configurable?: boolean\n is_active?: boolean\n metadata?: Record<string, unknown> | null\n custom_fieldset_code?: string | null\n created_at?: string\n updated_at?: string\n offers?: OfferInfo[]\n pricing?: PricingInfo\n} & Record<string, unknown>\n\ntype ProductsResponse = {\n items?: ProductRow[]\n total?: number\n totalPages?: number\n totalIsCapped?: boolean\n}\n\nconst PAGE_SIZE = 25\nconst ENTITY_ID = E.catalog.catalog_product\n\nfunction formatDate(value: string | undefined, locale: string | undefined): string {\n return formatDisplayDateTime(value, locale) ?? '\u2014'\n}\n\nfunction renderOffers(offers: OfferInfo[] | undefined): React.ReactNode {\n if (!offers || offers.length === 0) return <span className=\"text-xs text-muted-foreground\">\u2014</span>\n const visible = offers.slice(0, 3)\n return (\n <div className=\"flex flex-wrap gap-1\">\n {visible.map((offer) => {\n const label =\n typeof offer.channelName === 'string' && offer.channelName.trim().length\n ? offer.channelName.trim()\n : typeof offer.title === 'string' && offer.title.trim().length\n ? offer.title.trim()\n : offer.channelId\n const badgeTitle =\n typeof offer.channelCode === 'string' && offer.channelCode.trim().length ? offer.channelCode : undefined\n return (\n <span\n key={offer.id}\n className={`inline-flex items-center rounded-full border px-2 py-0.5 text-xs ${\n offer.isActive ? 'bg-secondary/80 text-secondary-foreground' : 'bg-muted text-muted-foreground'\n }`}\n title={badgeTitle}\n >\n {label}\n </span>\n )\n })}\n {offers.length > visible.length ? (\n <span className=\"text-xs text-muted-foreground\">+{offers.length - visible.length}</span>\n ) : null}\n </div>\n )\n}\n\nfunction renderPrice(pricing: PricingInfo | undefined, currency?: string | null, fallback = '\u2014'): React.ReactNode {\n if (!pricing) return <span className=\"text-xs text-muted-foreground\">{fallback}</span>\n const unit = pricing.unit_price_net ?? pricing.unit_price_gross\n if (unit == null) return <span className=\"text-xs text-muted-foreground\">{fallback}</span>\n const formatted = `${currency ?? pricing.currency_code ?? ''} ${unit}`\n const kind = pricing.kind ?? 'list'\n return (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{formatted.trim()}</span>\n <span className=\"text-xs text-muted-foreground\">{kind}</span>\n </div>\n )\n}\n\nexport type ProductsDataTableSnapshot = {\n search: string\n filterValues: FilterValues\n total: number\n}\n\nexport type ProductsDataTableProps = {\n /**\n * Extra actions rendered alongside the built-in Create button in the\n * DataTable header. Used by the Step 4.9 AI merchandising sheet\n * trigger without coupling DataTable to the AI module.\n */\n extraActions?: React.ReactNode\n /**\n * Optional callback invoked whenever the table's search / filter /\n * total-matching snapshot changes. Used by the Step 4.9 AI merchandising\n * sheet to form a selection-aware pageContext per spec \u00A710.1.\n */\n onSnapshotChange?: (snapshot: ProductsDataTableSnapshot) => void\n}\n\nexport default function ProductsDataTable({\n extraActions,\n onSnapshotChange,\n}: ProductsDataTableProps = {}) {\n const t = useT()\n const dateLocale = useLocale()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const scopeVersion = useOrganizationScopeVersion()\n const [rows, setRows] = React.useState<ProductRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [totalIsCapped, setTotalIsCapped] = React.useState(false)\n const [cacheStatus, setCacheStatus] = React.useState<'hit' | 'miss' | null>(null)\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'title', desc: false }])\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(false)\n const [reloadToken, setReloadToken] = React.useState(0)\n // Step 5.18 (spec \u00A710 line 836, D18 demo): refresh the list when a\n // catalog.product.* event arrives via the DOM event bridge. Confirmed\n // AI bulk mutations (one `ai.action.confirmed` + one\n // `catalog.product.updated` per record) and direct API writes both\n // surface here so the table reflects the new state without a manual\n // reload.\n useAppEvent('catalog.product.*', () => {\n setReloadToken((token) => token + 1)\n })\n const [customFieldsetFilter, setCustomFieldsetFilter] = React.useState<string | null>(null)\n const { data: customFieldDefs = [], isLoading: customFieldDefsLoading, refetch: refetchCustomFieldDefs } = useCustomFieldDefs(ENTITY_ID, {\n keyExtras: [scopeVersion],\n })\n const [channelOptionsCache, setChannelOptionsCache] = React.useState<Record<string, FilterOption>>({})\n const [categoryOptionsCache, setCategoryOptionsCache] = React.useState<Record<string, FilterOption>>({})\n const [tagOptionsCache, setTagOptionsCache] = React.useState<Record<string, FilterOption>>({})\n\n const registerOptions = React.useCallback(\n (\n setter: React.Dispatch<React.SetStateAction<Record<string, FilterOption>>>,\n options: FilterOption[]\n ) => {\n setter((prev) => {\n const next = { ...prev }\n options.forEach((opt) => {\n if (opt.value) next[opt.value] = opt\n })\n return next\n })\n },\n []\n )\n\n const registerChannelOptions = React.useCallback(\n (options: FilterOption[]) => registerOptions(setChannelOptionsCache, options),\n [registerOptions]\n )\n const registerCategoryOptions = React.useCallback(\n (options: FilterOption[]) => registerOptions(setCategoryOptionsCache, options),\n [registerOptions]\n )\n const registerTagOptions = React.useCallback(\n (options: FilterOption[]) => registerOptions(setTagOptionsCache, options),\n [registerOptions]\n )\n\n const channelOptions = React.useMemo(() => Object.values(channelOptionsCache), [channelOptionsCache])\n const categoryOptions = React.useMemo(() => Object.values(categoryOptionsCache), [categoryOptionsCache])\n const tagOptions = React.useMemo(() => Object.values(tagOptionsCache), [tagOptionsCache])\n\n const loadChannelOptions = React.useCallback(\n async (term?: string): Promise<FilterOption[]> => {\n try {\n const params = new URLSearchParams({ pageSize: '100', isActive: 'true' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; name?: string; code?: string }> }>(\n `/api/sales/channels?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.channelsLoadError', 'Failed to load channels') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label =\n typeof entry.name === 'string'\n ? entry.name\n : typeof entry.code === 'string'\n ? entry.code\n : value\n return { value, label, description: typeof entry.code === 'string' ? entry.code : undefined }\n })\n .filter((option) => !!option) as FilterOption[]\n registerChannelOptions(options)\n return options\n } catch {\n return []\n }\n },\n [registerChannelOptions, t],\n )\n\n const loadCategoryOptions = React.useCallback(\n async (term?: string): Promise<FilterOption[]> => {\n try {\n const params = new URLSearchParams({ pageSize: '200', view: 'manage' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; name?: string; parentName?: string | null }> }>(\n `/api/catalog/categories?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.categoriesLoadError', 'Failed to load categories') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label = typeof entry.name === 'string' && entry.name.trim().length ? entry.name : value\n const description =\n typeof entry.parentName === 'string' && entry.parentName.trim().length ? entry.parentName : null\n return { value, label, description }\n })\n .filter((option) => !!option) as FilterOption[]\n registerCategoryOptions(options)\n return options\n } catch {\n return []\n }\n },\n [registerCategoryOptions, t],\n )\n\n const loadTagOptions = React.useCallback(\n async (term?: string): Promise<FilterOption[]> => {\n try {\n const params = new URLSearchParams({ pageSize: '100' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; label?: string }> }>(\n `/api/catalog/tags?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.tagsLoadError', 'Failed to load tags') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label = typeof entry.label === 'string' && entry.label.trim().length ? entry.label : value\n return { value, label }\n })\n .filter((option) => !!option) as FilterOption[]\n registerTagOptions(options)\n return options\n } catch {\n return []\n }\n },\n [registerTagOptions, t],\n )\n\n const productTypeOptions = React.useMemo<FilterOption[]>(() => [\n { value: 'simple', label: t('catalog.products.types.simple', 'Simple') },\n { value: 'configurable', label: t('catalog.products.types.configurable', 'Configurable') },\n { value: 'virtual', label: t('catalog.products.types.virtual', 'Virtual') },\n { value: 'downloadable', label: t('catalog.products.types.downloadable', 'Downloadable') },\n {\n value: 'bundle',\n label: `${t('catalog.products.types.bundle', 'Bundle')} (${t('common.comingSoon', 'Coming soon')})`,\n },\n {\n value: 'grouped',\n label: `${t('catalog.products.types.grouped', 'Grouped')} (${t('common.comingSoon', 'Coming soon')})`,\n },\n ], [t])\n\n const productTypeLabelMap = React.useMemo(() => {\n const map = new Map<string, string>()\n productTypeOptions.forEach((opt) => map.set(opt.value, opt.label))\n return map\n }, [productTypeOptions])\n\n const filters = React.useMemo<FilterDef[]>(() => [\n { id: 'status', label: t('catalog.products.filters.status'), type: 'text' },\n { id: 'isActive', label: t('catalog.products.filters.active'), type: 'checkbox' },\n { id: 'configurable', label: t('catalog.products.filters.configurable'), type: 'checkbox' },\n { id: 'productType', label: t('catalog.products.filters.productType', 'Type'), type: 'select', options: productTypeOptions },\n {\n id: 'channelIds',\n label: t('catalog.products.filters.channels'),\n type: 'tags',\n loadOptions: loadChannelOptions,\n options: channelOptions,\n formatValue: (val) => channelOptionsCache[val]?.label ?? val,\n formatDescription: (val) => channelOptionsCache[val]?.description ?? null,\n },\n {\n id: 'categoryIds',\n label: t('catalog.products.filters.categories', 'Categories'),\n type: 'tags',\n loadOptions: loadCategoryOptions,\n options: categoryOptions,\n formatValue: (val) => categoryOptionsCache[val]?.label ?? val,\n formatDescription: (val) => categoryOptionsCache[val]?.description ?? null,\n },\n {\n id: 'tagIds',\n label: t('catalog.products.filters.tags', 'Tags'),\n type: 'tags',\n loadOptions: loadTagOptions,\n options: tagOptions,\n formatValue: (val) => tagOptionsCache[val]?.label ?? val,\n },\n ], [\n categoryOptions,\n categoryOptionsCache,\n channelOptions,\n channelOptionsCache,\n loadCategoryOptions,\n loadChannelOptions,\n loadTagOptions,\n productTypeOptions,\n tagOptions,\n tagOptionsCache,\n t,\n ])\n\n const columns = React.useMemo<ColumnDef<ProductRow>[]>(() => {\n const base: ColumnDef<ProductRow>[] = [\n {\n id: 'media',\n header: '',\n size: 80,\n cell: ({ row }) => (\n <ProductImageCell\n mediaId={row.original.default_media_id}\n mediaUrl={row.original.default_media_url}\n title={row.original.title}\n cropType=\"contain\"\n />\n ),\n meta: { sticky: true },\n },\n {\n accessorKey: 'title',\n header: t('catalog.products.table.title', 'Title'),\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.original.title || '\u2014'}</span>\n {row.original.subtitle ? (\n <span className=\"text-xs text-muted-foreground\">{row.original.subtitle}</span>\n ) : null}\n {row.original.handle ? (\n <span className=\"text-xs text-muted-foreground\">/{row.original.handle}</span>\n ) : null}\n {row.original.description ? (\n <span className=\"text-xs text-muted-foreground\">{markdownToPlainText(row.original.description)}</span>\n ) : null}\n </div>\n ),\n meta: { sticky: true },\n },\n {\n accessorKey: 'sku',\n header: t('catalog.products.table.sku', 'SKU'),\n cell: ({ getValue }) => {\n const value = getValue()\n return value ? <span className=\"font-mono text-xs\">{String(value)}</span> : <span className=\"text-xs text-muted-foreground\">\u2014</span>\n },\n },\n {\n accessorKey: 'product_type',\n header: t('catalog.products.table.type'),\n cell: ({ row }) => {\n const type = typeof row.original.product_type === 'string' ? row.original.product_type : 'simple'\n const label = productTypeLabelMap.get(type) ?? type\n return <span className=\"text-xs text-muted-foreground\">{label}</span>\n },\n },\n {\n accessorKey: 'is_configurable',\n header: t('catalog.products.table.configurable'),\n cell: ({ row }) => <BooleanIcon value={!!row.original.is_configurable} />,\n },\n {\n accessorKey: 'is_active',\n header: t('catalog.products.table.active'),\n cell: ({ row }) => <BooleanIcon value={!!row.original.is_active} />,\n },\n {\n accessorKey: 'pricing',\n header: t('catalog.products.table.price'),\n cell: ({ row }) => renderPrice(row.original.pricing, row.original.primary_currency_code),\n },\n {\n accessorKey: 'offers',\n header: t('catalog.products.table.channels'),\n cell: ({ row }) => renderOffers(row.original.offers),\n },\n {\n accessorKey: 'updated_at',\n header: t('catalog.products.table.updatedAt'),\n cell: ({ row }) => <span className=\"text-xs text-muted-foreground\">{formatDate(row.original.updated_at, dateLocale)}</span>,\n },\n ]\n return applyCustomFieldVisibility(base, customFieldDefs)\n }, [customFieldDefs, productTypeLabelMap, t, dateLocale])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value)\n setPage(1)\n }, [])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n setFilterValues(values)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const handleCustomFieldsetFilterChange = React.useCallback(\n (value: string | null) => {\n if (value === customFieldsetFilter) return\n setCustomFieldsetFilter(value)\n setFilterValues((prev) => {\n const entries = Object.entries(prev)\n if (!entries.some(([key]) => key.startsWith('cf_'))) return prev\n const next: FilterValues = {}\n entries.forEach(([key, val]) => {\n if (!key.startsWith('cf_')) next[key] = val\n })\n return next\n })\n setPage(1)\n },\n [customFieldsetFilter],\n )\n\n const handleRefresh = React.useCallback(() => {\n void refetchCustomFieldDefs()\n setReloadToken((token) => token + 1)\n }, [refetchCustomFieldDefs])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(PAGE_SIZE))\n if (search.trim()) params.set('search', search.trim())\n const sort = sorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n const status = filterValues.status\n if (typeof status === 'string' && status.trim()) {\n params.set('status', status.trim())\n }\n if (filterValues.isActive === true) params.set('isActive', 'true')\n if (filterValues.isActive === false) params.set('isActive', 'false')\n if (filterValues.configurable === true) params.set('configurable', 'true')\n if (filterValues.configurable === false) params.set('configurable', 'false')\n if (typeof filterValues.productType === 'string' && filterValues.productType.trim()) {\n params.set('productType', filterValues.productType.trim())\n }\n if (Array.isArray(filterValues.channelIds) && filterValues.channelIds.length) {\n const values = filterValues.channelIds\n .map((value) => (typeof value === 'string' ? value : null))\n .filter((value): value is string => !!value)\n if (values.length) params.set('channelIds', values.join(','))\n }\n if (Array.isArray(filterValues.categoryIds) && filterValues.categoryIds.length) {\n const values = filterValues.categoryIds\n .map((value) => (typeof value === 'string' ? value : null))\n .filter((value): value is string => !!value)\n if (values.length) params.set('categoryIds', values.join(','))\n }\n if (Array.isArray(filterValues.tagIds) && filterValues.tagIds.length) {\n const values = filterValues.tagIds\n .map((value) => (typeof value === 'string' ? value : null))\n .filter((value): value is string => !!value)\n if (values.length) params.set('tagIds', values.join(','))\n }\n Object.entries(filterValues).forEach(([key, value]) => {\n if (!key.startsWith('cf_') || value == null) return\n if (Array.isArray(value)) {\n const entries = value\n .map((entry) => (typeof entry === 'string' ? entry.trim() : String(entry || '').trim()))\n .filter((entry) => entry.length > 0)\n if (entries.length) params.set(key, entries.join(','))\n } else if (typeof value === 'object' && value !== null && ('from' in (value as Record<string, unknown>) || 'to' in (value as Record<string, unknown>))) {\n const range = value as { from?: string; to?: string }\n if (typeof range.from === 'string' && range.from.trim().length) {\n params.set(`${key}:from`, range.from.trim())\n }\n if (typeof range.to === 'string' && range.to.trim().length) {\n params.set(`${key}:to`, range.to.trim())\n }\n } else if (typeof value === 'string' && value.trim()) {\n params.set(key, value.trim())\n }\n })\n if (typeof customFieldsetFilter === 'string' && customFieldsetFilter.trim().length > 0) {\n params.set('customFieldset', customFieldsetFilter.trim())\n }\n return params.toString()\n }, [customFieldsetFilter, filterValues, page, search, sorting])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setCacheStatus(null)\n try {\n const fallback: ProductsResponse = { items: [], total: 0, totalPages: 1 }\n const call = await apiCall<ProductsResponse>(\n `/api/catalog/products?${queryParams}`,\n undefined,\n { fallback },\n )\n if (!call.ok) {\n const message = t('catalog.products.list.error.load', 'Failed to load products')\n flash(message, 'error')\n if (!cancelled) setCacheStatus(null)\n return\n }\n const payload = call.result ?? fallback\n if (cancelled) return\n setCacheStatus(call.cacheStatus ?? null)\n const items = Array.isArray(payload.items) ? payload.items : []\n const normalized = items.filter((item): item is ProductRow => typeof item?.id === 'string')\n setRows(normalized)\n setTotal(typeof payload.total === 'number' ? payload.total : normalized.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)\n setTotalIsCapped(payload.totalIsCapped === true)\n } catch (error) {\n if (!cancelled) {\n setCacheStatus(null)\n const message =\n error instanceof Error\n ? error.message\n : t('catalog.products.list.error.load', 'Failed to load products')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [queryParams, reloadToken, scopeVersion, t])\n\n const handleDelete = React.useCallback(async (row: ProductRow) => {\n const confirmed = await confirm({\n title: t('catalog.products.list.deleteConfirm', 'Delete this product?'),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const headers = buildOptimisticLockHeader(typeof row.updated_at === 'string' ? row.updated_at : null)\n await withScopedApiRequestHeaders(headers, () => (\n deleteCrud('catalog/products', row.id, {\n errorMessage: t('catalog.products.list.error.delete', 'Failed to delete product'),\n })\n ))\n flash(t('catalog.products.flash.deleted', 'Product deleted'), 'success')\n setReloadToken((token) => token + 1)\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : t('catalog.products.list.error.delete', 'Failed to delete product')\n flash(message, 'error')\n }\n }, [confirm, t])\n\n React.useEffect(() => {\n if (!onSnapshotChange) return\n onSnapshotChange({ search, filterValues, total })\n }, [onSnapshotChange, search, filterValues, total])\n\n const currentParams = React.useMemo(() => Object.fromEntries(new URLSearchParams(queryParams)), [queryParams])\n\n const exportConfig = React.useMemo(() => ({\n view: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('catalog/products', { ...currentParams, exportScope: 'view' }, format),\n },\n full: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('catalog/products', { ...currentParams, exportScope: 'full', all: 'true' }, format),\n },\n }), [currentParams])\n\n return (\n <>\n <DataTable<ProductRow>\n title={t('catalog.products.page.title', 'Products & services')}\n titleHeadingLevel={1}\n entityId={ENTITY_ID}\n customFieldFilterKeyExtras={[scopeVersion]}\n refreshButton={{\n label: t('catalog.products.actions.refresh', 'Refresh'),\n onRefresh: handleRefresh,\n isRefreshing: isLoading,\n }}\n actions={(\n <div className=\"flex items-center gap-2\">\n {extraActions}\n <Button asChild>\n <Link href=\"/backend/catalog/products/create\">\n {t('catalog.products.actions.create', 'Create')}\n </Link>\n </Button>\n </div>\n )}\n columns={columns}\n data={rows}\n emptyState={(\n <ListEmptyState\n entityName={t('catalog.products.page.title', 'Products & services')}\n createHref=\"/backend/catalog/products/create\"\n createLabel={t('catalog.products.actions.create', 'Create')}\n />\n )}\n searchValue={search}\n onSearchChange={handleSearchChange}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n onCustomFieldFilterFieldsetChange={handleCustomFieldsetFilterChange}\n sorting={sorting}\n onSortingChange={setSorting}\n injectionSpotId={extensionPoints.hosts.productsTable.baseSpotId}\n injectionContext={{\n search,\n filters: filterValues,\n customFieldset: customFieldsetFilter,\n page,\n sorting,\n scopeVersion,\n // Step 5.15: surface `total` so the merchandising AI widget\n // (rendered in `data-table:catalog.products:header`) can build\n // a selection-aware pageContext per spec \u00A710.1 without taking a\n // dependency on the host page.\n total,\n totalMatching: total,\n // `total` is a floor when the server capped the count, so the\n // merchandising widget must not present it as an exact match count.\n totalIsCapped,\n }}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n totalIsCapped,\n onPageChange: setPage,\n cacheStatus,\n }}\n exporter={exportConfig}\n isLoading={isLoading || customFieldDefsLoading}\n perspective={{ tableId: extensionPoints.hosts.productsTable.tableId }}\n stickyActionsColumn\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('catalog.products.table.actions.edit', 'Edit'),\n href: `/backend/catalog/products/${row.id}`,\n },\n {\n id: 'delete',\n label: t('catalog.products.table.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => {\n void handleDelete(row)\n },\n },\n ]}\n />\n )}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -10,7 +10,8 @@ import { StatusBadge } from "@open-mercato/ui/primitives/status-badge";
10
10
  import { Tag } from "@open-mercato/ui/primitives/tag";
11
11
  import { Button } from "@open-mercato/ui/primitives/button";
12
12
  import { Plus, GripVertical } from "lucide-react";
13
- import { useT } from "@open-mercato/shared/lib/i18n/context";
13
+ import { useT, useLocale } from "@open-mercato/shared/lib/i18n/context";
14
+ import { formatDisplayDateTime } from "@open-mercato/ui/primitives/date-format";
14
15
  import { apiCall, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
15
16
  import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
16
17
  import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
@@ -72,6 +73,7 @@ function DragHandle({ id }) {
72
73
  }
73
74
  function CustomerGroupsPage() {
74
75
  const t = useT();
76
+ const dateLocale = useLocale();
75
77
  const { confirm: confirmDialog, ConfirmDialogElement } = useConfirmDialog();
76
78
  const { payload: backendChromePayload, isReady: backendChromeReady } = useBackendChrome();
77
79
  const canManage = backendChromeReady && hasFeature(backendChromePayload?.grantedFeatures, MANAGE_FEATURE);
@@ -275,10 +277,10 @@ function CustomerGroupsPage() {
275
277
  {
276
278
  accessorKey: "updated_at",
277
279
  header: t("customer_groups.groups.list.columns.updatedAt", "Updated"),
278
- cell: ({ row }) => row.original.updated_at ? new Date(row.original.updated_at).toLocaleString() : "\u2014"
280
+ cell: ({ row }) => formatDisplayDateTime(row.original.updated_at, dateLocale) ?? "\u2014"
279
281
  }
280
282
  ],
281
- [t, dragReorderEnabled]
283
+ [t, dateLocale, dragReorderEnabled]
282
284
  );
283
285
  const filterDefs = React.useMemo(
284
286
  () => [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customer_groups/backend/customer-groups/page.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { StatusBadge, type StatusMap } from '@open-mercato/ui/primitives/status-badge'\nimport { Tag, type TagMap } from '@open-mercato/ui/primitives/tag'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Plus, GripVertical } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useBackendChrome } from '@open-mercato/ui/backend/BackendChromeProvider'\nimport { ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { hasFeature } from '@open-mercato/shared/security/features'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport {\n DndContext,\n closestCenter,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n type DragEndEvent,\n} from '@dnd-kit/core'\nimport {\n SortableContext,\n sortableKeyboardCoordinates,\n verticalListSortingStrategy,\n useSortable,\n} from '@dnd-kit/sortable'\nimport { CSS } from '@dnd-kit/utilities'\nimport { OrphanBanner } from '../../components/OrphanBanner'\n\ntype CustomerGroupRow = {\n id: string\n organization_id: string | null\n tenant_id: string | null\n code: string\n name: string\n description: string | null\n kind: string\n parent_id: string | null\n priority: number\n is_default: boolean\n is_active: boolean\n created_at: string | null\n updated_at: string | null\n}\n\ntype ResponsePayload = {\n items: CustomerGroupRow[]\n total: number\n page: number\n totalPages: number\n totalIsCapped?: boolean\n}\n\nconst PAGE_SIZE = 100\nconst MANAGE_FEATURE = 'customer_groups.groups.manage'\n\nconst kindTagMap: TagMap<string> = {\n b2c: 'info',\n b2b: 'success',\n internal: 'neutral',\n partner: 'brand',\n}\n\nconst activeStatusMap: StatusMap<'active' | 'inactive'> = {\n active: 'success',\n inactive: 'neutral',\n}\n\n// Drag handle for one row's priority cell. Mirrors the `SortableColumnItem`\n// pattern in `packages/ui/src/backend/columns/ColumnChooserPanel.tsx` \u2014 the only\n// other place in this repo wiring `@dnd-kit/core` + `@dnd-kit/sortable`.\n//\n// Known limitation: `DataTable`'s `TableRow` (packages/ui/src/primitives/table.tsx)\n// is a plain function component that does not forward `ref`, so `useSortable`'s\n// `setNodeRef` cannot be attached to the actual `<tr>` \u2014 only to this handle. The\n// drag therefore animates the handle icon, not the whole row, while the\n// underlying reorder math (collision detection, `arrayMove`) is fully correct\n// because the handles stack in the same vertical order as the rows. A full\n// row-lift animation would require `TableRow` to forward `ref`, which is a\n// DataTable/Table primitive contract change gated behind \"Ask First\" in\n// packages/ui/AGENTS.md.\nfunction DragHandle({ id }: { id: string }) {\n const t = useT()\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id })\n const style: React.CSSProperties = {\n transform: CSS.Transform.toString(transform),\n transition,\n opacity: isDragging ? 0.5 : 1,\n }\n return (\n <span\n ref={setNodeRef}\n style={style}\n className=\"inline-flex cursor-grab items-center text-muted-foreground active:cursor-grabbing\"\n aria-label={t('customer_groups.groups.list.dragHandle', 'Reorder')}\n {...attributes}\n {...listeners}\n >\n <GripVertical className=\"size-4\" />\n </span>\n )\n}\n\nexport default function CustomerGroupsPage() {\n const t = useT()\n const { confirm: confirmDialog, ConfirmDialogElement } = useConfirmDialog()\n const { payload: backendChromePayload, isReady: backendChromeReady } = useBackendChrome()\n const canManage = backendChromeReady && hasFeature(backendChromePayload?.grantedFeatures, MANAGE_FEATURE)\n const [rows, setRows] = React.useState<CustomerGroupRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [totalIsCapped, setTotalIsCapped] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const [reloadToken, setReloadToken] = React.useState(0)\n const mutationContextId = 'customer-groups-list:mutation'\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: mutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setLoadError(null)\n try {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(PAGE_SIZE))\n // Always requested explicitly: drag-reorder relies on the displayed\n // order matching `CustomerGroup.priority` ascending.\n params.set('sortField', 'priority')\n params.set('sortDir', 'asc')\n if (search) params.set('search', search)\n if (filters.isActive === 'true') params.set('isActive', 'true')\n if (filters.isActive === 'false') params.set('isActive', 'false')\n\n const fallback: ResponsePayload = { items: [], total: 0, page: 1, totalPages: 1 }\n const call = await apiCall<ResponsePayload>(\n `/api/customer_groups/customer-groups?${params.toString()}`,\n undefined,\n { fallback },\n )\n if (cancelled) return\n if (!call.ok) {\n setRows([])\n setLoadError(t('customer_groups.groups.list.error.load', 'Failed to load customer groups'))\n return\n }\n const payload = call.result ?? fallback\n const items = Array.isArray(payload.items) ? payload.items : []\n setRows(items)\n setTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload.totalPages === 'number' && payload.totalPages > 0 ? payload.totalPages : 1)\n setTotalIsCapped(payload.totalIsCapped === true)\n } catch {\n if (!cancelled) {\n setRows([])\n setLoadError(t('customer_groups.groups.list.error.load', 'Failed to load customer groups'))\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [page, search, filters, reloadToken, t])\n\n const handleSearchChange = React.useCallback((next: string) => {\n setSearch(next)\n setPage(1)\n }, [])\n\n const handleFiltersApply = React.useCallback((next: FilterValues) => {\n setFilters(next)\n setPage(1)\n }, [])\n\n // Drag-reorder only makes sense over the full, priority-ordered set: a search\n // term or an is_active filter narrows the visible rows to a subset, and\n // rewriting priorities for just that subset (in gaps of 10, per the reorder\n // command) would scramble the relative order of rows currently hidden by the\n // filter. Reordering stays available only on the unfiltered view.\n // The same holds for a paginated view: the reorder command rewrites the posted\n // ids in gaps of 10, so posting one page of a multi-page set would collide with\n // (and scramble) the priorities of groups on other pages. Reordering therefore\n // also requires the whole set to be loaded, and the manage feature.\n const isUnfilteredView = !search && (!filters.isActive || filters.isActive === '')\n const allGroupsLoaded = page === 1 && !totalIsCapped && total <= rows.length\n const dragReorderEnabled = canManage && !loadError && isUnfilteredView && allGroupsLoaded\n const showReorderUnavailableHint = canManage && !loadError && !isLoading && isUnfilteredView && !allGroupsLoaded\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),\n useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),\n )\n\n const handleDragEnd = React.useCallback(\n async (event: DragEndEvent) => {\n const { active, over } = event\n if (!over || active.id === over.id) return\n const oldIndex = rows.findIndex((row) => row.id === active.id)\n const newIndex = rows.findIndex((row) => row.id === over.id)\n if (oldIndex === -1 || newIndex === -1) return\n\n const previousRows = rows\n const reordered = [...rows]\n const [moved] = reordered.splice(oldIndex, 1)\n reordered.splice(newIndex, 0, moved)\n // Optimistic reorder \u2014 visible immediately, no page reload.\n setRows(reordered)\n\n const ids = reordered.map((row) => row.id)\n try {\n await runMutation({\n operation: async () => {\n const call = await apiCall('/api/customer_groups/customer-groups/reorder', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ ids }),\n })\n if (!call.ok) {\n throw Object.assign(new Error('[internal] customer_groups.reorder failed'), { status: call.status })\n }\n return call\n },\n context: {\n formId: mutationContextId,\n resourceKind: 'customer_groups.group',\n resourceId: 'reorder',\n retryLastMutation,\n },\n mutationPayload: { ids },\n })\n flash(t('customer_groups.groups.list.flash.reordered', 'Group order updated'), 'success')\n setReloadToken((token) => token + 1)\n } catch {\n // Revert the optimistic reorder on failure.\n setRows(previousRows)\n flash(t('customer_groups.groups.list.flash.reorderError', 'Could not save the new order'), 'error')\n }\n },\n [mutationContextId, retryLastMutation, rows, runMutation, t],\n )\n\n const handleDelete = React.useCallback(\n async (row: CustomerGroupRow) => {\n const confirmed = await confirmDialog({\n title: t('customer_groups.groups.list.confirmDelete', 'Delete customer group \"{code}\"?', { code: row.code }),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n await runMutation({\n operation: async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(row.updated_at),\n () => apiCall(`/api/customer_groups/customer-groups?id=${encodeURIComponent(row.id)}`, { method: 'DELETE' }),\n )\n if (!call.ok) {\n throw Object.assign(new Error('[internal] customer_groups.delete failed'), {\n status: call.status,\n ...((call.result as Record<string, unknown> | null) ?? {}),\n })\n }\n return call\n },\n context: {\n formId: mutationContextId,\n resourceKind: 'customer_groups.group',\n resourceId: row.id,\n retryLastMutation,\n },\n mutationPayload: { id: row.id },\n })\n flash(t('customer_groups.groups.list.flash.deleted', 'Customer group deleted'), 'success')\n setReloadToken((token) => token + 1)\n } catch (error) {\n if (surfaceRecordConflict(error, t, { onRefresh: () => setReloadToken((token) => token + 1) })) return\n const serverMessage = (error as { error?: unknown } | null)?.error\n flash(\n typeof serverMessage === 'string' && serverMessage.trim()\n ? serverMessage\n : t('customer_groups.groups.list.flash.deleteError', 'Could not delete the customer group'),\n 'error',\n )\n }\n },\n [t, confirmDialog, mutationContextId, retryLastMutation, runMutation],\n )\n\n const columns = React.useMemo<ColumnDef<CustomerGroupRow>[]>(\n () => [\n {\n accessorKey: 'priority',\n header: t('customer_groups.groups.list.columns.priority', 'Priority'),\n cell: ({ row }) => (\n <div className=\"flex items-center gap-2\">\n {dragReorderEnabled ? (\n <DragHandle id={row.original.id} />\n ) : (\n <GripVertical className=\"size-4 text-muted-foreground/40\" aria-hidden=\"true\" />\n )}\n <span className=\"tabular-nums text-muted-foreground\">{row.original.priority}</span>\n </div>\n ),\n },\n {\n accessorKey: 'code',\n header: t('customer_groups.groups.list.columns.code', 'Code'),\n cell: ({ row }) => <span className=\"font-mono font-medium\">{row.original.code}</span>,\n },\n {\n accessorKey: 'name',\n header: t('customer_groups.groups.list.columns.name', 'Name'),\n },\n {\n accessorKey: 'kind',\n header: t('customer_groups.groups.list.columns.kind', 'Kind'),\n cell: ({ row }) => (\n <Tag variant={kindTagMap[row.original.kind] ?? 'default'} dot>\n {t(`customer_groups.groups.kind.${row.original.kind}`, row.original.kind)}\n </Tag>\n ),\n },\n {\n accessorKey: 'is_default',\n header: t('customer_groups.groups.list.columns.isDefault', 'Default'),\n cell: ({ row }) =>\n row.original.is_default ? (\n <Tag variant=\"brand\" dot>\n {t('customer_groups.groups.list.defaultTag', 'Default')}\n </Tag>\n ) : (\n <span className=\"text-muted-foreground\">\u2014</span>\n ),\n },\n {\n accessorKey: 'is_active',\n header: t('customer_groups.groups.list.columns.isActive', 'Status'),\n cell: ({ row }) => (\n <StatusBadge variant={activeStatusMap[row.original.is_active ? 'active' : 'inactive']} dot>\n {row.original.is_active\n ? t('customer_groups.groups.list.status.active', 'Active')\n : t('customer_groups.groups.list.status.inactive', 'Inactive')}\n </StatusBadge>\n ),\n },\n {\n accessorKey: 'updated_at',\n header: t('customer_groups.groups.list.columns.updatedAt', 'Updated'),\n cell: ({ row }) => (row.original.updated_at ? new Date(row.original.updated_at).toLocaleString() : '\u2014'),\n },\n ],\n [t, dragReorderEnabled],\n )\n\n const filterDefs = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'isActive',\n label: t('customer_groups.groups.list.filters.status', 'Status'),\n type: 'select',\n options: [\n { label: t('customer_groups.groups.list.filters.all', 'All'), value: '' },\n { label: t('customer_groups.groups.list.filters.active', 'Active'), value: 'true' },\n { label: t('customer_groups.groups.list.filters.inactive', 'Inactive'), value: 'false' },\n ],\n },\n ],\n [t],\n )\n\n const table = (\n <DataTable\n title={t('customer_groups.groups.list.title', 'Customer groups')}\n titleHeadingLevel={1}\n columns={columns}\n data={rows}\n sortable={false}\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder={t('customer_groups.groups.list.searchPlaceholder', 'Search groups...')}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={() => handleFiltersApply({})}\n actions={\n canManage ? (\n <Button asChild>\n <Link href=\"/backend/customer-groups/create\">\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('customer_groups.groups.list.actions.create', 'Create group')}\n </Link>\n </Button>\n ) : undefined\n }\n rowActions={(row) => (\n <RowActions\n items={canManage ? [\n { id: 'edit', label: t('common.edit', 'Edit'), href: `/backend/customer-groups/${row.id}/edit` },\n { id: 'delete', label: t('common.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },\n ] : [\n { id: 'open', label: t('common.open', 'Open'), href: `/backend/customer-groups/${row.id}/edit` },\n ]}\n />\n )}\n emptyState={\n <ListEmptyState\n entityName={t('customer_groups.groups.list.title', 'Customer groups')}\n title={t('customer_groups.groups.list.empty.title', 'No customer groups yet')}\n description={t(\n 'customer_groups.groups.list.empty.description',\n 'Create your first group to start organizing customers.',\n )}\n createHref={canManage ? '/backend/customer-groups/create' : undefined}\n createLabel={t('customer_groups.groups.list.empty.createLabel', 'Create your first group')}\n />\n }\n error={loadError ? <ErrorMessage label={loadError} /> : null}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n totalIsCapped,\n onPageChange: setPage,\n }}\n isLoading={isLoading}\n />\n )\n\n return (\n <Page>\n <PageBody>\n {canManage ? <OrphanBanner /> : null}\n {showReorderUnavailableHint ? (\n <Alert status=\"information\" size=\"sm\" className=\"mb-4\">\n {t(\n 'customer_groups.groups.list.reorderUnavailable',\n 'Drag-to-reorder is off because there are more groups than fit on one page. Edit a group to change its priority.',\n )}\n </Alert>\n ) : null}\n {dragReorderEnabled ? (\n <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>\n <SortableContext items={rows.map((row) => row.id)} strategy={verticalListSortingStrategy}>\n {table}\n </SortableContext>\n </DndContext>\n ) : (\n table\n )}\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 Link from 'next/link'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { StatusBadge, type StatusMap } from '@open-mercato/ui/primitives/status-badge'\nimport { Tag, type TagMap } from '@open-mercato/ui/primitives/tag'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Plus, GripVertical } from 'lucide-react'\nimport { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'\nimport { formatDisplayDateTime } from '@open-mercato/ui/primitives/date-format'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useBackendChrome } from '@open-mercato/ui/backend/BackendChromeProvider'\nimport { ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { hasFeature } from '@open-mercato/shared/security/features'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport {\n DndContext,\n closestCenter,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n type DragEndEvent,\n} from '@dnd-kit/core'\nimport {\n SortableContext,\n sortableKeyboardCoordinates,\n verticalListSortingStrategy,\n useSortable,\n} from '@dnd-kit/sortable'\nimport { CSS } from '@dnd-kit/utilities'\nimport { OrphanBanner } from '../../components/OrphanBanner'\n\ntype CustomerGroupRow = {\n id: string\n organization_id: string | null\n tenant_id: string | null\n code: string\n name: string\n description: string | null\n kind: string\n parent_id: string | null\n priority: number\n is_default: boolean\n is_active: boolean\n created_at: string | null\n updated_at: string | null\n}\n\ntype ResponsePayload = {\n items: CustomerGroupRow[]\n total: number\n page: number\n totalPages: number\n totalIsCapped?: boolean\n}\n\nconst PAGE_SIZE = 100\nconst MANAGE_FEATURE = 'customer_groups.groups.manage'\n\nconst kindTagMap: TagMap<string> = {\n b2c: 'info',\n b2b: 'success',\n internal: 'neutral',\n partner: 'brand',\n}\n\nconst activeStatusMap: StatusMap<'active' | 'inactive'> = {\n active: 'success',\n inactive: 'neutral',\n}\n\n// Drag handle for one row's priority cell. Mirrors the `SortableColumnItem`\n// pattern in `packages/ui/src/backend/columns/ColumnChooserPanel.tsx` \u2014 the only\n// other place in this repo wiring `@dnd-kit/core` + `@dnd-kit/sortable`.\n//\n// Known limitation: `DataTable`'s `TableRow` (packages/ui/src/primitives/table.tsx)\n// is a plain function component that does not forward `ref`, so `useSortable`'s\n// `setNodeRef` cannot be attached to the actual `<tr>` \u2014 only to this handle. The\n// drag therefore animates the handle icon, not the whole row, while the\n// underlying reorder math (collision detection, `arrayMove`) is fully correct\n// because the handles stack in the same vertical order as the rows. A full\n// row-lift animation would require `TableRow` to forward `ref`, which is a\n// DataTable/Table primitive contract change gated behind \"Ask First\" in\n// packages/ui/AGENTS.md.\nfunction DragHandle({ id }: { id: string }) {\n const t = useT()\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id })\n const style: React.CSSProperties = {\n transform: CSS.Transform.toString(transform),\n transition,\n opacity: isDragging ? 0.5 : 1,\n }\n return (\n <span\n ref={setNodeRef}\n style={style}\n className=\"inline-flex cursor-grab items-center text-muted-foreground active:cursor-grabbing\"\n aria-label={t('customer_groups.groups.list.dragHandle', 'Reorder')}\n {...attributes}\n {...listeners}\n >\n <GripVertical className=\"size-4\" />\n </span>\n )\n}\n\nexport default function CustomerGroupsPage() {\n const t = useT()\n const dateLocale = useLocale()\n const { confirm: confirmDialog, ConfirmDialogElement } = useConfirmDialog()\n const { payload: backendChromePayload, isReady: backendChromeReady } = useBackendChrome()\n const canManage = backendChromeReady && hasFeature(backendChromePayload?.grantedFeatures, MANAGE_FEATURE)\n const [rows, setRows] = React.useState<CustomerGroupRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [totalIsCapped, setTotalIsCapped] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const [reloadToken, setReloadToken] = React.useState(0)\n const mutationContextId = 'customer-groups-list:mutation'\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: mutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setLoadError(null)\n try {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(PAGE_SIZE))\n // Always requested explicitly: drag-reorder relies on the displayed\n // order matching `CustomerGroup.priority` ascending.\n params.set('sortField', 'priority')\n params.set('sortDir', 'asc')\n if (search) params.set('search', search)\n if (filters.isActive === 'true') params.set('isActive', 'true')\n if (filters.isActive === 'false') params.set('isActive', 'false')\n\n const fallback: ResponsePayload = { items: [], total: 0, page: 1, totalPages: 1 }\n const call = await apiCall<ResponsePayload>(\n `/api/customer_groups/customer-groups?${params.toString()}`,\n undefined,\n { fallback },\n )\n if (cancelled) return\n if (!call.ok) {\n setRows([])\n setLoadError(t('customer_groups.groups.list.error.load', 'Failed to load customer groups'))\n return\n }\n const payload = call.result ?? fallback\n const items = Array.isArray(payload.items) ? payload.items : []\n setRows(items)\n setTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload.totalPages === 'number' && payload.totalPages > 0 ? payload.totalPages : 1)\n setTotalIsCapped(payload.totalIsCapped === true)\n } catch {\n if (!cancelled) {\n setRows([])\n setLoadError(t('customer_groups.groups.list.error.load', 'Failed to load customer groups'))\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [page, search, filters, reloadToken, t])\n\n const handleSearchChange = React.useCallback((next: string) => {\n setSearch(next)\n setPage(1)\n }, [])\n\n const handleFiltersApply = React.useCallback((next: FilterValues) => {\n setFilters(next)\n setPage(1)\n }, [])\n\n // Drag-reorder only makes sense over the full, priority-ordered set: a search\n // term or an is_active filter narrows the visible rows to a subset, and\n // rewriting priorities for just that subset (in gaps of 10, per the reorder\n // command) would scramble the relative order of rows currently hidden by the\n // filter. Reordering stays available only on the unfiltered view.\n // The same holds for a paginated view: the reorder command rewrites the posted\n // ids in gaps of 10, so posting one page of a multi-page set would collide with\n // (and scramble) the priorities of groups on other pages. Reordering therefore\n // also requires the whole set to be loaded, and the manage feature.\n const isUnfilteredView = !search && (!filters.isActive || filters.isActive === '')\n const allGroupsLoaded = page === 1 && !totalIsCapped && total <= rows.length\n const dragReorderEnabled = canManage && !loadError && isUnfilteredView && allGroupsLoaded\n const showReorderUnavailableHint = canManage && !loadError && !isLoading && isUnfilteredView && !allGroupsLoaded\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),\n useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),\n )\n\n const handleDragEnd = React.useCallback(\n async (event: DragEndEvent) => {\n const { active, over } = event\n if (!over || active.id === over.id) return\n const oldIndex = rows.findIndex((row) => row.id === active.id)\n const newIndex = rows.findIndex((row) => row.id === over.id)\n if (oldIndex === -1 || newIndex === -1) return\n\n const previousRows = rows\n const reordered = [...rows]\n const [moved] = reordered.splice(oldIndex, 1)\n reordered.splice(newIndex, 0, moved)\n // Optimistic reorder \u2014 visible immediately, no page reload.\n setRows(reordered)\n\n const ids = reordered.map((row) => row.id)\n try {\n await runMutation({\n operation: async () => {\n const call = await apiCall('/api/customer_groups/customer-groups/reorder', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ ids }),\n })\n if (!call.ok) {\n throw Object.assign(new Error('[internal] customer_groups.reorder failed'), { status: call.status })\n }\n return call\n },\n context: {\n formId: mutationContextId,\n resourceKind: 'customer_groups.group',\n resourceId: 'reorder',\n retryLastMutation,\n },\n mutationPayload: { ids },\n })\n flash(t('customer_groups.groups.list.flash.reordered', 'Group order updated'), 'success')\n setReloadToken((token) => token + 1)\n } catch {\n // Revert the optimistic reorder on failure.\n setRows(previousRows)\n flash(t('customer_groups.groups.list.flash.reorderError', 'Could not save the new order'), 'error')\n }\n },\n [mutationContextId, retryLastMutation, rows, runMutation, t],\n )\n\n const handleDelete = React.useCallback(\n async (row: CustomerGroupRow) => {\n const confirmed = await confirmDialog({\n title: t('customer_groups.groups.list.confirmDelete', 'Delete customer group \"{code}\"?', { code: row.code }),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n await runMutation({\n operation: async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(row.updated_at),\n () => apiCall(`/api/customer_groups/customer-groups?id=${encodeURIComponent(row.id)}`, { method: 'DELETE' }),\n )\n if (!call.ok) {\n throw Object.assign(new Error('[internal] customer_groups.delete failed'), {\n status: call.status,\n ...((call.result as Record<string, unknown> | null) ?? {}),\n })\n }\n return call\n },\n context: {\n formId: mutationContextId,\n resourceKind: 'customer_groups.group',\n resourceId: row.id,\n retryLastMutation,\n },\n mutationPayload: { id: row.id },\n })\n flash(t('customer_groups.groups.list.flash.deleted', 'Customer group deleted'), 'success')\n setReloadToken((token) => token + 1)\n } catch (error) {\n if (surfaceRecordConflict(error, t, { onRefresh: () => setReloadToken((token) => token + 1) })) return\n const serverMessage = (error as { error?: unknown } | null)?.error\n flash(\n typeof serverMessage === 'string' && serverMessage.trim()\n ? serverMessage\n : t('customer_groups.groups.list.flash.deleteError', 'Could not delete the customer group'),\n 'error',\n )\n }\n },\n [t, confirmDialog, mutationContextId, retryLastMutation, runMutation],\n )\n\n const columns = React.useMemo<ColumnDef<CustomerGroupRow>[]>(\n () => [\n {\n accessorKey: 'priority',\n header: t('customer_groups.groups.list.columns.priority', 'Priority'),\n cell: ({ row }) => (\n <div className=\"flex items-center gap-2\">\n {dragReorderEnabled ? (\n <DragHandle id={row.original.id} />\n ) : (\n <GripVertical className=\"size-4 text-muted-foreground/40\" aria-hidden=\"true\" />\n )}\n <span className=\"tabular-nums text-muted-foreground\">{row.original.priority}</span>\n </div>\n ),\n },\n {\n accessorKey: 'code',\n header: t('customer_groups.groups.list.columns.code', 'Code'),\n cell: ({ row }) => <span className=\"font-mono font-medium\">{row.original.code}</span>,\n },\n {\n accessorKey: 'name',\n header: t('customer_groups.groups.list.columns.name', 'Name'),\n },\n {\n accessorKey: 'kind',\n header: t('customer_groups.groups.list.columns.kind', 'Kind'),\n cell: ({ row }) => (\n <Tag variant={kindTagMap[row.original.kind] ?? 'default'} dot>\n {t(`customer_groups.groups.kind.${row.original.kind}`, row.original.kind)}\n </Tag>\n ),\n },\n {\n accessorKey: 'is_default',\n header: t('customer_groups.groups.list.columns.isDefault', 'Default'),\n cell: ({ row }) =>\n row.original.is_default ? (\n <Tag variant=\"brand\" dot>\n {t('customer_groups.groups.list.defaultTag', 'Default')}\n </Tag>\n ) : (\n <span className=\"text-muted-foreground\">\u2014</span>\n ),\n },\n {\n accessorKey: 'is_active',\n header: t('customer_groups.groups.list.columns.isActive', 'Status'),\n cell: ({ row }) => (\n <StatusBadge variant={activeStatusMap[row.original.is_active ? 'active' : 'inactive']} dot>\n {row.original.is_active\n ? t('customer_groups.groups.list.status.active', 'Active')\n : t('customer_groups.groups.list.status.inactive', 'Inactive')}\n </StatusBadge>\n ),\n },\n {\n accessorKey: 'updated_at',\n header: t('customer_groups.groups.list.columns.updatedAt', 'Updated'),\n cell: ({ row }) => formatDisplayDateTime(row.original.updated_at, dateLocale) ?? '\u2014',\n },\n ],\n [t, dateLocale, dragReorderEnabled],\n )\n\n const filterDefs = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'isActive',\n label: t('customer_groups.groups.list.filters.status', 'Status'),\n type: 'select',\n options: [\n { label: t('customer_groups.groups.list.filters.all', 'All'), value: '' },\n { label: t('customer_groups.groups.list.filters.active', 'Active'), value: 'true' },\n { label: t('customer_groups.groups.list.filters.inactive', 'Inactive'), value: 'false' },\n ],\n },\n ],\n [t],\n )\n\n const table = (\n <DataTable\n title={t('customer_groups.groups.list.title', 'Customer groups')}\n titleHeadingLevel={1}\n columns={columns}\n data={rows}\n sortable={false}\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder={t('customer_groups.groups.list.searchPlaceholder', 'Search groups...')}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={() => handleFiltersApply({})}\n actions={\n canManage ? (\n <Button asChild>\n <Link href=\"/backend/customer-groups/create\">\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('customer_groups.groups.list.actions.create', 'Create group')}\n </Link>\n </Button>\n ) : undefined\n }\n rowActions={(row) => (\n <RowActions\n items={canManage ? [\n { id: 'edit', label: t('common.edit', 'Edit'), href: `/backend/customer-groups/${row.id}/edit` },\n { id: 'delete', label: t('common.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },\n ] : [\n { id: 'open', label: t('common.open', 'Open'), href: `/backend/customer-groups/${row.id}/edit` },\n ]}\n />\n )}\n emptyState={\n <ListEmptyState\n entityName={t('customer_groups.groups.list.title', 'Customer groups')}\n title={t('customer_groups.groups.list.empty.title', 'No customer groups yet')}\n description={t(\n 'customer_groups.groups.list.empty.description',\n 'Create your first group to start organizing customers.',\n )}\n createHref={canManage ? '/backend/customer-groups/create' : undefined}\n createLabel={t('customer_groups.groups.list.empty.createLabel', 'Create your first group')}\n />\n }\n error={loadError ? <ErrorMessage label={loadError} /> : null}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n totalIsCapped,\n onPageChange: setPage,\n }}\n isLoading={isLoading}\n />\n )\n\n return (\n <Page>\n <PageBody>\n {canManage ? <OrphanBanner /> : null}\n {showReorderUnavailableHint ? (\n <Alert status=\"information\" size=\"sm\" className=\"mb-4\">\n {t(\n 'customer_groups.groups.list.reorderUnavailable',\n 'Drag-to-reorder is off because there are more groups than fit on one page. Edit a group to change its priority.',\n )}\n </Alert>\n ) : null}\n {dragReorderEnabled ? (\n <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>\n <SortableContext items={rows.map((row) => row.id)} strategy={verticalListSortingStrategy}>\n {table}\n </SortableContext>\n </DndContext>\n ) : (\n table\n )}\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -15,14 +15,14 @@ const ChipButton = React.forwardRef(
15
15
  onClick,
16
16
  "aria-label": ariaLabel ?? (label ? `${label}: ${value}` : value),
17
17
  "aria-pressed": active,
18
- className: `inline-flex items-center gap-1.5 rounded-sm border border-border bg-card px-2.5 py-1.5 text-xs leading-normal transition-colors hover:bg-muted/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${active ? "border-foreground/40 bg-muted text-foreground" : "text-foreground"}`,
18
+ className: `inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-sm border border-border bg-card px-2.5 py-1.5 text-xs leading-normal transition-colors hover:bg-muted/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${active ? "border-foreground/40 bg-muted text-foreground" : "text-foreground"}`,
19
19
  children: [
20
- label ? /* @__PURE__ */ jsxs("span", { className: "font-normal text-muted-foreground", children: [
20
+ label ? /* @__PURE__ */ jsxs("span", { className: "shrink-0 font-normal text-muted-foreground", children: [
21
21
  label,
22
22
  ":"
23
23
  ] }) : null,
24
- /* @__PURE__ */ jsx("span", { className: label ? "font-semibold text-foreground" : "font-normal text-foreground", children: value }),
25
- withChevron ? /* @__PURE__ */ jsx(ChevronDown, { className: "size-3 text-muted-foreground", "aria-hidden": "true" }) : null
24
+ /* @__PURE__ */ jsx("span", { className: `min-w-0 truncate ${label ? "font-semibold text-foreground" : "font-normal text-foreground"}`, children: value }),
25
+ withChevron ? /* @__PURE__ */ jsx(ChevronDown, { className: "size-3 shrink-0 text-muted-foreground", "aria-hidden": "true" }) : null
26
26
  ]
27
27
  }
28
28
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/pipeline/components/ChipButton.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { ChevronDown } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\n\nexport type ChipButtonProps = {\n label?: string\n value: string\n active?: boolean\n ariaLabel?: string\n onClick?: () => void\n /** Show the chevron-down affordance \u2014 true for popover-backed chips, false for plain chips like \"+ More\" */\n withChevron?: boolean\n}\n\nexport const ChipButton = React.forwardRef<HTMLButtonElement, ChipButtonProps>(\n function ChipButton(\n { label, value, active = false, ariaLabel, onClick, withChevron = true },\n ref,\n ) {\n return (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n ref={ref}\n type=\"button\"\n onClick={onClick}\n aria-label={ariaLabel ?? (label ? `${label}: ${value}` : value)}\n aria-pressed={active}\n className={`inline-flex items-center gap-1.5 rounded-sm border border-border bg-card px-2.5 py-1.5 text-xs leading-normal transition-colors hover:bg-muted/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${\n active ? 'border-foreground/40 bg-muted text-foreground' : 'text-foreground'\n }`}\n >\n {label ? <span className=\"font-normal text-muted-foreground\">{label}:</span> : null}\n <span className={label ? 'font-semibold text-foreground' : 'font-normal text-foreground'}>\n {value}\n </span>\n {withChevron ? (\n <ChevronDown className=\"size-3 text-muted-foreground\" aria-hidden=\"true\" />\n ) : null}\n </Button>\n )\n },\n)\n\nexport default ChipButton\n"],
5
- "mappings": ";AAkCiB,SACT,KADS;AAhCjB,YAAY,WAAW;AACvB,SAAS,mBAAmB;AAC5B,SAAS,cAAc;AAYhB,MAAM,aAAa,MAAM;AAAA,EAC9B,SAASA,YACP,EAAE,OAAO,OAAO,SAAS,OAAO,WAAW,SAAS,cAAc,KAAK,GACvE,KACA;AACA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR,MAAK;AAAA,QACL;AAAA,QACA,MAAK;AAAA,QACL;AAAA,QACA,cAAY,cAAc,QAAQ,GAAG,KAAK,KAAK,KAAK,KAAK;AAAA,QACzD,gBAAc;AAAA,QACd,WAAW,yPACT,SAAS,kDAAkD,iBAC7D;AAAA,QAEC;AAAA,kBAAQ,qBAAC,UAAK,WAAU,qCAAqC;AAAA;AAAA,YAAM;AAAA,aAAC,IAAU;AAAA,UAC/E,oBAAC,UAAK,WAAW,QAAQ,kCAAkC,+BACxD,iBACH;AAAA,UACC,cACC,oBAAC,eAAY,WAAU,gCAA+B,eAAY,QAAO,IACvE;AAAA;AAAA;AAAA,IACN;AAAA,EAEJ;AACF;AAEA,IAAO,qBAAQ;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { ChevronDown } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\n\nexport type ChipButtonProps = {\n label?: string\n value: string\n active?: boolean\n ariaLabel?: string\n onClick?: () => void\n /** Show the chevron-down affordance \u2014 true for popover-backed chips, false for plain chips like \"+ More\" */\n withChevron?: boolean\n}\n\nexport const ChipButton = React.forwardRef<HTMLButtonElement, ChipButtonProps>(\n function ChipButton(\n { label, value, active = false, ariaLabel, onClick, withChevron = true },\n ref,\n ) {\n return (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n ref={ref}\n type=\"button\"\n onClick={onClick}\n aria-label={ariaLabel ?? (label ? `${label}: ${value}` : value)}\n aria-pressed={active}\n className={`inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-sm border border-border bg-card px-2.5 py-1.5 text-xs leading-normal transition-colors hover:bg-muted/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${\n active ? 'border-foreground/40 bg-muted text-foreground' : 'text-foreground'\n }`}\n >\n {label ? <span className=\"shrink-0 font-normal text-muted-foreground\">{label}:</span> : null}\n <span className={`min-w-0 truncate ${label ? 'font-semibold text-foreground' : 'font-normal text-foreground'}`}>\n {value}\n </span>\n {withChevron ? (\n <ChevronDown className=\"size-3 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n ) : null}\n </Button>\n )\n },\n)\n\nexport default ChipButton\n"],
5
+ "mappings": ";AAkCiB,SACT,KADS;AAhCjB,YAAY,WAAW;AACvB,SAAS,mBAAmB;AAC5B,SAAS,cAAc;AAYhB,MAAM,aAAa,MAAM;AAAA,EAC9B,SAASA,YACP,EAAE,OAAO,OAAO,SAAS,OAAO,WAAW,SAAS,cAAc,KAAK,GACvE,KACA;AACA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR,MAAK;AAAA,QACL;AAAA,QACA,MAAK;AAAA,QACL;AAAA,QACA,cAAY,cAAc,QAAQ,GAAG,KAAK,KAAK,KAAK,KAAK;AAAA,QACzD,gBAAc;AAAA,QACd,WAAW,4QACT,SAAS,kDAAkD,iBAC7D;AAAA,QAEC;AAAA,kBAAQ,qBAAC,UAAK,WAAU,8CAA8C;AAAA;AAAA,YAAM;AAAA,aAAC,IAAU;AAAA,UACxF,oBAAC,UAAK,WAAW,oBAAoB,QAAQ,kCAAkC,6BAA6B,IACzG,iBACH;AAAA,UACC,cACC,oBAAC,eAAY,WAAU,yCAAwC,eAAY,QAAO,IAChF;AAAA;AAAA;AAAA,IACN;AAAA,EAEJ;AACF;AAEA,IAAO,qBAAQ;",
6
6
  "names": ["ChipButton"]
7
7
  }