@open-mercato/core 0.6.6-develop.6198.1.0822f97cc6 → 0.6.6-develop.6204.1.30b1f58642

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +52 -45
  2. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  3. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +9 -13
  4. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
  5. package/dist/modules/customers/components/detail/ActivitiesSection.js +17 -12
  6. package/dist/modules/customers/components/detail/ActivitiesSection.js.map +2 -2
  7. package/dist/modules/customers/components/detail/ActivityHistorySection.js +11 -7
  8. package/dist/modules/customers/components/detail/ActivityHistorySection.js.map +2 -2
  9. package/dist/modules/dictionaries/components/DictionariesManager.js +75 -33
  10. package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
  11. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js +95 -43
  12. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js.map +2 -2
  13. package/dist/modules/dictionaries/components/DictionarySelectControl.js +40 -19
  14. package/dist/modules/dictionaries/components/DictionarySelectControl.js.map +2 -2
  15. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +81 -37
  16. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
  17. package/dist/modules/integrations/backend/integrations/page.js +45 -24
  18. package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
  19. package/dist/modules/notifications/api/[id]/action/route.js +18 -3
  20. package/dist/modules/notifications/api/[id]/action/route.js.map +2 -2
  21. package/dist/modules/notifications/api/[id]/restore/route.js +19 -3
  22. package/dist/modules/notifications/api/[id]/restore/route.js.map +2 -2
  23. package/dist/modules/notifications/api/mark-all-read/route.js +18 -4
  24. package/dist/modules/notifications/api/mark-all-read/route.js.map +2 -2
  25. package/dist/modules/notifications/api/route.js +17 -4
  26. package/dist/modules/notifications/api/route.js.map +2 -2
  27. package/dist/modules/notifications/api/settings/route.js +26 -5
  28. package/dist/modules/notifications/api/settings/route.js.map +2 -2
  29. package/dist/modules/notifications/lib/routeHelpers.js +64 -5
  30. package/dist/modules/notifications/lib/routeHelpers.js.map +2 -2
  31. package/package.json +7 -7
  32. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +58 -48
  33. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +14 -13
  34. package/src/modules/customers/components/detail/ActivitiesSection.tsx +22 -13
  35. package/src/modules/customers/components/detail/ActivityHistorySection.tsx +13 -7
  36. package/src/modules/dictionaries/components/DictionariesManager.tsx +82 -34
  37. package/src/modules/dictionaries/components/DictionaryEntriesEditor.tsx +99 -42
  38. package/src/modules/dictionaries/components/DictionarySelectControl.tsx +43 -18
  39. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +78 -35
  40. package/src/modules/integrations/backend/integrations/page.tsx +42 -21
  41. package/src/modules/notifications/api/[id]/action/route.ts +17 -2
  42. package/src/modules/notifications/api/[id]/restore/route.ts +19 -3
  43. package/src/modules/notifications/api/mark-all-read/route.ts +18 -4
  44. package/src/modules/notifications/api/route.ts +16 -3
  45. package/src/modules/notifications/api/settings/route.ts +26 -5
  46. package/src/modules/notifications/lib/routeHelpers.ts +101 -4
@@ -173,8 +173,13 @@ function EditVariantPage({ params }) {
173
173
  const resolvedProductId = typeof record.product_id === "string" ? record.product_id : typeof record.productId === "string" ? record.productId : currentProductId;
174
174
  if (resolvedProductId) setCurrentProductId(resolvedProductId);
175
175
  const metadata = typeof record.metadata === "object" && record.metadata ? { ...record.metadata } : {};
176
- const attachments = await fetchVariantAttachments(variantId);
177
- const priceDrafts = await loadVariantPrices(variantId, priceKinds);
176
+ const [attachments, priceDrafts, productRes] = await Promise.all([
177
+ fetchVariantAttachments(variantId),
178
+ loadVariantPrices(variantId, priceKinds),
179
+ resolvedProductId ? apiCall(
180
+ `/api/catalog/products?id=${encodeURIComponent(resolvedProductId)}&page=1&pageSize=1`
181
+ ) : Promise.resolve(null)
182
+ ]);
178
183
  const priceIdMap = {};
179
184
  const priceVersionMap = {};
180
185
  Object.entries(priceDrafts).forEach(([kindId, draft]) => {
@@ -188,10 +193,7 @@ function EditVariantPage({ params }) {
188
193
  const customDefaults = extractCustomFieldEntries(record);
189
194
  let loadedOptionDefinitions = [];
190
195
  if (resolvedProductId) {
191
- const productRes = await apiCall(
192
- `/api/catalog/products?id=${encodeURIComponent(resolvedProductId)}&page=1&pageSize=1`
193
- );
194
- if (productRes.ok) {
196
+ if (productRes?.ok) {
195
197
  const product = Array.isArray(productRes.result?.items) ? productRes.result?.items?.[0] : void 0;
196
198
  if (product) {
197
199
  setProductTitle(typeof product.title === "string" ? product.title : "");
@@ -222,12 +224,6 @@ function EditVariantPage({ params }) {
222
224
  }
223
225
  }
224
226
  const variantTaxRateId = typeof record.tax_rate_id === "string" ? record.tax_rate_id : typeof record.taxRateId === "string" ? record.taxRateId : null;
225
- if (variantTaxRateId) {
226
- const selectedVariantTaxRate = await fetchTaxRateById(variantTaxRateId).catch(() => null);
227
- if (selectedVariantTaxRate && !cancelled) {
228
- setTaxRates((current) => mergeTaxRateSummaries(current, selectedVariantTaxRate));
229
- }
230
- }
231
227
  if (!cancelled) {
232
228
  const optionValues = typeof record.option_values === "object" && record.option_values ? { ...record.option_values } : typeof record.optionValues === "object" && record.optionValues ? { ...record.optionValues } : {};
233
229
  const normalizedOptionValues = reconcileOptionValues(optionValues, loadedOptionDefinitions);
@@ -271,7 +267,7 @@ function EditVariantPage({ params }) {
271
267
  return () => {
272
268
  cancelled = true;
273
269
  };
274
- }, [variantId, t, currentProductId, fetchTaxRateById, priceKinds]);
270
+ }, [variantId, t, currentProductId, priceKinds]);
275
271
  const groups = React.useMemo(() => {
276
272
  const list = [
277
273
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../../src/modules/catalog/backend/catalog/products/%5BproductId%5D/variants/%5BvariantId%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { createCrud, updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { apiCall, readApiResultOrThrow, 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 { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { E } from '#generated/entities.ids.generated'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport {\n type VariantFormValues,\n type VariantPriceDraft,\n type OptionDefinition,\n createVariantInitialValues,\n normalizeOptionSchema,\n mapPriceItemToDraft,\n findInvalidVariantPriceKinds,\n} from '@open-mercato/core/modules/catalog/components/products/variantForm'\nimport {\n type PriceKindSummary,\n type PriceKindApiPayload,\n type TaxRateSummary,\n normalizePriceKindSummary,\n normalizeTaxRateSummary,\n mergeTaxRateSummaries,\n} from '@open-mercato/core/modules/catalog/components/products/productForm'\nimport { parseNumericInput } from '@open-mercato/core/modules/catalog/components/products/productFormUtils'\nimport {\n VariantBasicsSection,\n VariantOptionValuesSection,\n VariantDimensionsSection,\n VariantMetadataSection,\n VariantPricesSection,\n VariantMediaSection,\n} from '@open-mercato/core/modules/catalog/components/products/VariantBuilder'\nimport type { ProductMediaItem } from '@open-mercato/core/modules/catalog/components/products/ProductMediaManager'\nimport { buildAttachmentImageUrl, slugifyAttachmentFileName } from '@open-mercato/core/modules/attachments/lib/imageUrls'\nimport { fetchOptionSchemaTemplate } from '../../../optionSchemaClient'\nimport CreateVariantPage from '../create/page'\n\ntype VariantResponse = {\n items?: Array<Record<string, unknown>>\n}\n\ntype ProductResponse = {\n items?: Array<{\n id?: string\n title?: string | null\n metadata?: Record<string, unknown> | null\n tax_rate_id?: string | null\n taxRateId?: string | null\n tax_rate?: number | string | null\n taxRate?: number | string | null\n }>\n}\n\ntype PriceListResponse = {\n items?: Array<Record<string, unknown>>\n}\n\ntype AttachmentListResponse = {\n items?: ProductMediaItem[]\n}\n\nexport function handleVariantDeleteError(\n err: unknown,\n t: (key: string, fallback?: string) => string,\n): void {\n if (surfaceRecordConflict(err, t)) return\n const message = err instanceof Error && err.message\n ? err.message\n : t('catalog.variants.form.deleteError', 'Failed to delete variant.')\n flash(message, 'error')\n}\n\nfunction resolveVariantPriceLabel(prices: Record<string, VariantPriceDraft> | undefined): string | null {\n if (!prices || typeof prices !== 'object') return null\n const entries = Object.values(prices)\n for (const entry of entries) {\n const amount = typeof entry?.amount === 'string' ? entry.amount.trim() : ''\n if (!amount) continue\n const currencyCode =\n typeof entry.currencyCode === 'string' && entry.currencyCode.trim().length\n ? entry.currencyCode.trim().toUpperCase()\n : null\n return currencyCode ? `${currencyCode} ${amount}` : amount\n }\n return null\n}\n\nexport default function EditVariantPage({ params }: { params?: { productId?: string; variantId?: string } }) {\n const router = useRouter()\n const t = useT()\n const productId = params?.productId ? String(params.productId) : null\n const variantId = params?.variantId ? String(params.variantId) : null\n const isCreateSentinel = variantId === 'create'\n const [priceKinds, setPriceKinds] = React.useState<PriceKindSummary[]>([])\n const [taxRates, setTaxRates] = React.useState<TaxRateSummary[]>([])\n const [optionDefinitions, setOptionDefinitions] = React.useState<OptionDefinition[]>([])\n const [initialValues, setInitialValues] = React.useState<VariantFormValues | null>(null)\n const [existingPriceIds, setExistingPriceIds] = React.useState<Record<string, string>>({})\n // price-kind id \u2192 loaded price `updatedAt`, so the price sync sends each price's\n // own optimistic-lock version (the variant CrudForm submit scope would otherwise\n // leak the variant's version onto the catalog/prices guard) (#2055).\n const [existingPriceVersions, setExistingPriceVersions] = React.useState<Record<string, string | null>>({})\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n const [currentProductId, setCurrentProductId] = React.useState<string | null>(productId)\n const [productTitle, setProductTitle] = React.useState<string>('')\n const [productTaxRateId, setProductTaxRateId] = React.useState<string | null>(null)\n const [productTaxRate, setProductTaxRate] = React.useState<number | null>(null)\n\n const parseTaxRateSummary = React.useCallback(\n (item: Record<string, unknown>) =>\n normalizeTaxRateSummary(\n item,\n t('catalog.products.create.taxRates.unnamed', 'Untitled tax rate'),\n ),\n [t],\n )\n\n const fetchTaxRateById = React.useCallback(\n async (taxRateId: string): Promise<TaxRateSummary | null> => {\n const payload = await readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n `/api/sales/tax-rates?id=${encodeURIComponent(taxRateId)}&pageSize=1`,\n undefined,\n { errorMessage: t('catalog.products.create.taxRates.error', 'Failed to load tax rates.'), fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n return (\n items\n .map((item) => parseTaxRateSummary(item))\n .find((item): item is TaxRateSummary => item?.id === taxRateId) ?? null\n )\n },\n [parseTaxRateSummary, t],\n )\n\n React.useEffect(() => {\n const loadPriceKinds = async () => {\n try {\n const payload = await readApiResultOrThrow<{ items?: PriceKindApiPayload[] }>(\n '/api/catalog/price-kinds?pageSize=100',\n undefined,\n { errorMessage: t('catalog.priceKinds.errors.load', 'Failed to load price kinds.') },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setPriceKinds(items.map((item) => normalizePriceKindSummary(item)).filter((item): item is PriceKindSummary => !!item))\n } catch (err) {\n console.error('catalog.price-kinds.fetch failed', err)\n setPriceKinds([])\n }\n }\n loadPriceKinds().catch(() => {})\n }, [t])\n\n React.useEffect(() => {\n const loadTaxRates = async () => {\n try {\n const payload = await readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n '/api/sales/tax-rates?pageSize=200',\n undefined,\n { errorMessage: t('catalog.products.create.taxRates.error', 'Failed to load tax rates.'), fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setTaxRates(\n items\n .map((item) => parseTaxRateSummary(item))\n .filter((item): item is TaxRateSummary => item !== null),\n )\n } catch (err) {\n console.error('sales.tax-rates.fetch failed', err)\n setTaxRates([])\n }\n }\n loadTaxRates().catch(() => {})\n }, [parseTaxRateSummary, t])\n\n React.useEffect(() => {\n const ids = [initialValues?.taxRateId, productTaxRateId].filter(\n (value): value is string => typeof value === 'string' && value.length > 0,\n )\n const missingIds = ids.filter((id) => !taxRates.some((rate) => rate.id === id))\n if (!missingIds.length) return\n Promise.all(missingIds.map((id) => fetchTaxRateById(id).catch(() => null)))\n .then((selectedRates) => {\n setTaxRates((current) =>\n selectedRates.reduce(\n (next, selected) => mergeTaxRateSummaries(next, selected),\n current,\n ),\n )\n })\n .catch(() => {})\n }, [fetchTaxRateById, initialValues?.taxRateId, productTaxRateId, taxRates])\n\n React.useEffect(() => {\n if (!variantId || isCreateSentinel || priceKinds.length === 0) return\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n setIsNotFound(false)\n try {\n const variantRes = await apiCall<VariantResponse>(\n `/api/catalog/variants?id=${encodeURIComponent(variantId!)}&page=1&pageSize=1`,\n )\n if (!variantRes.ok) {\n if (variantRes.status === 404) {\n if (!cancelled) setIsNotFound(true)\n return\n }\n throw new Error(t('catalog.variants.form.errors.load', 'Failed to load variant.'))\n }\n const record = Array.isArray(variantRes.result?.items) ? variantRes.result?.items?.[0] : undefined\n if (!record) {\n if (!cancelled) setIsNotFound(true)\n return\n }\n const resolvedProductId =\n typeof record.product_id === 'string'\n ? record.product_id\n : typeof record.productId === 'string'\n ? record.productId\n : currentProductId\n if (resolvedProductId) setCurrentProductId(resolvedProductId)\n const metadata = typeof record.metadata === 'object' && record.metadata ? { ...(record.metadata as Record<string, unknown>) } : {}\n const attachments = await fetchVariantAttachments(variantId!)\n const priceDrafts = await loadVariantPrices(variantId!, priceKinds)\n const priceIdMap: Record<string, string> = {}\n const priceVersionMap: Record<string, string | null> = {}\n Object.entries(priceDrafts).forEach(([kindId, draft]) => {\n if (draft.priceId) {\n priceIdMap[kindId] = draft.priceId\n priceVersionMap[kindId] = draft.updatedAt ?? null\n }\n })\n setExistingPriceIds(priceIdMap)\n setExistingPriceVersions(priceVersionMap)\n const customDefaults = extractCustomFieldEntries(record)\n let loadedOptionDefinitions: OptionDefinition[] = []\n if (resolvedProductId) {\n const productRes = await apiCall<ProductResponse>(\n `/api/catalog/products?id=${encodeURIComponent(resolvedProductId)}&page=1&pageSize=1`,\n )\n if (productRes.ok) {\n const product = Array.isArray(productRes.result?.items) ? productRes.result?.items?.[0] : undefined\n if (product) {\n setProductTitle(typeof product.title === 'string' ? product.title : '')\n const taxRateId =\n typeof (product as any).tax_rate_id === 'string'\n ? (product as any).tax_rate_id\n : typeof (product as any).taxRateId === 'string'\n ? (product as any).taxRateId\n : null\n const taxRateValueRaw =\n typeof (product as any).tax_rate === 'number'\n ? (product as any).tax_rate\n : typeof (product as any).tax_rate === 'string'\n ? Number((product as any).tax_rate)\n : typeof (product as any).taxRate === 'number'\n ? (product as any).taxRate\n : typeof (product as any).taxRate === 'string'\n ? Number((product as any).taxRate)\n : null\n const taxRateValue = Number.isFinite(taxRateValueRaw) ? Number(taxRateValueRaw) : null\n setProductTaxRateId(taxRateId)\n setProductTaxRate(taxRateValue)\n const productMetadata = (product.metadata ?? {}) as Record<string, unknown>\n const optionSchemaId =\n typeof (product as any).option_schema_id === 'string'\n ? (product as any).option_schema_id\n : typeof (product as any).optionSchemaId === 'string'\n ? (product as any).optionSchemaId\n : null\n let schemaSource: unknown =\n productMetadata.optionSchema ?? (productMetadata.option_schema as unknown)\n if (optionSchemaId) {\n const template = await fetchOptionSchemaTemplate(optionSchemaId)\n if (template?.schema?.options) {\n schemaSource = template.schema.options.map((option) => ({\n code: option.code,\n label: option.label,\n values: Array.isArray(option.choices)\n ? option.choices.map((choice) => ({\n id: choice.code ?? undefined,\n label: choice.label ?? choice.code ?? '',\n }))\n : [],\n }))\n }\n }\n loadedOptionDefinitions = normalizeOptionSchema(schemaSource)\n setOptionDefinitions(loadedOptionDefinitions)\n }\n }\n }\n const variantTaxRateId =\n typeof (record as any).tax_rate_id === 'string'\n ? (record as any).tax_rate_id\n : typeof (record as any).taxRateId === 'string'\n ? (record as any).taxRateId\n : null\n if (variantTaxRateId) {\n const selectedVariantTaxRate = await fetchTaxRateById(variantTaxRateId).catch(() => null)\n if (selectedVariantTaxRate && !cancelled) {\n setTaxRates((current) => mergeTaxRateSummaries(current, selectedVariantTaxRate))\n }\n }\n if (!cancelled) {\n const optionValues =\n typeof record.option_values === 'object' && record.option_values\n ? { ...(record.option_values as Record<string, string>) }\n : typeof record.optionValues === 'object' && record.optionValues\n ? { ...(record.optionValues as Record<string, string>) }\n : {}\n const normalizedOptionValues = reconcileOptionValues(optionValues, loadedOptionDefinitions)\n const defaultMediaId =\n typeof record.default_media_id === 'string'\n ? record.default_media_id\n : typeof record.defaultMediaId === 'string'\n ? record.defaultMediaId\n : attachments[0]?.id ?? null\n const defaultMediaUrl =\n typeof record.default_media_url === 'string'\n ? record.default_media_url\n : typeof record.defaultMediaUrl === 'string'\n ? record.defaultMediaUrl\n : ''\n const base = createVariantInitialValues()\n setInitialValues({\n ...base,\n id: variantId!,\n mediaDraftId: variantId!,\n name: typeof record.name === 'string' ? record.name : '',\n sku: typeof record.sku === 'string' ? record.sku : '',\n barcode: typeof record.barcode === 'string' ? record.barcode : '',\n gtinType:\n typeof record.gtin_type === 'string'\n ? record.gtin_type\n : typeof record.gtinType === 'string'\n ? record.gtinType\n : null,\n hsCode:\n typeof record.hs_code === 'string'\n ? record.hs_code\n : typeof record.hsCode === 'string'\n ? record.hsCode\n : '',\n isDefault: record.is_default === true || record.isDefault === true,\n isActive: record.is_active !== false && record.isActive !== false,\n optionValues: normalizedOptionValues,\n metadata,\n mediaItems: attachments,\n defaultMediaId,\n defaultMediaUrl,\n prices: priceDrafts,\n taxRateId: variantTaxRateId,\n customFieldsetCode:\n typeof record.custom_fieldset_code === 'string'\n ? record.custom_fieldset_code\n : typeof record.customFieldsetCode === 'string'\n ? record.customFieldsetCode\n : null,\n updatedAt:\n typeof record.updatedAt === 'string'\n ? record.updatedAt\n : typeof record.updated_at === 'string'\n ? record.updated_at\n : null,\n ...customDefaults,\n })\n }\n } catch (err) {\n console.error('catalog.variants.load.failed', err)\n if (!cancelled) {\n const message = err instanceof Error && err.message ? err.message : t('catalog.variants.form.errors.load', 'Failed to load variant.')\n setError(message)\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [variantId, t, currentProductId, fetchTaxRateById, priceKinds])\n\n const groups = React.useMemo<CrudFormGroup[]>(() => {\n const list: CrudFormGroup[] = [\n {\n id: 'general',\n column: 1,\n title: t('catalog.variants.form.general', 'General'),\n component: ({ values, setValue, errors }) => (\n <VariantBasicsSection values={values as VariantFormValues} setValue={setValue} errors={errors} />\n ),\n },\n {\n id: 'metadata',\n column: 1,\n title: t('catalog.products.edit.metadata.title', 'Metadata'),\n description: t('catalog.products.edit.metadata.hint', 'Attach structured key/value pairs for integrations.'),\n component: ({ values, setValue }) => (\n <VariantMetadataSection values={values as VariantFormValues} setValue={setValue} showIntro={false} embedded />\n ),\n },\n {\n id: 'prices',\n column: 1,\n title: t('catalog.variants.form.pricesLabel', 'Prices'),\n description: t('catalog.variants.form.pricesHint', 'Populate list prices per price kind.'),\n component: ({ values, setValue }) => (\n <VariantPricesSection\n values={values as VariantFormValues}\n setValue={setValue}\n priceKinds={priceKinds}\n taxRates={taxRates}\n showHeader={false}\n embedded\n />\n ),\n },\n {\n id: 'media',\n column: 1,\n title: t('catalog.variants.form.media', 'Media'),\n component: ({ values, setValue }) => (\n <VariantMediaSection values={values as VariantFormValues} setValue={setValue} showLabel={false} />\n ),\n },\n ]\n\n if (optionDefinitions.length) {\n list.push({\n id: 'options',\n column: 2,\n title: t('catalog.variants.form.options', 'Option values'),\n component: ({ values, setValue }) => (\n <VariantOptionValuesSection\n values={values as VariantFormValues}\n setValue={setValue}\n optionDefinitions={optionDefinitions}\n showHeading={false}\n />\n ),\n })\n }\n\n list.push({\n id: 'dimensions',\n column: 2,\n title: t('catalog.variants.form.dimensions', 'Dimensions & weight'),\n component: ({ values, setValue }) => (\n <VariantDimensionsSection values={values as VariantFormValues} setValue={setValue} showHeading={false} />\n ),\n })\n\n list.push({\n id: 'custom',\n column: 2,\n title: t('catalog.variants.form.customFields', 'Custom attributes'),\n kind: 'customFields',\n })\n\n return list\n }, [optionDefinitions, priceKinds, t, taxRates])\n\n if (isCreateSentinel) {\n if (!productId) {\n return (\n <Page>\n <PageBody>\n <div className=\"rounded border border-destructive/40 bg-destructive/10 px-4 py-3 text-sm text-destructive\">\n {t('catalog.variants.form.errors.productMissing', 'Product identifier is missing.')}\n </div>\n </PageBody>\n </Page>\n )\n }\n return <CreateVariantPage params={{ productId }} />\n }\n\n if (!variantId || !currentProductId) {\n return (\n <Page>\n <PageBody>\n <div className=\"rounded border border-destructive/40 bg-destructive/10 px-4 py-3 text-sm text-destructive\">\n {t('catalog.variants.form.errors.variantMissing', 'Variant identifier is missing.')}\n </div>\n </PageBody>\n </Page>\n )\n }\n\n const formTitle = productTitle\n ? t('catalog.variants.form.editTitleFor', 'Edit variant \u2022 {{title}}').replace('{{title}}', productTitle)\n : t('catalog.variants.form.editTitle', 'Edit variant')\n const productVariantsHref = `/backend/catalog/products/${currentProductId}#variants`\n\n // When the variant was deleted (e.g. concurrently in another tab) the GET\n // returns no record. Render a dedicated not-found state with a recovery link\n // instead of an empty CrudForm that throws runtime errors (#2055 QA).\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('catalog.variants.form.errors.notFound', 'Variant not found.')}\n backHref={productVariantsHref}\n backLabel={t('catalog.variants.form.actions.backToProduct', 'Back to product variants')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error && !loading) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <CrudForm<VariantFormValues>\n title={formTitle}\n backHref={productVariantsHref}\n versionHistory={{ resourceKind: 'catalog.variant', resourceId: variantId ? String(variantId) : '' }}\n extraActions={(\n <SendObjectMessageDialog\n object={{\n entityModule: 'catalog',\n entityType: 'variant',\n entityId: variantId,\n previewData: {\n title:\n (typeof initialValues?.name === 'string' && initialValues.name.trim().length\n ? initialValues.name\n : variantId),\n metadata: {\n [t('catalog.variants.form.skuLabel')]:\n (typeof initialValues?.sku === 'string' && initialValues.sku.trim().length\n ? initialValues.sku\n : '-'),\n [t('catalog.variants.form.pricesLabel')]:\n resolveVariantPriceLabel(initialValues?.prices) ?? '-',\n },\n },\n }}\n viewHref={`/backend/catalog/products/${currentProductId}/variants/${variantId}`}\n />\n )}\n fields={[]}\n groups={groups}\n entityId={E.catalog.catalog_product_variant}\n customFieldsetBindings={{ [E.catalog.catalog_product_variant]: { valueKey: 'customFieldsetCode' } }}\n initialValues={initialValues ?? undefined}\n isLoading={loading}\n loadingMessage={t('catalog.variants.form.loading', 'Loading variant...')}\n submitLabel={t('catalog.variants.form.save', 'Save changes')}\n cancelHref={productVariantsHref}\n onSubmit={async (values) => {\n const name = values.name?.trim()\n if (!name) {\n const message = t('catalog.variants.form.errors.nameRequired', 'Provide the variant name.')\n throw createCrudFormError(message, { name: message })\n }\n const invalidPriceKinds = findInvalidVariantPriceKinds(priceKinds, values.prices)\n if (invalidPriceKinds.length) {\n const message = t('catalog.variants.form.errors.invalidPrice', 'Provide a valid non-negative price.')\n throw createCrudFormError(message, { prices: message })\n }\n const resolveTaxRateValue = (taxRateId?: string | null) => {\n if (!taxRateId) return null\n const match = taxRates.find((rate) => rate.id === taxRateId)\n return typeof match?.rate === 'number' && Number.isFinite(match.rate) ? match.rate : null\n }\n const resolvedTaxRateId = values.taxRateId ?? productTaxRateId ?? null\n const resolvedTaxRateValue =\n values.taxRateId && resolvedTaxRateId\n ? resolveTaxRateValue(resolvedTaxRateId)\n : productTaxRateId\n ? resolveTaxRateValue(productTaxRateId) ?? productTaxRate\n : productTaxRate ?? null\n const metadata = typeof values.metadata === 'object' && values.metadata ? { ...values.metadata } : {}\n const defaultMediaEntry = values.defaultMediaId\n ? (Array.isArray(values.mediaItems) ? values.mediaItems : []).find((item) => item.id === values.defaultMediaId)\n : null\n const defaultMediaUrl = defaultMediaEntry\n ? buildAttachmentImageUrl(defaultMediaEntry.id, {\n slug: slugifyAttachmentFileName(defaultMediaEntry.fileName),\n })\n : null\n const payload: Record<string, unknown> = {\n id: variantId,\n productId: currentProductId,\n name,\n sku: values.sku?.trim() || undefined,\n barcode: values.barcode?.trim() || undefined,\n gtinType: values.gtinType ?? null,\n hsCode: values.hsCode?.trim() || null,\n isDefault: Boolean(values.isDefault),\n isActive: values.isActive !== false,\n optionValues: Object.keys(values.optionValues ?? {}).length ? values.optionValues : undefined,\n metadata,\n defaultMediaId: values.defaultMediaId ?? undefined,\n defaultMediaUrl: defaultMediaUrl ?? undefined,\n customFieldsetCode: values.customFieldsetCode?.trim().length ? values.customFieldsetCode : undefined,\n taxRateId: resolvedTaxRateId,\n taxRate: resolvedTaxRateValue,\n }\n const customFields = collectCustomFieldValues(values)\n if (Object.keys(customFields).length) payload.customFields = customFields\n\n await updateCrud('catalog/variants', payload)\n await syncVariantPricesUpdate({\n priceKinds,\n priceDrafts: values.prices ?? {},\n existingPriceIds,\n existingPriceVersions,\n productId: currentProductId,\n variantId,\n taxRates,\n taxRateId: values.taxRateId,\n productTaxRateId,\n productTaxRate,\n })\n flash(t('catalog.variants.form.updated', 'Variant updated.'), 'success')\n router.push(productVariantsHref)\n }}\n onDelete={async () => {\n try {\n await deleteCrud('catalog/variants', variantId!, {\n errorMessage: t('catalog.variants.form.deleteError', 'Failed to delete variant.'),\n })\n } catch (err) {\n handleVariantDeleteError(err, t)\n throw err\n }\n flash(t('catalog.variants.form.deleted', 'Variant deleted.'), 'success')\n router.push(productVariantsHref)\n }}\n deleteRedirect={productVariantsHref}\n />\n </PageBody>\n </Page>\n )\n}\n\nfunction reconcileOptionValues(\n optionValues: Record<string, string>,\n optionDefinitions: OptionDefinition[],\n): Record<string, string> {\n if (!optionValues || !optionDefinitions.length) {\n return optionValues ?? {}\n }\n const remaining = new Map(Object.entries(optionValues))\n const normalized: Record<string, string> = {}\n\n for (const option of optionDefinitions) {\n const code = option.code?.trim()\n if (!code) continue\n if (remaining.has(code)) {\n const value = remaining.get(code)\n if (value !== undefined) {\n normalized[code] = value\n }\n remaining.delete(code)\n continue\n }\n const matchKey = findOptionKeyByValue(remaining, option.values)\n if (matchKey) {\n const value = remaining.get(matchKey)\n if (value !== undefined) {\n normalized[code] = value\n }\n remaining.delete(matchKey)\n }\n }\n\n remaining.forEach((value, key) => {\n if (normalized[key] === undefined) {\n normalized[key] = value\n }\n })\n\n return normalized\n}\n\nfunction findOptionKeyByValue(\n candidates: Map<string, string>,\n optionValues: { id: string; label: string }[],\n): string | null {\n if (!optionValues.length) return null\n const matches: string[] = []\n candidates.forEach((value, key) => {\n if (optionValues.some((entry) => entry.label === value)) {\n matches.push(key)\n }\n })\n return matches.length === 1 ? matches[0] : null\n}\n\nasync function fetchVariantAttachments(variantId: string): Promise<ProductMediaItem[]> {\n try {\n const res = await apiCall<AttachmentListResponse>(\n `/api/attachments?entityId=${encodeURIComponent(E.catalog.catalog_product_variant)}&recordId=${encodeURIComponent(variantId)}`,\n )\n if (!res.ok) return []\n return Array.isArray(res.result?.items) ? res.result?.items ?? [] : []\n } catch (err) {\n console.error('catalog.variants.attachments.load', err)\n return []\n }\n}\n\nasync function loadVariantPrices(variantId: string, priceKinds: PriceKindSummary[]): Promise<Record<string, VariantPriceDraft>> {\n const kindDisplayModes = new Map(priceKinds.map((k) => [k.id, k.displayMode]))\n const drafts: Record<string, VariantPriceDraft> = {}\n const pageSize = 100\n let page = 1\n try {\n while (true) {\n const res = await apiCall<PriceListResponse>(\n `/api/catalog/prices?variantId=${encodeURIComponent(variantId)}&page=${page}&pageSize=${pageSize}`,\n )\n if (!res.ok) break\n const items = Array.isArray(res.result?.items) ? res.result?.items : []\n for (const item of items) {\n const draft = mapPriceItemToDraft(item as Record<string, unknown>, kindDisplayModes)\n if (draft) drafts[draft.priceKindId] = draft\n }\n if (items.length < pageSize) break\n page += 1\n }\n } catch (err) {\n console.error('catalog.variants.prices.load', err)\n }\n return drafts\n}\n\nasync function syncVariantPricesUpdate({\n priceKinds,\n priceDrafts,\n existingPriceIds,\n existingPriceVersions,\n productId,\n variantId,\n taxRates,\n taxRateId,\n productTaxRateId,\n productTaxRate,\n}: {\n priceKinds: PriceKindSummary[]\n priceDrafts: Record<string, VariantPriceDraft>\n existingPriceIds: Record<string, string>\n existingPriceVersions: Record<string, string | null>\n productId: string\n variantId: string\n taxRates: TaxRateSummary[]\n taxRateId: string | null\n productTaxRateId?: string | null\n productTaxRate?: number | null\n}): Promise<void> {\n const selectedTaxRate = taxRates.find((rate) => rate.id === taxRateId) ?? null\n const fallbackProductTaxRate =\n !selectedTaxRate && productTaxRateId\n ? taxRates.find((rate) => rate.id === productTaxRateId) ?? null\n : null\n const resolvedTaxRateValue =\n selectedTaxRate?.rate ??\n fallbackProductTaxRate?.rate ??\n (Number.isFinite(productTaxRate ?? null) ? productTaxRate ?? null : null)\n const resolvedTaxRateId = (selectedTaxRate ?? fallbackProductTaxRate)?.id ?? null\n for (const kind of priceKinds) {\n const draft = priceDrafts?.[kind.id]\n const amount = typeof draft?.amount === 'string' ? draft.amount.trim() : ''\n const existingId = draft?.priceId ?? existingPriceIds[kind.id]\n // The price's own version \u2014 overrides the variant header the parent CrudForm\n // submit scope put on the stack, so the catalog/prices guard compares the\n // right row (otherwise a stale/false 409). #2055.\n const lockVersion = draft?.updatedAt ?? existingPriceVersions[kind.id] ?? null\n if (!amount) {\n if (existingId) {\n try {\n await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(lockVersion),\n () => deleteCrud('catalog/prices', existingId),\n )\n } catch (err) {\n console.error('catalog.prices.delete', err)\n }\n }\n continue\n }\n const numeric = parseNumericInput(amount)\n if (!Number.isFinite(numeric) || numeric < 0) continue\n const payload: Record<string, unknown> = {\n productId,\n variantId,\n priceKindId: kind.id,\n currencyCode: kind.currencyCode ?? undefined,\n }\n if (resolvedTaxRateId) payload.taxRateId = resolvedTaxRateId\n else if (typeof resolvedTaxRateValue === 'number' && Number.isFinite(resolvedTaxRateValue)) payload.taxRate = resolvedTaxRateValue\n if (kind.displayMode === 'including-tax') payload.unitPriceGross = numeric\n else payload.unitPriceNet = numeric\n if (existingId) {\n await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(lockVersion),\n () => updateCrud('catalog/prices', { id: existingId, ...payload }),\n )\n } else {\n await createCrud('catalog/prices', payload)\n }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { createCrud, updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { apiCall, readApiResultOrThrow, 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 { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { E } from '#generated/entities.ids.generated'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport {\n type VariantFormValues,\n type VariantPriceDraft,\n type OptionDefinition,\n createVariantInitialValues,\n normalizeOptionSchema,\n mapPriceItemToDraft,\n findInvalidVariantPriceKinds,\n} from '@open-mercato/core/modules/catalog/components/products/variantForm'\nimport {\n type PriceKindSummary,\n type PriceKindApiPayload,\n type TaxRateSummary,\n normalizePriceKindSummary,\n normalizeTaxRateSummary,\n mergeTaxRateSummaries,\n} from '@open-mercato/core/modules/catalog/components/products/productForm'\nimport { parseNumericInput } from '@open-mercato/core/modules/catalog/components/products/productFormUtils'\nimport {\n VariantBasicsSection,\n VariantOptionValuesSection,\n VariantDimensionsSection,\n VariantMetadataSection,\n VariantPricesSection,\n VariantMediaSection,\n} from '@open-mercato/core/modules/catalog/components/products/VariantBuilder'\nimport type { ProductMediaItem } from '@open-mercato/core/modules/catalog/components/products/ProductMediaManager'\nimport { buildAttachmentImageUrl, slugifyAttachmentFileName } from '@open-mercato/core/modules/attachments/lib/imageUrls'\nimport { fetchOptionSchemaTemplate } from '../../../optionSchemaClient'\nimport CreateVariantPage from '../create/page'\n\ntype VariantResponse = {\n items?: Array<Record<string, unknown>>\n}\n\ntype ProductResponse = {\n items?: Array<{\n id?: string\n title?: string | null\n metadata?: Record<string, unknown> | null\n tax_rate_id?: string | null\n taxRateId?: string | null\n tax_rate?: number | string | null\n taxRate?: number | string | null\n }>\n}\n\ntype PriceListResponse = {\n items?: Array<Record<string, unknown>>\n}\n\ntype AttachmentListResponse = {\n items?: ProductMediaItem[]\n}\n\nexport function handleVariantDeleteError(\n err: unknown,\n t: (key: string, fallback?: string) => string,\n): void {\n if (surfaceRecordConflict(err, t)) return\n const message = err instanceof Error && err.message\n ? err.message\n : t('catalog.variants.form.deleteError', 'Failed to delete variant.')\n flash(message, 'error')\n}\n\nfunction resolveVariantPriceLabel(prices: Record<string, VariantPriceDraft> | undefined): string | null {\n if (!prices || typeof prices !== 'object') return null\n const entries = Object.values(prices)\n for (const entry of entries) {\n const amount = typeof entry?.amount === 'string' ? entry.amount.trim() : ''\n if (!amount) continue\n const currencyCode =\n typeof entry.currencyCode === 'string' && entry.currencyCode.trim().length\n ? entry.currencyCode.trim().toUpperCase()\n : null\n return currencyCode ? `${currencyCode} ${amount}` : amount\n }\n return null\n}\n\nexport default function EditVariantPage({ params }: { params?: { productId?: string; variantId?: string } }) {\n const router = useRouter()\n const t = useT()\n const productId = params?.productId ? String(params.productId) : null\n const variantId = params?.variantId ? String(params.variantId) : null\n const isCreateSentinel = variantId === 'create'\n const [priceKinds, setPriceKinds] = React.useState<PriceKindSummary[]>([])\n const [taxRates, setTaxRates] = React.useState<TaxRateSummary[]>([])\n const [optionDefinitions, setOptionDefinitions] = React.useState<OptionDefinition[]>([])\n const [initialValues, setInitialValues] = React.useState<VariantFormValues | null>(null)\n const [existingPriceIds, setExistingPriceIds] = React.useState<Record<string, string>>({})\n // price-kind id \u2192 loaded price `updatedAt`, so the price sync sends each price's\n // own optimistic-lock version (the variant CrudForm submit scope would otherwise\n // leak the variant's version onto the catalog/prices guard) (#2055).\n const [existingPriceVersions, setExistingPriceVersions] = React.useState<Record<string, string | null>>({})\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n const [currentProductId, setCurrentProductId] = React.useState<string | null>(productId)\n const [productTitle, setProductTitle] = React.useState<string>('')\n const [productTaxRateId, setProductTaxRateId] = React.useState<string | null>(null)\n const [productTaxRate, setProductTaxRate] = React.useState<number | null>(null)\n\n const parseTaxRateSummary = React.useCallback(\n (item: Record<string, unknown>) =>\n normalizeTaxRateSummary(\n item,\n t('catalog.products.create.taxRates.unnamed', 'Untitled tax rate'),\n ),\n [t],\n )\n\n const fetchTaxRateById = React.useCallback(\n async (taxRateId: string): Promise<TaxRateSummary | null> => {\n const payload = await readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n `/api/sales/tax-rates?id=${encodeURIComponent(taxRateId)}&pageSize=1`,\n undefined,\n { errorMessage: t('catalog.products.create.taxRates.error', 'Failed to load tax rates.'), fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n return (\n items\n .map((item) => parseTaxRateSummary(item))\n .find((item): item is TaxRateSummary => item?.id === taxRateId) ?? null\n )\n },\n [parseTaxRateSummary, t],\n )\n\n React.useEffect(() => {\n const loadPriceKinds = async () => {\n try {\n const payload = await readApiResultOrThrow<{ items?: PriceKindApiPayload[] }>(\n '/api/catalog/price-kinds?pageSize=100',\n undefined,\n { errorMessage: t('catalog.priceKinds.errors.load', 'Failed to load price kinds.') },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setPriceKinds(items.map((item) => normalizePriceKindSummary(item)).filter((item): item is PriceKindSummary => !!item))\n } catch (err) {\n console.error('catalog.price-kinds.fetch failed', err)\n setPriceKinds([])\n }\n }\n loadPriceKinds().catch(() => {})\n }, [t])\n\n React.useEffect(() => {\n const loadTaxRates = async () => {\n try {\n const payload = await readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n '/api/sales/tax-rates?pageSize=200',\n undefined,\n { errorMessage: t('catalog.products.create.taxRates.error', 'Failed to load tax rates.'), fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setTaxRates(\n items\n .map((item) => parseTaxRateSummary(item))\n .filter((item): item is TaxRateSummary => item !== null),\n )\n } catch (err) {\n console.error('sales.tax-rates.fetch failed', err)\n setTaxRates([])\n }\n }\n loadTaxRates().catch(() => {})\n }, [parseTaxRateSummary, t])\n\n React.useEffect(() => {\n const ids = [initialValues?.taxRateId, productTaxRateId].filter(\n (value): value is string => typeof value === 'string' && value.length > 0,\n )\n const missingIds = ids.filter((id) => !taxRates.some((rate) => rate.id === id))\n if (!missingIds.length) return\n Promise.all(missingIds.map((id) => fetchTaxRateById(id).catch(() => null)))\n .then((selectedRates) => {\n setTaxRates((current) =>\n selectedRates.reduce(\n (next, selected) => mergeTaxRateSummaries(next, selected),\n current,\n ),\n )\n })\n .catch(() => {})\n }, [fetchTaxRateById, initialValues?.taxRateId, productTaxRateId, taxRates])\n\n React.useEffect(() => {\n if (!variantId || isCreateSentinel || priceKinds.length === 0) return\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n setIsNotFound(false)\n try {\n const variantRes = await apiCall<VariantResponse>(\n `/api/catalog/variants?id=${encodeURIComponent(variantId!)}&page=1&pageSize=1`,\n )\n if (!variantRes.ok) {\n if (variantRes.status === 404) {\n if (!cancelled) setIsNotFound(true)\n return\n }\n throw new Error(t('catalog.variants.form.errors.load', 'Failed to load variant.'))\n }\n const record = Array.isArray(variantRes.result?.items) ? variantRes.result?.items?.[0] : undefined\n if (!record) {\n if (!cancelled) setIsNotFound(true)\n return\n }\n const resolvedProductId =\n typeof record.product_id === 'string'\n ? record.product_id\n : typeof record.productId === 'string'\n ? record.productId\n : currentProductId\n if (resolvedProductId) setCurrentProductId(resolvedProductId)\n const metadata = typeof record.metadata === 'object' && record.metadata ? { ...(record.metadata as Record<string, unknown>) } : {}\n const [attachments, priceDrafts, productRes] = await Promise.all([\n fetchVariantAttachments(variantId!),\n loadVariantPrices(variantId!, priceKinds),\n resolvedProductId\n ? apiCall<ProductResponse>(\n `/api/catalog/products?id=${encodeURIComponent(resolvedProductId)}&page=1&pageSize=1`,\n )\n : Promise.resolve(null),\n ])\n const priceIdMap: Record<string, string> = {}\n const priceVersionMap: Record<string, string | null> = {}\n Object.entries(priceDrafts).forEach(([kindId, draft]) => {\n if (draft.priceId) {\n priceIdMap[kindId] = draft.priceId\n priceVersionMap[kindId] = draft.updatedAt ?? null\n }\n })\n setExistingPriceIds(priceIdMap)\n setExistingPriceVersions(priceVersionMap)\n const customDefaults = extractCustomFieldEntries(record)\n let loadedOptionDefinitions: OptionDefinition[] = []\n if (resolvedProductId) {\n if (productRes?.ok) {\n const product = Array.isArray(productRes.result?.items) ? productRes.result?.items?.[0] : undefined\n if (product) {\n setProductTitle(typeof product.title === 'string' ? product.title : '')\n const taxRateId =\n typeof (product as any).tax_rate_id === 'string'\n ? (product as any).tax_rate_id\n : typeof (product as any).taxRateId === 'string'\n ? (product as any).taxRateId\n : null\n const taxRateValueRaw =\n typeof (product as any).tax_rate === 'number'\n ? (product as any).tax_rate\n : typeof (product as any).tax_rate === 'string'\n ? Number((product as any).tax_rate)\n : typeof (product as any).taxRate === 'number'\n ? (product as any).taxRate\n : typeof (product as any).taxRate === 'string'\n ? Number((product as any).taxRate)\n : null\n const taxRateValue = Number.isFinite(taxRateValueRaw) ? Number(taxRateValueRaw) : null\n setProductTaxRateId(taxRateId)\n setProductTaxRate(taxRateValue)\n const productMetadata = (product.metadata ?? {}) as Record<string, unknown>\n const optionSchemaId =\n typeof (product as any).option_schema_id === 'string'\n ? (product as any).option_schema_id\n : typeof (product as any).optionSchemaId === 'string'\n ? (product as any).optionSchemaId\n : null\n let schemaSource: unknown =\n productMetadata.optionSchema ?? (productMetadata.option_schema as unknown)\n if (optionSchemaId) {\n const template = await fetchOptionSchemaTemplate(optionSchemaId)\n if (template?.schema?.options) {\n schemaSource = template.schema.options.map((option) => ({\n code: option.code,\n label: option.label,\n values: Array.isArray(option.choices)\n ? option.choices.map((choice) => ({\n id: choice.code ?? undefined,\n label: choice.label ?? choice.code ?? '',\n }))\n : [],\n }))\n }\n }\n loadedOptionDefinitions = normalizeOptionSchema(schemaSource)\n setOptionDefinitions(loadedOptionDefinitions)\n }\n }\n }\n const variantTaxRateId =\n typeof (record as any).tax_rate_id === 'string'\n ? (record as any).tax_rate_id\n : typeof (record as any).taxRateId === 'string'\n ? (record as any).taxRateId\n : null\n // The variant's own tax rate is hydrated into the select options by the\n // dedicated tax-rate effect (it watches initialValues.taxRateId), so it no\n // longer needs a serial fetch inside the primary loader.\n if (!cancelled) {\n const optionValues =\n typeof record.option_values === 'object' && record.option_values\n ? { ...(record.option_values as Record<string, string>) }\n : typeof record.optionValues === 'object' && record.optionValues\n ? { ...(record.optionValues as Record<string, string>) }\n : {}\n const normalizedOptionValues = reconcileOptionValues(optionValues, loadedOptionDefinitions)\n const defaultMediaId =\n typeof record.default_media_id === 'string'\n ? record.default_media_id\n : typeof record.defaultMediaId === 'string'\n ? record.defaultMediaId\n : attachments[0]?.id ?? null\n const defaultMediaUrl =\n typeof record.default_media_url === 'string'\n ? record.default_media_url\n : typeof record.defaultMediaUrl === 'string'\n ? record.defaultMediaUrl\n : ''\n const base = createVariantInitialValues()\n setInitialValues({\n ...base,\n id: variantId!,\n mediaDraftId: variantId!,\n name: typeof record.name === 'string' ? record.name : '',\n sku: typeof record.sku === 'string' ? record.sku : '',\n barcode: typeof record.barcode === 'string' ? record.barcode : '',\n gtinType:\n typeof record.gtin_type === 'string'\n ? record.gtin_type\n : typeof record.gtinType === 'string'\n ? record.gtinType\n : null,\n hsCode:\n typeof record.hs_code === 'string'\n ? record.hs_code\n : typeof record.hsCode === 'string'\n ? record.hsCode\n : '',\n isDefault: record.is_default === true || record.isDefault === true,\n isActive: record.is_active !== false && record.isActive !== false,\n optionValues: normalizedOptionValues,\n metadata,\n mediaItems: attachments,\n defaultMediaId,\n defaultMediaUrl,\n prices: priceDrafts,\n taxRateId: variantTaxRateId,\n customFieldsetCode:\n typeof record.custom_fieldset_code === 'string'\n ? record.custom_fieldset_code\n : typeof record.customFieldsetCode === 'string'\n ? record.customFieldsetCode\n : null,\n updatedAt:\n typeof record.updatedAt === 'string'\n ? record.updatedAt\n : typeof record.updated_at === 'string'\n ? record.updated_at\n : null,\n ...customDefaults,\n })\n }\n } catch (err) {\n console.error('catalog.variants.load.failed', err)\n if (!cancelled) {\n const message = err instanceof Error && err.message ? err.message : t('catalog.variants.form.errors.load', 'Failed to load variant.')\n setError(message)\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [variantId, t, currentProductId, priceKinds])\n\n const groups = React.useMemo<CrudFormGroup[]>(() => {\n const list: CrudFormGroup[] = [\n {\n id: 'general',\n column: 1,\n title: t('catalog.variants.form.general', 'General'),\n component: ({ values, setValue, errors }) => (\n <VariantBasicsSection values={values as VariantFormValues} setValue={setValue} errors={errors} />\n ),\n },\n {\n id: 'metadata',\n column: 1,\n title: t('catalog.products.edit.metadata.title', 'Metadata'),\n description: t('catalog.products.edit.metadata.hint', 'Attach structured key/value pairs for integrations.'),\n component: ({ values, setValue }) => (\n <VariantMetadataSection values={values as VariantFormValues} setValue={setValue} showIntro={false} embedded />\n ),\n },\n {\n id: 'prices',\n column: 1,\n title: t('catalog.variants.form.pricesLabel', 'Prices'),\n description: t('catalog.variants.form.pricesHint', 'Populate list prices per price kind.'),\n component: ({ values, setValue }) => (\n <VariantPricesSection\n values={values as VariantFormValues}\n setValue={setValue}\n priceKinds={priceKinds}\n taxRates={taxRates}\n showHeader={false}\n embedded\n />\n ),\n },\n {\n id: 'media',\n column: 1,\n title: t('catalog.variants.form.media', 'Media'),\n component: ({ values, setValue }) => (\n <VariantMediaSection values={values as VariantFormValues} setValue={setValue} showLabel={false} />\n ),\n },\n ]\n\n if (optionDefinitions.length) {\n list.push({\n id: 'options',\n column: 2,\n title: t('catalog.variants.form.options', 'Option values'),\n component: ({ values, setValue }) => (\n <VariantOptionValuesSection\n values={values as VariantFormValues}\n setValue={setValue}\n optionDefinitions={optionDefinitions}\n showHeading={false}\n />\n ),\n })\n }\n\n list.push({\n id: 'dimensions',\n column: 2,\n title: t('catalog.variants.form.dimensions', 'Dimensions & weight'),\n component: ({ values, setValue }) => (\n <VariantDimensionsSection values={values as VariantFormValues} setValue={setValue} showHeading={false} />\n ),\n })\n\n list.push({\n id: 'custom',\n column: 2,\n title: t('catalog.variants.form.customFields', 'Custom attributes'),\n kind: 'customFields',\n })\n\n return list\n }, [optionDefinitions, priceKinds, t, taxRates])\n\n if (isCreateSentinel) {\n if (!productId) {\n return (\n <Page>\n <PageBody>\n <div className=\"rounded border border-destructive/40 bg-destructive/10 px-4 py-3 text-sm text-destructive\">\n {t('catalog.variants.form.errors.productMissing', 'Product identifier is missing.')}\n </div>\n </PageBody>\n </Page>\n )\n }\n return <CreateVariantPage params={{ productId }} />\n }\n\n if (!variantId || !currentProductId) {\n return (\n <Page>\n <PageBody>\n <div className=\"rounded border border-destructive/40 bg-destructive/10 px-4 py-3 text-sm text-destructive\">\n {t('catalog.variants.form.errors.variantMissing', 'Variant identifier is missing.')}\n </div>\n </PageBody>\n </Page>\n )\n }\n\n const formTitle = productTitle\n ? t('catalog.variants.form.editTitleFor', 'Edit variant \u2022 {{title}}').replace('{{title}}', productTitle)\n : t('catalog.variants.form.editTitle', 'Edit variant')\n const productVariantsHref = `/backend/catalog/products/${currentProductId}#variants`\n\n // When the variant was deleted (e.g. concurrently in another tab) the GET\n // returns no record. Render a dedicated not-found state with a recovery link\n // instead of an empty CrudForm that throws runtime errors (#2055 QA).\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('catalog.variants.form.errors.notFound', 'Variant not found.')}\n backHref={productVariantsHref}\n backLabel={t('catalog.variants.form.actions.backToProduct', 'Back to product variants')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error && !loading) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <CrudForm<VariantFormValues>\n title={formTitle}\n backHref={productVariantsHref}\n versionHistory={{ resourceKind: 'catalog.variant', resourceId: variantId ? String(variantId) : '' }}\n extraActions={(\n <SendObjectMessageDialog\n object={{\n entityModule: 'catalog',\n entityType: 'variant',\n entityId: variantId,\n previewData: {\n title:\n (typeof initialValues?.name === 'string' && initialValues.name.trim().length\n ? initialValues.name\n : variantId),\n metadata: {\n [t('catalog.variants.form.skuLabel')]:\n (typeof initialValues?.sku === 'string' && initialValues.sku.trim().length\n ? initialValues.sku\n : '-'),\n [t('catalog.variants.form.pricesLabel')]:\n resolveVariantPriceLabel(initialValues?.prices) ?? '-',\n },\n },\n }}\n viewHref={`/backend/catalog/products/${currentProductId}/variants/${variantId}`}\n />\n )}\n fields={[]}\n groups={groups}\n entityId={E.catalog.catalog_product_variant}\n customFieldsetBindings={{ [E.catalog.catalog_product_variant]: { valueKey: 'customFieldsetCode' } }}\n initialValues={initialValues ?? undefined}\n isLoading={loading}\n loadingMessage={t('catalog.variants.form.loading', 'Loading variant...')}\n submitLabel={t('catalog.variants.form.save', 'Save changes')}\n cancelHref={productVariantsHref}\n onSubmit={async (values) => {\n const name = values.name?.trim()\n if (!name) {\n const message = t('catalog.variants.form.errors.nameRequired', 'Provide the variant name.')\n throw createCrudFormError(message, { name: message })\n }\n const invalidPriceKinds = findInvalidVariantPriceKinds(priceKinds, values.prices)\n if (invalidPriceKinds.length) {\n const message = t('catalog.variants.form.errors.invalidPrice', 'Provide a valid non-negative price.')\n throw createCrudFormError(message, { prices: message })\n }\n const resolveTaxRateValue = (taxRateId?: string | null) => {\n if (!taxRateId) return null\n const match = taxRates.find((rate) => rate.id === taxRateId)\n return typeof match?.rate === 'number' && Number.isFinite(match.rate) ? match.rate : null\n }\n const resolvedTaxRateId = values.taxRateId ?? productTaxRateId ?? null\n const resolvedTaxRateValue =\n values.taxRateId && resolvedTaxRateId\n ? resolveTaxRateValue(resolvedTaxRateId)\n : productTaxRateId\n ? resolveTaxRateValue(productTaxRateId) ?? productTaxRate\n : productTaxRate ?? null\n const metadata = typeof values.metadata === 'object' && values.metadata ? { ...values.metadata } : {}\n const defaultMediaEntry = values.defaultMediaId\n ? (Array.isArray(values.mediaItems) ? values.mediaItems : []).find((item) => item.id === values.defaultMediaId)\n : null\n const defaultMediaUrl = defaultMediaEntry\n ? buildAttachmentImageUrl(defaultMediaEntry.id, {\n slug: slugifyAttachmentFileName(defaultMediaEntry.fileName),\n })\n : null\n const payload: Record<string, unknown> = {\n id: variantId,\n productId: currentProductId,\n name,\n sku: values.sku?.trim() || undefined,\n barcode: values.barcode?.trim() || undefined,\n gtinType: values.gtinType ?? null,\n hsCode: values.hsCode?.trim() || null,\n isDefault: Boolean(values.isDefault),\n isActive: values.isActive !== false,\n optionValues: Object.keys(values.optionValues ?? {}).length ? values.optionValues : undefined,\n metadata,\n defaultMediaId: values.defaultMediaId ?? undefined,\n defaultMediaUrl: defaultMediaUrl ?? undefined,\n customFieldsetCode: values.customFieldsetCode?.trim().length ? values.customFieldsetCode : undefined,\n taxRateId: resolvedTaxRateId,\n taxRate: resolvedTaxRateValue,\n }\n const customFields = collectCustomFieldValues(values)\n if (Object.keys(customFields).length) payload.customFields = customFields\n\n await updateCrud('catalog/variants', payload)\n await syncVariantPricesUpdate({\n priceKinds,\n priceDrafts: values.prices ?? {},\n existingPriceIds,\n existingPriceVersions,\n productId: currentProductId,\n variantId,\n taxRates,\n taxRateId: values.taxRateId,\n productTaxRateId,\n productTaxRate,\n })\n flash(t('catalog.variants.form.updated', 'Variant updated.'), 'success')\n router.push(productVariantsHref)\n }}\n onDelete={async () => {\n try {\n await deleteCrud('catalog/variants', variantId!, {\n errorMessage: t('catalog.variants.form.deleteError', 'Failed to delete variant.'),\n })\n } catch (err) {\n handleVariantDeleteError(err, t)\n throw err\n }\n flash(t('catalog.variants.form.deleted', 'Variant deleted.'), 'success')\n router.push(productVariantsHref)\n }}\n deleteRedirect={productVariantsHref}\n />\n </PageBody>\n </Page>\n )\n}\n\nfunction reconcileOptionValues(\n optionValues: Record<string, string>,\n optionDefinitions: OptionDefinition[],\n): Record<string, string> {\n if (!optionValues || !optionDefinitions.length) {\n return optionValues ?? {}\n }\n const remaining = new Map(Object.entries(optionValues))\n const normalized: Record<string, string> = {}\n\n for (const option of optionDefinitions) {\n const code = option.code?.trim()\n if (!code) continue\n if (remaining.has(code)) {\n const value = remaining.get(code)\n if (value !== undefined) {\n normalized[code] = value\n }\n remaining.delete(code)\n continue\n }\n const matchKey = findOptionKeyByValue(remaining, option.values)\n if (matchKey) {\n const value = remaining.get(matchKey)\n if (value !== undefined) {\n normalized[code] = value\n }\n remaining.delete(matchKey)\n }\n }\n\n remaining.forEach((value, key) => {\n if (normalized[key] === undefined) {\n normalized[key] = value\n }\n })\n\n return normalized\n}\n\nfunction findOptionKeyByValue(\n candidates: Map<string, string>,\n optionValues: { id: string; label: string }[],\n): string | null {\n if (!optionValues.length) return null\n const matches: string[] = []\n candidates.forEach((value, key) => {\n if (optionValues.some((entry) => entry.label === value)) {\n matches.push(key)\n }\n })\n return matches.length === 1 ? matches[0] : null\n}\n\nasync function fetchVariantAttachments(variantId: string): Promise<ProductMediaItem[]> {\n try {\n const res = await apiCall<AttachmentListResponse>(\n `/api/attachments?entityId=${encodeURIComponent(E.catalog.catalog_product_variant)}&recordId=${encodeURIComponent(variantId)}`,\n )\n if (!res.ok) return []\n return Array.isArray(res.result?.items) ? res.result?.items ?? [] : []\n } catch (err) {\n console.error('catalog.variants.attachments.load', err)\n return []\n }\n}\n\nasync function loadVariantPrices(variantId: string, priceKinds: PriceKindSummary[]): Promise<Record<string, VariantPriceDraft>> {\n const kindDisplayModes = new Map(priceKinds.map((k) => [k.id, k.displayMode]))\n const drafts: Record<string, VariantPriceDraft> = {}\n const pageSize = 100\n let page = 1\n try {\n while (true) {\n const res = await apiCall<PriceListResponse>(\n `/api/catalog/prices?variantId=${encodeURIComponent(variantId)}&page=${page}&pageSize=${pageSize}`,\n )\n if (!res.ok) break\n const items = Array.isArray(res.result?.items) ? res.result?.items : []\n for (const item of items) {\n const draft = mapPriceItemToDraft(item as Record<string, unknown>, kindDisplayModes)\n if (draft) drafts[draft.priceKindId] = draft\n }\n if (items.length < pageSize) break\n page += 1\n }\n } catch (err) {\n console.error('catalog.variants.prices.load', err)\n }\n return drafts\n}\n\nasync function syncVariantPricesUpdate({\n priceKinds,\n priceDrafts,\n existingPriceIds,\n existingPriceVersions,\n productId,\n variantId,\n taxRates,\n taxRateId,\n productTaxRateId,\n productTaxRate,\n}: {\n priceKinds: PriceKindSummary[]\n priceDrafts: Record<string, VariantPriceDraft>\n existingPriceIds: Record<string, string>\n existingPriceVersions: Record<string, string | null>\n productId: string\n variantId: string\n taxRates: TaxRateSummary[]\n taxRateId: string | null\n productTaxRateId?: string | null\n productTaxRate?: number | null\n}): Promise<void> {\n const selectedTaxRate = taxRates.find((rate) => rate.id === taxRateId) ?? null\n const fallbackProductTaxRate =\n !selectedTaxRate && productTaxRateId\n ? taxRates.find((rate) => rate.id === productTaxRateId) ?? null\n : null\n const resolvedTaxRateValue =\n selectedTaxRate?.rate ??\n fallbackProductTaxRate?.rate ??\n (Number.isFinite(productTaxRate ?? null) ? productTaxRate ?? null : null)\n const resolvedTaxRateId = (selectedTaxRate ?? fallbackProductTaxRate)?.id ?? null\n for (const kind of priceKinds) {\n const draft = priceDrafts?.[kind.id]\n const amount = typeof draft?.amount === 'string' ? draft.amount.trim() : ''\n const existingId = draft?.priceId ?? existingPriceIds[kind.id]\n // The price's own version \u2014 overrides the variant header the parent CrudForm\n // submit scope put on the stack, so the catalog/prices guard compares the\n // right row (otherwise a stale/false 409). #2055.\n const lockVersion = draft?.updatedAt ?? existingPriceVersions[kind.id] ?? null\n if (!amount) {\n if (existingId) {\n try {\n await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(lockVersion),\n () => deleteCrud('catalog/prices', existingId),\n )\n } catch (err) {\n console.error('catalog.prices.delete', err)\n }\n }\n continue\n }\n const numeric = parseNumericInput(amount)\n if (!Number.isFinite(numeric) || numeric < 0) continue\n const payload: Record<string, unknown> = {\n productId,\n variantId,\n priceKindId: kind.id,\n currencyCode: kind.currencyCode ?? undefined,\n }\n if (resolvedTaxRateId) payload.taxRateId = resolvedTaxRateId\n else if (typeof resolvedTaxRateValue === 'number' && Number.isFinite(resolvedTaxRateValue)) payload.taxRate = resolvedTaxRateValue\n if (kind.displayMode === 'including-tax') payload.unitPriceGross = numeric\n else payload.unitPriceNet = numeric\n if (existingId) {\n await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(lockVersion),\n () => updateCrud('catalog/prices', { id: existingId, ...payload }),\n )\n } else {\n await createCrud('catalog/prices', payload)\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -156,20 +156,25 @@ function ActivitiesSection({
156
156
  setHasMore(canonicalHasMore);
157
157
  return;
158
158
  }
159
+ const legacyPageNumbers = Array.from({ length: loadedPages }, (_, index) => index + 1);
160
+ const legacyPayloads = await Promise.all(
161
+ legacyPageNumbers.map((legacyPage) => {
162
+ const legacyParams = new URLSearchParams({
163
+ entityId,
164
+ page: String(legacyPage),
165
+ pageSize: "50",
166
+ sortField: "occurredAt",
167
+ sortDir: "desc"
168
+ });
169
+ if (dealId) legacyParams.set("dealId", dealId);
170
+ return readApiResultOrThrow(
171
+ `/api/customers/activities?${legacyParams.toString()}`
172
+ ).catch(() => ({ items: [], totalPages: 1 }));
173
+ })
174
+ );
159
175
  const legacyItems = [];
160
176
  let legacyTotalPages = 1;
161
- for (let legacyPage = 1; legacyPage <= loadedPages; legacyPage += 1) {
162
- const legacyParams = new URLSearchParams({
163
- entityId,
164
- page: String(legacyPage),
165
- pageSize: "50",
166
- sortField: "occurredAt",
167
- sortDir: "desc"
168
- });
169
- if (dealId) legacyParams.set("dealId", dealId);
170
- const legacyPayload = await readApiResultOrThrow(
171
- `/api/customers/activities?${legacyParams.toString()}`
172
- ).catch(() => ({ items: [], totalPages: 1 }));
177
+ for (const legacyPayload of legacyPayloads) {
173
178
  legacyItems.push(...Array.isArray(legacyPayload?.items) ? legacyPayload.items.map(normalizeLegacyActivity) : []);
174
179
  legacyTotalPages = typeof legacyPayload?.totalPages === "number" ? legacyPayload.totalPages : legacyTotalPages;
175
180
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/ActivitiesSection.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Clock, Search } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport type { SectionAction, TabEmptyStateConfig } from '@open-mercato/ui/backend/detail'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Kbd } from '@open-mercato/ui/primitives/kbd'\nimport { ActivityTimelineFilters } from './ActivityTimelineFilters'\nimport { ActivityTimeline } from './ActivityTimeline'\nimport type { ActivitySummary, InteractionSummary } from './types'\n\ntype GuardedMutationRunner = <T>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\nexport type ActivitiesSectionProps = {\n entityId: string | null\n entityName?: string | null\n dealId?: string | null\n useCanonicalInteractions?: boolean\n addActionLabel: string\n emptyState: TabEmptyStateConfig\n onActionChange?: (action: SectionAction | null) => void\n onLoadingChange?: (isLoading: boolean) => void\n onDataRefresh?: () => void\n dealOptions?: Array<{ id: string; label: string }>\n entityOptions?: Array<{ id: string; label: string }>\n defaultEntityId?: string | null\n runGuardedMutation?: GuardedMutationRunner\n refreshKey?: number\n onEditActivity?: (activity: InteractionSummary) => void\n}\n\nfunction toDateOnly(value: string | null | undefined): string {\n if (!value) return ''\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? '' : date.toISOString().slice(0, 10)\n}\n\nfunction normalizeLegacyActivity(activity: ActivitySummary): InteractionSummary {\n return {\n id: activity.id,\n interactionType: activity.activityType,\n title: activity.subject ?? null,\n body: activity.body ?? null,\n status: 'done',\n scheduledAt: null,\n occurredAt: activity.occurredAt ?? null,\n priority: null,\n authorUserId: activity.authorUserId ?? null,\n ownerUserId: null,\n appearanceIcon: activity.appearanceIcon ?? null,\n appearanceColor: activity.appearanceColor ?? null,\n source: 'legacy-activity',\n entityId: activity.entityId ?? null,\n dealId: activity.dealId ?? null,\n organizationId: null,\n tenantId: null,\n authorName: activity.authorName ?? null,\n authorEmail: activity.authorEmail ?? null,\n dealTitle: activity.dealTitle ?? null,\n customValues: activity.customValues ?? null,\n createdAt: activity.createdAt,\n updatedAt: activity.createdAt,\n }\n}\n\nfunction sortTimelineActivities(items: InteractionSummary[]): InteractionSummary[] {\n const now = Date.now()\n return [...items].sort((left, right) => {\n const leftScheduled = left.scheduledAt ? new Date(left.scheduledAt).getTime() : Number.NaN\n const rightScheduled = right.scheduledAt ? new Date(right.scheduledAt).getTime() : Number.NaN\n const leftIsPlanned = left.status === 'planned' && Number.isFinite(leftScheduled)\n const rightIsPlanned = right.status === 'planned' && Number.isFinite(rightScheduled)\n const leftIsUpcoming = leftIsPlanned && leftScheduled >= now\n const rightIsUpcoming = rightIsPlanned && rightScheduled >= now\n\n if (leftIsUpcoming !== rightIsUpcoming) {\n return leftIsUpcoming ? -1 : 1\n }\n\n if (leftIsUpcoming && rightIsUpcoming) {\n if (leftScheduled === rightScheduled) return left.id.localeCompare(right.id)\n return leftScheduled - rightScheduled\n }\n\n const leftTime = left.occurredAt ?? left.createdAt\n const rightTime = right.occurredAt ?? right.createdAt\n const compare = rightTime.localeCompare(leftTime)\n if (compare !== 0) return compare\n return right.id.localeCompare(left.id)\n })\n}\n\nexport function ActivitiesSection({\n entityId,\n entityName,\n dealId,\n useCanonicalInteractions = false,\n onActionChange,\n onLoadingChange,\n refreshKey = 0,\n onEditActivity,\n runGuardedMutation,\n}: ActivitiesSectionProps) {\n const t = useT()\n const [filterTypes, setFilterTypes] = React.useState<string[]>([])\n const [filterDateFrom, setFilterDateFrom] = React.useState('')\n const [filterDateTo, setFilterDateTo] = React.useState('')\n const [searchTerm, setSearchTerm] = React.useState('')\n const [activities, setActivities] = React.useState<InteractionSummary[]>([])\n const [loading, setLoading] = React.useState(false)\n const [hasMore, setHasMore] = React.useState(false)\n const [loadedPages, setLoadedPages] = React.useState(1)\n const searchInputRef = React.useRef<HTMLInputElement>(null)\n\n React.useEffect(() => {\n if (!entityId) return\n function handleShortcut(event: KeyboardEvent) {\n if ((event.metaKey || event.ctrlKey) && event.key === '1') {\n event.preventDefault()\n searchInputRef.current?.focus()\n }\n }\n window.addEventListener('keydown', handleShortcut)\n return () => window.removeEventListener('keydown', handleShortcut)\n }, [entityId])\n\n const visibleActivities = React.useMemo(() => {\n const term = searchTerm.trim().toLowerCase()\n if (!term) return activities\n return activities.filter((activity) => {\n const haystack = [\n activity.title,\n activity.body,\n activity.authorName,\n activity.dealTitle,\n activity.interactionType,\n ]\n .filter((value): value is string => typeof value === 'string' && value.length > 0)\n .join(' ')\n .toLowerCase()\n return haystack.includes(term)\n })\n }, [activities, searchTerm])\n\n React.useEffect(() => {\n onActionChange?.(null)\n return () => onActionChange?.(null)\n }, [onActionChange])\n\n React.useEffect(() => {\n onLoadingChange?.(loading)\n }, [loading, onLoadingChange])\n\n const loadActivities = React.useCallback(async () => {\n if (!entityId) {\n setActivities([])\n return\n }\n\n setLoading(true)\n try {\n // Always fetch canonical interactions (new activities are always created here)\n const taskFilterActive = filterTypes.includes('task')\n const canonicalParams = new URLSearchParams({\n entityId,\n limit: '50',\n sortField: 'occurredAt',\n sortDir: 'desc',\n })\n // Hide tasks from the activity timeline by default \u2014 they have their own tab \u2014\n // but lift the exclusion when the user explicitly toggled the Task chip on\n // (mirrors `ActivityHistorySection.tsx` after the #1805 fix).\n if (!taskFilterActive) canonicalParams.set('excludeInteractionType', 'task')\n if (dealId) canonicalParams.set('dealId', dealId)\n if (filterTypes.length > 0) canonicalParams.set('type', filterTypes.join(','))\n if (filterDateFrom) canonicalParams.set('from', filterDateFrom)\n if (filterDateTo) canonicalParams.set('to', filterDateTo)\n\n const canonicalItems: InteractionSummary[] = []\n let canonicalCursor: string | undefined\n let canonicalHasMore = false\n let pageIndex = 0\n do {\n const params = new URLSearchParams(canonicalParams)\n if (canonicalCursor) params.set('cursor', canonicalCursor)\n const canonicalPayload = await readApiResultOrThrow<{ items?: InteractionSummary[]; nextCursor?: string }>(\n `/api/customers/interactions?${params.toString()}`,\n ).catch(() => ({ items: [] as InteractionSummary[], nextCursor: undefined }))\n canonicalItems.push(...(Array.isArray(canonicalPayload?.items) ? canonicalPayload.items : []))\n canonicalCursor = typeof canonicalPayload?.nextCursor === 'string' ? canonicalPayload.nextCursor : undefined\n canonicalHasMore = Boolean(canonicalCursor)\n pageIndex += 1\n } while (canonicalCursor && pageIndex < loadedPages)\n\n if (useCanonicalInteractions) {\n setActivities(sortTimelineActivities(canonicalItems))\n setHasMore(canonicalHasMore)\n return\n }\n\n // In legacy mode, also fetch legacy activities and merge with canonical\n const legacyItems: InteractionSummary[] = []\n let legacyTotalPages = 1\n for (let legacyPage = 1; legacyPage <= loadedPages; legacyPage += 1) {\n const legacyParams = new URLSearchParams({\n entityId,\n page: String(legacyPage),\n pageSize: '50',\n sortField: 'occurredAt',\n sortDir: 'desc',\n })\n if (dealId) legacyParams.set('dealId', dealId)\n const legacyPayload = await readApiResultOrThrow<{ items?: ActivitySummary[]; totalPages?: number }>(\n `/api/customers/activities?${legacyParams.toString()}`,\n ).catch(() => ({ items: [] as ActivitySummary[], totalPages: 1 }))\n legacyItems.push(...(Array.isArray(legacyPayload?.items) ? legacyPayload.items.map(normalizeLegacyActivity) : []))\n legacyTotalPages = typeof legacyPayload?.totalPages === 'number' ? legacyPayload.totalPages : legacyTotalPages\n }\n const legacyFiltered = legacyItems.filter((entry) => {\n if (filterTypes.length > 0 && !filterTypes.includes(entry.interactionType)) return false\n const dateOnly = toDateOnly(entry.occurredAt ?? entry.createdAt)\n if (filterDateFrom && dateOnly < filterDateFrom) return false\n if (filterDateTo && dateOnly > filterDateTo) return false\n return true\n })\n\n // Merge and deduplicate by id, sort newest first\n const seen = new Set<string>()\n const merged: InteractionSummary[] = []\n for (const item of [...canonicalItems, ...legacyFiltered]) {\n if (!seen.has(item.id)) {\n seen.add(item.id)\n merged.push(item)\n }\n }\n setActivities(sortTimelineActivities(merged))\n setHasMore(canonicalHasMore || legacyTotalPages > loadedPages)\n } catch (error) {\n console.error('customers.activities.history failed', error)\n flash(t('customers.activities.loadFailed', 'Failed to load activities.'), 'error')\n setActivities([])\n setHasMore(false)\n } finally {\n setLoading(false)\n }\n }, [dealId, entityId, filterDateFrom, filterDateTo, filterTypes, loadedPages, useCanonicalInteractions, refreshKey, t])\n\n React.useEffect(() => {\n setLoadedPages(1)\n }, [dealId, entityId, filterDateFrom, filterDateTo, filterTypes, useCanonicalInteractions])\n\n const handleMarkDone = React.useCallback(async (activityId: string) => {\n try {\n const operation = () =>\n apiCallOrThrow('/api/customers/interactions/complete', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: activityId, occurredAt: new Date().toISOString() }),\n })\n if (runGuardedMutation) {\n await runGuardedMutation(operation, {\n id: activityId,\n status: 'done',\n operation: 'completeActivity',\n })\n } else {\n await operation()\n }\n flash(t('customers.activities.actions.markDoneSuccess', 'Activity marked done'), 'success')\n await loadActivities()\n } catch (err) {\n console.warn('[customers.activitiesSection] mark done failed', activityId, err)\n flash(t('customers.activities.actions.markDoneError', 'Could not mark activity as done'), 'error')\n }\n }, [loadActivities, runGuardedMutation, t])\n\n const resolvedUserIdsRef = React.useRef(new Set<string>())\n\n // Resolve missing author names from user IDs\n React.useEffect(() => {\n loadActivities()\n .then(() => { resolvedUserIdsRef.current = new Set() })\n .catch((err) => console.warn('[ActivitiesSection] loadActivities failed', err))\n }, [loadActivities])\n\n React.useEffect(() => {\n const unresolvedIds = new Set<string>()\n for (const a of activities) {\n if (a.authorUserId && !a.authorName && !resolvedUserIdsRef.current.has(a.authorUserId)) {\n unresolvedIds.add(a.authorUserId)\n }\n }\n if (unresolvedIds.size === 0) return\n\n for (const uid of unresolvedIds) resolvedUserIdsRef.current.add(uid)\n\n const controller = new AbortController()\n readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n `/api/auth/users?ids=${[...unresolvedIds].join(',')}`,\n { signal: controller.signal },\n )\n .then((data) => {\n const users = Array.isArray(data?.items) ? data.items : []\n const nameMap = new Map<string, string>()\n for (const user of users) {\n const userId = typeof user.id === 'string' ? user.id : null\n const name = typeof user.display_name === 'string' && user.display_name.trim()\n ? user.display_name.trim()\n : typeof user.email === 'string'\n ? user.email\n : null\n if (userId && name) nameMap.set(userId, name)\n }\n if (nameMap.size > 0) {\n setActivities((prev) =>\n prev.map((a) => {\n if (a.authorUserId && !a.authorName && nameMap.has(a.authorUserId)) {\n return { ...a, authorName: nameMap.get(a.authorUserId) ?? null }\n }\n return a\n }),\n )\n }\n })\n .catch((err) => console.warn('[ActivitiesSection] resolve author names failed', err))\n return () => controller.abort()\n }, [activities])\n\n const totalCount = activities.length\n const visibleCount = visibleActivities.length\n\n return (\n <div className=\"flex flex-col gap-3.5 rounded-[10px] border border-border bg-card pt-4 pb-[18px] px-[18px]\">\n <div className=\"flex items-center gap-2\">\n <Clock className=\"size-[15px] text-muted-foreground\" />\n <h3 className=\"text-[13px] font-semibold text-foreground\">\n {entityName\n ? t('customers.timeline.history.title', 'Interaction history with {{name}}', { name: entityName })\n : t('customers.timeline.history.titleGeneric', 'Interaction history')}\n </h3>\n </div>\n\n <label className=\"relative flex items-center\">\n <Search className=\"pointer-events-none absolute left-2.5 size-5 text-muted-foreground\" aria-hidden />\n <input\n ref={searchInputRef}\n type=\"search\"\n value={searchTerm}\n onChange={(event) => setSearchTerm(event.target.value)}\n placeholder={t('customers.timeline.history.searchPlaceholder', 'Search...')}\n aria-label={t('customers.timeline.history.searchAriaLabel', 'Search interaction history')}\n className=\"h-9 w-full rounded-[10px] border border-border bg-card pl-9 pr-14 text-sm text-foreground shadow-xs placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring [&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden\"\n />\n <Kbd className=\"pointer-events-none absolute right-2 hidden text-[11px] uppercase tracking-[0.48px] sm:inline-flex\">\n \u23181\n </Kbd>\n </label>\n\n <ActivityTimelineFilters\n entityId={entityId}\n activeTypes={filterTypes}\n dateFrom={filterDateFrom}\n dateTo={filterDateTo}\n onTypesChange={setFilterTypes}\n onDateFromChange={setFilterDateFrom}\n onDateToChange={setFilterDateTo}\n onReset={() => {\n setFilterTypes([])\n setFilterDateFrom('')\n setFilterDateTo('')\n }}\n />\n\n {loading && totalCount === 0 ? (\n <div className=\"rounded-lg border border-dashed border-border/70 px-4 py-8 text-sm text-muted-foreground\">\n {t('customers.people.detail.activities.loading', 'Loading activities\u2026')}\n </div>\n ) : (\n <>\n <ActivityTimeline\n activities={visibleActivities}\n onEdit={onEditActivity}\n onMarkDone={handleMarkDone}\n />\n {totalCount > 0 ? (\n <div className=\"flex items-center justify-between gap-3 border-t border-border/60 pt-3\">\n <span className=\"text-xs text-muted-foreground\">\n {searchTerm.trim()\n ? t('customers.activities.seeMatching', 'Showing {visible} of {total} activities', {\n visible: visibleCount,\n total: totalCount,\n })\n : t('customers.activities.seeAll', 'See all {count} activities', { count: totalCount })}\n </span>\n {hasMore ? (\n <Button type=\"button\" variant=\"link\" size=\"sm\" onClick={() => setLoadedPages((value) => value + 1)}>\n {t('customers.activities.loadMore', 'Load more')}\n </Button>\n ) : null}\n </div>\n ) : null}\n </>\n )}\n </div>\n )\n}\n\nexport default ActivitiesSection\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Clock, Search } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport type { SectionAction, TabEmptyStateConfig } from '@open-mercato/ui/backend/detail'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Kbd } from '@open-mercato/ui/primitives/kbd'\nimport { ActivityTimelineFilters } from './ActivityTimelineFilters'\nimport { ActivityTimeline } from './ActivityTimeline'\nimport type { ActivitySummary, InteractionSummary } from './types'\n\ntype GuardedMutationRunner = <T>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\nexport type ActivitiesSectionProps = {\n entityId: string | null\n entityName?: string | null\n dealId?: string | null\n useCanonicalInteractions?: boolean\n addActionLabel: string\n emptyState: TabEmptyStateConfig\n onActionChange?: (action: SectionAction | null) => void\n onLoadingChange?: (isLoading: boolean) => void\n onDataRefresh?: () => void\n dealOptions?: Array<{ id: string; label: string }>\n entityOptions?: Array<{ id: string; label: string }>\n defaultEntityId?: string | null\n runGuardedMutation?: GuardedMutationRunner\n refreshKey?: number\n onEditActivity?: (activity: InteractionSummary) => void\n}\n\nfunction toDateOnly(value: string | null | undefined): string {\n if (!value) return ''\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? '' : date.toISOString().slice(0, 10)\n}\n\nfunction normalizeLegacyActivity(activity: ActivitySummary): InteractionSummary {\n return {\n id: activity.id,\n interactionType: activity.activityType,\n title: activity.subject ?? null,\n body: activity.body ?? null,\n status: 'done',\n scheduledAt: null,\n occurredAt: activity.occurredAt ?? null,\n priority: null,\n authorUserId: activity.authorUserId ?? null,\n ownerUserId: null,\n appearanceIcon: activity.appearanceIcon ?? null,\n appearanceColor: activity.appearanceColor ?? null,\n source: 'legacy-activity',\n entityId: activity.entityId ?? null,\n dealId: activity.dealId ?? null,\n organizationId: null,\n tenantId: null,\n authorName: activity.authorName ?? null,\n authorEmail: activity.authorEmail ?? null,\n dealTitle: activity.dealTitle ?? null,\n customValues: activity.customValues ?? null,\n createdAt: activity.createdAt,\n updatedAt: activity.createdAt,\n }\n}\n\nfunction sortTimelineActivities(items: InteractionSummary[]): InteractionSummary[] {\n const now = Date.now()\n return [...items].sort((left, right) => {\n const leftScheduled = left.scheduledAt ? new Date(left.scheduledAt).getTime() : Number.NaN\n const rightScheduled = right.scheduledAt ? new Date(right.scheduledAt).getTime() : Number.NaN\n const leftIsPlanned = left.status === 'planned' && Number.isFinite(leftScheduled)\n const rightIsPlanned = right.status === 'planned' && Number.isFinite(rightScheduled)\n const leftIsUpcoming = leftIsPlanned && leftScheduled >= now\n const rightIsUpcoming = rightIsPlanned && rightScheduled >= now\n\n if (leftIsUpcoming !== rightIsUpcoming) {\n return leftIsUpcoming ? -1 : 1\n }\n\n if (leftIsUpcoming && rightIsUpcoming) {\n if (leftScheduled === rightScheduled) return left.id.localeCompare(right.id)\n return leftScheduled - rightScheduled\n }\n\n const leftTime = left.occurredAt ?? left.createdAt\n const rightTime = right.occurredAt ?? right.createdAt\n const compare = rightTime.localeCompare(leftTime)\n if (compare !== 0) return compare\n return right.id.localeCompare(left.id)\n })\n}\n\nexport function ActivitiesSection({\n entityId,\n entityName,\n dealId,\n useCanonicalInteractions = false,\n onActionChange,\n onLoadingChange,\n refreshKey = 0,\n onEditActivity,\n runGuardedMutation,\n}: ActivitiesSectionProps) {\n const t = useT()\n const [filterTypes, setFilterTypes] = React.useState<string[]>([])\n const [filterDateFrom, setFilterDateFrom] = React.useState('')\n const [filterDateTo, setFilterDateTo] = React.useState('')\n const [searchTerm, setSearchTerm] = React.useState('')\n const [activities, setActivities] = React.useState<InteractionSummary[]>([])\n const [loading, setLoading] = React.useState(false)\n const [hasMore, setHasMore] = React.useState(false)\n const [loadedPages, setLoadedPages] = React.useState(1)\n const searchInputRef = React.useRef<HTMLInputElement>(null)\n\n React.useEffect(() => {\n if (!entityId) return\n function handleShortcut(event: KeyboardEvent) {\n if ((event.metaKey || event.ctrlKey) && event.key === '1') {\n event.preventDefault()\n searchInputRef.current?.focus()\n }\n }\n window.addEventListener('keydown', handleShortcut)\n return () => window.removeEventListener('keydown', handleShortcut)\n }, [entityId])\n\n const visibleActivities = React.useMemo(() => {\n const term = searchTerm.trim().toLowerCase()\n if (!term) return activities\n return activities.filter((activity) => {\n const haystack = [\n activity.title,\n activity.body,\n activity.authorName,\n activity.dealTitle,\n activity.interactionType,\n ]\n .filter((value): value is string => typeof value === 'string' && value.length > 0)\n .join(' ')\n .toLowerCase()\n return haystack.includes(term)\n })\n }, [activities, searchTerm])\n\n React.useEffect(() => {\n onActionChange?.(null)\n return () => onActionChange?.(null)\n }, [onActionChange])\n\n React.useEffect(() => {\n onLoadingChange?.(loading)\n }, [loading, onLoadingChange])\n\n const loadActivities = React.useCallback(async () => {\n if (!entityId) {\n setActivities([])\n return\n }\n\n setLoading(true)\n try {\n // Always fetch canonical interactions (new activities are always created here)\n const taskFilterActive = filterTypes.includes('task')\n const canonicalParams = new URLSearchParams({\n entityId,\n limit: '50',\n sortField: 'occurredAt',\n sortDir: 'desc',\n })\n // Hide tasks from the activity timeline by default \u2014 they have their own tab \u2014\n // but lift the exclusion when the user explicitly toggled the Task chip on\n // (mirrors `ActivityHistorySection.tsx` after the #1805 fix).\n if (!taskFilterActive) canonicalParams.set('excludeInteractionType', 'task')\n if (dealId) canonicalParams.set('dealId', dealId)\n if (filterTypes.length > 0) canonicalParams.set('type', filterTypes.join(','))\n if (filterDateFrom) canonicalParams.set('from', filterDateFrom)\n if (filterDateTo) canonicalParams.set('to', filterDateTo)\n\n const canonicalItems: InteractionSummary[] = []\n let canonicalCursor: string | undefined\n let canonicalHasMore = false\n let pageIndex = 0\n do {\n const params = new URLSearchParams(canonicalParams)\n if (canonicalCursor) params.set('cursor', canonicalCursor)\n const canonicalPayload = await readApiResultOrThrow<{ items?: InteractionSummary[]; nextCursor?: string }>(\n `/api/customers/interactions?${params.toString()}`,\n ).catch(() => ({ items: [] as InteractionSummary[], nextCursor: undefined }))\n canonicalItems.push(...(Array.isArray(canonicalPayload?.items) ? canonicalPayload.items : []))\n canonicalCursor = typeof canonicalPayload?.nextCursor === 'string' ? canonicalPayload.nextCursor : undefined\n canonicalHasMore = Boolean(canonicalCursor)\n pageIndex += 1\n } while (canonicalCursor && pageIndex < loadedPages)\n\n if (useCanonicalInteractions) {\n setActivities(sortTimelineActivities(canonicalItems))\n setHasMore(canonicalHasMore)\n return\n }\n\n // In legacy mode, also fetch legacy activities and merge with canonical.\n // Legacy fallback uses known page numbers, so request every page up front\n // and resolve them together instead of awaiting each one sequentially.\n const legacyPageNumbers = Array.from({ length: loadedPages }, (_, index) => index + 1)\n const legacyPayloads = await Promise.all(\n legacyPageNumbers.map((legacyPage) => {\n const legacyParams = new URLSearchParams({\n entityId,\n page: String(legacyPage),\n pageSize: '50',\n sortField: 'occurredAt',\n sortDir: 'desc',\n })\n if (dealId) legacyParams.set('dealId', dealId)\n return readApiResultOrThrow<{ items?: ActivitySummary[]; totalPages?: number }>(\n `/api/customers/activities?${legacyParams.toString()}`,\n ).catch(() => ({ items: [] as ActivitySummary[], totalPages: 1 }))\n }),\n )\n // Merge in page order so timeline ordering stays stable regardless of\n // which request settles first.\n const legacyItems: InteractionSummary[] = []\n let legacyTotalPages = 1\n for (const legacyPayload of legacyPayloads) {\n legacyItems.push(...(Array.isArray(legacyPayload?.items) ? legacyPayload.items.map(normalizeLegacyActivity) : []))\n legacyTotalPages = typeof legacyPayload?.totalPages === 'number' ? legacyPayload.totalPages : legacyTotalPages\n }\n const legacyFiltered = legacyItems.filter((entry) => {\n if (filterTypes.length > 0 && !filterTypes.includes(entry.interactionType)) return false\n const dateOnly = toDateOnly(entry.occurredAt ?? entry.createdAt)\n if (filterDateFrom && dateOnly < filterDateFrom) return false\n if (filterDateTo && dateOnly > filterDateTo) return false\n return true\n })\n\n // Merge and deduplicate by id, sort newest first\n const seen = new Set<string>()\n const merged: InteractionSummary[] = []\n for (const item of [...canonicalItems, ...legacyFiltered]) {\n if (!seen.has(item.id)) {\n seen.add(item.id)\n merged.push(item)\n }\n }\n setActivities(sortTimelineActivities(merged))\n setHasMore(canonicalHasMore || legacyTotalPages > loadedPages)\n } catch (error) {\n console.error('customers.activities.history failed', error)\n flash(t('customers.activities.loadFailed', 'Failed to load activities.'), 'error')\n setActivities([])\n setHasMore(false)\n } finally {\n setLoading(false)\n }\n }, [dealId, entityId, filterDateFrom, filterDateTo, filterTypes, loadedPages, useCanonicalInteractions, refreshKey, t])\n\n React.useEffect(() => {\n setLoadedPages(1)\n }, [dealId, entityId, filterDateFrom, filterDateTo, filterTypes, useCanonicalInteractions])\n\n const handleMarkDone = React.useCallback(async (activityId: string) => {\n try {\n const operation = () =>\n apiCallOrThrow('/api/customers/interactions/complete', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: activityId, occurredAt: new Date().toISOString() }),\n })\n if (runGuardedMutation) {\n await runGuardedMutation(operation, {\n id: activityId,\n status: 'done',\n operation: 'completeActivity',\n })\n } else {\n await operation()\n }\n flash(t('customers.activities.actions.markDoneSuccess', 'Activity marked done'), 'success')\n await loadActivities()\n } catch (err) {\n console.warn('[customers.activitiesSection] mark done failed', activityId, err)\n flash(t('customers.activities.actions.markDoneError', 'Could not mark activity as done'), 'error')\n }\n }, [loadActivities, runGuardedMutation, t])\n\n const resolvedUserIdsRef = React.useRef(new Set<string>())\n\n // Resolve missing author names from user IDs\n React.useEffect(() => {\n loadActivities()\n .then(() => { resolvedUserIdsRef.current = new Set() })\n .catch((err) => console.warn('[ActivitiesSection] loadActivities failed', err))\n }, [loadActivities])\n\n React.useEffect(() => {\n const unresolvedIds = new Set<string>()\n for (const a of activities) {\n if (a.authorUserId && !a.authorName && !resolvedUserIdsRef.current.has(a.authorUserId)) {\n unresolvedIds.add(a.authorUserId)\n }\n }\n if (unresolvedIds.size === 0) return\n\n for (const uid of unresolvedIds) resolvedUserIdsRef.current.add(uid)\n\n const controller = new AbortController()\n readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n `/api/auth/users?ids=${[...unresolvedIds].join(',')}`,\n { signal: controller.signal },\n )\n .then((data) => {\n const users = Array.isArray(data?.items) ? data.items : []\n const nameMap = new Map<string, string>()\n for (const user of users) {\n const userId = typeof user.id === 'string' ? user.id : null\n const name = typeof user.display_name === 'string' && user.display_name.trim()\n ? user.display_name.trim()\n : typeof user.email === 'string'\n ? user.email\n : null\n if (userId && name) nameMap.set(userId, name)\n }\n if (nameMap.size > 0) {\n setActivities((prev) =>\n prev.map((a) => {\n if (a.authorUserId && !a.authorName && nameMap.has(a.authorUserId)) {\n return { ...a, authorName: nameMap.get(a.authorUserId) ?? null }\n }\n return a\n }),\n )\n }\n })\n .catch((err) => console.warn('[ActivitiesSection] resolve author names failed', err))\n return () => controller.abort()\n }, [activities])\n\n const totalCount = activities.length\n const visibleCount = visibleActivities.length\n\n return (\n <div className=\"flex flex-col gap-3.5 rounded-[10px] border border-border bg-card pt-4 pb-[18px] px-[18px]\">\n <div className=\"flex items-center gap-2\">\n <Clock className=\"size-[15px] text-muted-foreground\" />\n <h3 className=\"text-[13px] font-semibold text-foreground\">\n {entityName\n ? t('customers.timeline.history.title', 'Interaction history with {{name}}', { name: entityName })\n : t('customers.timeline.history.titleGeneric', 'Interaction history')}\n </h3>\n </div>\n\n <label className=\"relative flex items-center\">\n <Search className=\"pointer-events-none absolute left-2.5 size-5 text-muted-foreground\" aria-hidden />\n <input\n ref={searchInputRef}\n type=\"search\"\n value={searchTerm}\n onChange={(event) => setSearchTerm(event.target.value)}\n placeholder={t('customers.timeline.history.searchPlaceholder', 'Search...')}\n aria-label={t('customers.timeline.history.searchAriaLabel', 'Search interaction history')}\n className=\"h-9 w-full rounded-[10px] border border-border bg-card pl-9 pr-14 text-sm text-foreground shadow-xs placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring [&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden\"\n />\n <Kbd className=\"pointer-events-none absolute right-2 hidden text-[11px] uppercase tracking-[0.48px] sm:inline-flex\">\n \u23181\n </Kbd>\n </label>\n\n <ActivityTimelineFilters\n entityId={entityId}\n activeTypes={filterTypes}\n dateFrom={filterDateFrom}\n dateTo={filterDateTo}\n onTypesChange={setFilterTypes}\n onDateFromChange={setFilterDateFrom}\n onDateToChange={setFilterDateTo}\n onReset={() => {\n setFilterTypes([])\n setFilterDateFrom('')\n setFilterDateTo('')\n }}\n />\n\n {loading && totalCount === 0 ? (\n <div className=\"rounded-lg border border-dashed border-border/70 px-4 py-8 text-sm text-muted-foreground\">\n {t('customers.people.detail.activities.loading', 'Loading activities\u2026')}\n </div>\n ) : (\n <>\n <ActivityTimeline\n activities={visibleActivities}\n onEdit={onEditActivity}\n onMarkDone={handleMarkDone}\n />\n {totalCount > 0 ? (\n <div className=\"flex items-center justify-between gap-3 border-t border-border/60 pt-3\">\n <span className=\"text-xs text-muted-foreground\">\n {searchTerm.trim()\n ? t('customers.activities.seeMatching', 'Showing {visible} of {total} activities', {\n visible: visibleCount,\n total: totalCount,\n })\n : t('customers.activities.seeAll', 'See all {count} activities', { count: totalCount })}\n </span>\n {hasMore ? (\n <Button type=\"button\" variant=\"link\" size=\"sm\" onClick={() => setLoadedPages((value) => value + 1)}>\n {t('customers.activities.loadMore', 'Load more')}\n </Button>\n ) : null}\n </div>\n ) : null}\n </>\n )}\n </div>\n )\n}\n\nexport default ActivitiesSection\n"],
5
+ "mappings": ";AA4VM,SA6CE,UA5CA,KADF;AA1VN,YAAY,WAAW;AACvB,SAAS,OAAO,cAAc;AAC9B,SAAS,YAAY;AACrB,SAAS,gBAAgB,4BAA4B;AACrD,SAAS,aAAa;AAEtB,SAAS,cAAc;AACvB,SAAS,WAAW;AACpB,SAAS,+BAA+B;AACxC,SAAS,wBAAwB;AA0BjC,SAAS,WAAW,OAA0C;AAC5D,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,KAAK,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE;AAC3E;AAEA,SAAS,wBAAwB,UAA+C;AAC9E,SAAO;AAAA,IACL,IAAI,SAAS;AAAA,IACb,iBAAiB,SAAS;AAAA,IAC1B,OAAO,SAAS,WAAW;AAAA,IAC3B,MAAM,SAAS,QAAQ;AAAA,IACvB,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,YAAY,SAAS,cAAc;AAAA,IACnC,UAAU;AAAA,IACV,cAAc,SAAS,gBAAgB;AAAA,IACvC,aAAa;AAAA,IACb,gBAAgB,SAAS,kBAAkB;AAAA,IAC3C,iBAAiB,SAAS,mBAAmB;AAAA,IAC7C,QAAQ;AAAA,IACR,UAAU,SAAS,YAAY;AAAA,IAC/B,QAAQ,SAAS,UAAU;AAAA,IAC3B,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,YAAY,SAAS,cAAc;AAAA,IACnC,aAAa,SAAS,eAAe;AAAA,IACrC,WAAW,SAAS,aAAa;AAAA,IACjC,cAAc,SAAS,gBAAgB;AAAA,IACvC,WAAW,SAAS;AAAA,IACpB,WAAW,SAAS;AAAA,EACtB;AACF;AAEA,SAAS,uBAAuB,OAAmD;AACjF,QAAM,MAAM,KAAK,IAAI;AACrB,SAAO,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,UAAU;AACtC,UAAM,gBAAgB,KAAK,cAAc,IAAI,KAAK,KAAK,WAAW,EAAE,QAAQ,IAAI,OAAO;AACvF,UAAM,iBAAiB,MAAM,cAAc,IAAI,KAAK,MAAM,WAAW,EAAE,QAAQ,IAAI,OAAO;AAC1F,UAAM,gBAAgB,KAAK,WAAW,aAAa,OAAO,SAAS,aAAa;AAChF,UAAM,iBAAiB,MAAM,WAAW,aAAa,OAAO,SAAS,cAAc;AACnF,UAAM,iBAAiB,iBAAiB,iBAAiB;AACzD,UAAM,kBAAkB,kBAAkB,kBAAkB;AAE5D,QAAI,mBAAmB,iBAAiB;AACtC,aAAO,iBAAiB,KAAK;AAAA,IAC/B;AAEA,QAAI,kBAAkB,iBAAiB;AACrC,UAAI,kBAAkB,eAAgB,QAAO,KAAK,GAAG,cAAc,MAAM,EAAE;AAC3E,aAAO,gBAAgB;AAAA,IACzB;AAEA,UAAM,WAAW,KAAK,cAAc,KAAK;AACzC,UAAM,YAAY,MAAM,cAAc,MAAM;AAC5C,UAAM,UAAU,UAAU,cAAc,QAAQ;AAChD,QAAI,YAAY,EAAG,QAAO;AAC1B,WAAO,MAAM,GAAG,cAAc,KAAK,EAAE;AAAA,EACvC,CAAC;AACH;AAEO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA,2BAA2B;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AACF,GAA2B;AACzB,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAmB,CAAC,CAAC;AACjE,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAS,EAAE;AAC7D,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,EAAE;AACzD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,EAAE;AACrD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAA+B,CAAC,CAAC;AAC3E,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,CAAC;AACtD,QAAM,iBAAiB,MAAM,OAAyB,IAAI;AAE1D,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,SAAU;AACf,aAAS,eAAe,OAAsB;AAC5C,WAAK,MAAM,WAAW,MAAM,YAAY,MAAM,QAAQ,KAAK;AACzD,cAAM,eAAe;AACrB,uBAAe,SAAS,MAAM;AAAA,MAChC;AAAA,IACF;AACA,WAAO,iBAAiB,WAAW,cAAc;AACjD,WAAO,MAAM,OAAO,oBAAoB,WAAW,cAAc;AAAA,EACnE,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,oBAAoB,MAAM,QAAQ,MAAM;AAC5C,UAAM,OAAO,WAAW,KAAK,EAAE,YAAY;AAC3C,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO,WAAW,OAAO,CAAC,aAAa;AACrC,YAAM,WAAW;AAAA,QACf,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,MACX,EACG,OAAO,CAAC,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC,EAChF,KAAK,GAAG,EACR,YAAY;AACf,aAAO,SAAS,SAAS,IAAI;AAAA,IAC/B,CAAC;AAAA,EACH,GAAG,CAAC,YAAY,UAAU,CAAC;AAE3B,QAAM,UAAU,MAAM;AACpB,qBAAiB,IAAI;AACrB,WAAO,MAAM,iBAAiB,IAAI;AAAA,EACpC,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,UAAU,MAAM;AACpB,sBAAkB,OAAO;AAAA,EAC3B,GAAG,CAAC,SAAS,eAAe,CAAC;AAE7B,QAAM,iBAAiB,MAAM,YAAY,YAAY;AACnD,QAAI,CAAC,UAAU;AACb,oBAAc,CAAC,CAAC;AAChB;AAAA,IACF;AAEA,eAAW,IAAI;AACf,QAAI;AAEF,YAAM,mBAAmB,YAAY,SAAS,MAAM;AACpD,YAAM,kBAAkB,IAAI,gBAAgB;AAAA,QAC1C;AAAA,QACA,OAAO;AAAA,QACP,WAAW;AAAA,QACX,SAAS;AAAA,MACX,CAAC;AAID,UAAI,CAAC,iBAAkB,iBAAgB,IAAI,0BAA0B,MAAM;AAC3E,UAAI,OAAQ,iBAAgB,IAAI,UAAU,MAAM;AAChD,UAAI,YAAY,SAAS,EAAG,iBAAgB,IAAI,QAAQ,YAAY,KAAK,GAAG,CAAC;AAC7E,UAAI,eAAgB,iBAAgB,IAAI,QAAQ,cAAc;AAC9D,UAAI,aAAc,iBAAgB,IAAI,MAAM,YAAY;AAExD,YAAM,iBAAuC,CAAC;AAC9C,UAAI;AACJ,UAAI,mBAAmB;AACvB,UAAI,YAAY;AAChB,SAAG;AACD,cAAM,SAAS,IAAI,gBAAgB,eAAe;AAClD,YAAI,gBAAiB,QAAO,IAAI,UAAU,eAAe;AACzD,cAAM,mBAAmB,MAAM;AAAA,UAC7B,+BAA+B,OAAO,SAAS,CAAC;AAAA,QAClD,EAAE,MAAM,OAAO,EAAE,OAAO,CAAC,GAA2B,YAAY,OAAU,EAAE;AAC5E,uBAAe,KAAK,GAAI,MAAM,QAAQ,kBAAkB,KAAK,IAAI,iBAAiB,QAAQ,CAAC,CAAE;AAC7F,0BAAkB,OAAO,kBAAkB,eAAe,WAAW,iBAAiB,aAAa;AACnG,2BAAmB,QAAQ,eAAe;AAC1C,qBAAa;AAAA,MACf,SAAS,mBAAmB,YAAY;AAExC,UAAI,0BAA0B;AAC5B,sBAAc,uBAAuB,cAAc,CAAC;AACpD,mBAAW,gBAAgB;AAC3B;AAAA,MACF;AAKA,YAAM,oBAAoB,MAAM,KAAK,EAAE,QAAQ,YAAY,GAAG,CAAC,GAAG,UAAU,QAAQ,CAAC;AACrF,YAAM,iBAAiB,MAAM,QAAQ;AAAA,QACnC,kBAAkB,IAAI,CAAC,eAAe;AACpC,gBAAM,eAAe,IAAI,gBAAgB;AAAA,YACvC;AAAA,YACA,MAAM,OAAO,UAAU;AAAA,YACvB,UAAU;AAAA,YACV,WAAW;AAAA,YACX,SAAS;AAAA,UACX,CAAC;AACD,cAAI,OAAQ,cAAa,IAAI,UAAU,MAAM;AAC7C,iBAAO;AAAA,YACL,6BAA6B,aAAa,SAAS,CAAC;AAAA,UACtD,EAAE,MAAM,OAAO,EAAE,OAAO,CAAC,GAAwB,YAAY,EAAE,EAAE;AAAA,QACnE,CAAC;AAAA,MACH;AAGA,YAAM,cAAoC,CAAC;AAC3C,UAAI,mBAAmB;AACvB,iBAAW,iBAAiB,gBAAgB;AAC1C,oBAAY,KAAK,GAAI,MAAM,QAAQ,eAAe,KAAK,IAAI,cAAc,MAAM,IAAI,uBAAuB,IAAI,CAAC,CAAE;AACjH,2BAAmB,OAAO,eAAe,eAAe,WAAW,cAAc,aAAa;AAAA,MAChG;AACA,YAAM,iBAAiB,YAAY,OAAO,CAAC,UAAU;AACnD,YAAI,YAAY,SAAS,KAAK,CAAC,YAAY,SAAS,MAAM,eAAe,EAAG,QAAO;AACnF,cAAM,WAAW,WAAW,MAAM,cAAc,MAAM,SAAS;AAC/D,YAAI,kBAAkB,WAAW,eAAgB,QAAO;AACxD,YAAI,gBAAgB,WAAW,aAAc,QAAO;AACpD,eAAO;AAAA,MACT,CAAC;AAGD,YAAM,OAAO,oBAAI,IAAY;AAC7B,YAAM,SAA+B,CAAC;AACtC,iBAAW,QAAQ,CAAC,GAAG,gBAAgB,GAAG,cAAc,GAAG;AACzD,YAAI,CAAC,KAAK,IAAI,KAAK,EAAE,GAAG;AACtB,eAAK,IAAI,KAAK,EAAE;AAChB,iBAAO,KAAK,IAAI;AAAA,QAClB;AAAA,MACF;AACA,oBAAc,uBAAuB,MAAM,CAAC;AAC5C,iBAAW,oBAAoB,mBAAmB,WAAW;AAAA,IAC/D,SAAS,OAAO;AACd,cAAQ,MAAM,uCAAuC,KAAK;AAC1D,YAAM,EAAE,mCAAmC,4BAA4B,GAAG,OAAO;AACjF,oBAAc,CAAC,CAAC;AAChB,iBAAW,KAAK;AAAA,IAClB,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,gBAAgB,cAAc,aAAa,aAAa,0BAA0B,YAAY,CAAC,CAAC;AAEtH,QAAM,UAAU,MAAM;AACpB,mBAAe,CAAC;AAAA,EAClB,GAAG,CAAC,QAAQ,UAAU,gBAAgB,cAAc,aAAa,wBAAwB,CAAC;AAE1F,QAAM,iBAAiB,MAAM,YAAY,OAAO,eAAuB;AACrE,QAAI;AACF,YAAM,YAAY,MAChB,eAAe,wCAAwC;AAAA,QACrD,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,IAAI,YAAY,aAAY,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC;AAAA,MAC/E,CAAC;AACH,UAAI,oBAAoB;AACtB,cAAM,mBAAmB,WAAW;AAAA,UAClC,IAAI;AAAA,UACJ,QAAQ;AAAA,UACR,WAAW;AAAA,QACb,CAAC;AAAA,MACH,OAAO;AACL,cAAM,UAAU;AAAA,MAClB;AACA,YAAM,EAAE,gDAAgD,sBAAsB,GAAG,SAAS;AAC1F,YAAM,eAAe;AAAA,IACvB,SAAS,KAAK;AACZ,cAAQ,KAAK,kDAAkD,YAAY,GAAG;AAC9E,YAAM,EAAE,8CAA8C,iCAAiC,GAAG,OAAO;AAAA,IACnG;AAAA,EACF,GAAG,CAAC,gBAAgB,oBAAoB,CAAC,CAAC;AAE1C,QAAM,qBAAqB,MAAM,OAAO,oBAAI,IAAY,CAAC;AAGzD,QAAM,UAAU,MAAM;AACpB,mBAAe,EACZ,KAAK,MAAM;AAAE,yBAAmB,UAAU,oBAAI,IAAI;AAAA,IAAE,CAAC,EACrD,MAAM,CAAC,QAAQ,QAAQ,KAAK,6CAA6C,GAAG,CAAC;AAAA,EAClF,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,UAAU,MAAM;AACpB,UAAM,gBAAgB,oBAAI,IAAY;AACtC,eAAW,KAAK,YAAY;AAC1B,UAAI,EAAE,gBAAgB,CAAC,EAAE,cAAc,CAAC,mBAAmB,QAAQ,IAAI,EAAE,YAAY,GAAG;AACtF,sBAAc,IAAI,EAAE,YAAY;AAAA,MAClC;AAAA,IACF;AACA,QAAI,cAAc,SAAS,EAAG;AAE9B,eAAW,OAAO,cAAe,oBAAmB,QAAQ,IAAI,GAAG;AAEnE,UAAM,aAAa,IAAI,gBAAgB;AACvC;AAAA,MACE,uBAAuB,CAAC,GAAG,aAAa,EAAE,KAAK,GAAG,CAAC;AAAA,MACnD,EAAE,QAAQ,WAAW,OAAO;AAAA,IAC9B,EACG,KAAK,CAAC,SAAS;AACd,YAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,IAAI,KAAK,QAAQ,CAAC;AACzD,YAAM,UAAU,oBAAI,IAAoB;AACxC,iBAAW,QAAQ,OAAO;AACxB,cAAM,SAAS,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;AACvD,cAAM,OAAO,OAAO,KAAK,iBAAiB,YAAY,KAAK,aAAa,KAAK,IACzE,KAAK,aAAa,KAAK,IACvB,OAAO,KAAK,UAAU,WACpB,KAAK,QACL;AACN,YAAI,UAAU,KAAM,SAAQ,IAAI,QAAQ,IAAI;AAAA,MAC9C;AACA,UAAI,QAAQ,OAAO,GAAG;AACpB;AAAA,UAAc,CAAC,SACb,KAAK,IAAI,CAAC,MAAM;AACd,gBAAI,EAAE,gBAAgB,CAAC,EAAE,cAAc,QAAQ,IAAI,EAAE,YAAY,GAAG;AAClE,qBAAO,EAAE,GAAG,GAAG,YAAY,QAAQ,IAAI,EAAE,YAAY,KAAK,KAAK;AAAA,YACjE;AACA,mBAAO;AAAA,UACT,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC,EACA,MAAM,CAAC,QAAQ,QAAQ,KAAK,mDAAmD,GAAG,CAAC;AACtF,WAAO,MAAM,WAAW,MAAM;AAAA,EAChC,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,aAAa,WAAW;AAC9B,QAAM,eAAe,kBAAkB;AAEvC,SACE,qBAAC,SAAI,WAAU,8FACb;AAAA,yBAAC,SAAI,WAAU,2BACb;AAAA,0BAAC,SAAM,WAAU,qCAAoC;AAAA,MACrD,oBAAC,QAAG,WAAU,6CACX,uBACG,EAAE,oCAAoC,qCAAqC,EAAE,MAAM,WAAW,CAAC,IAC/F,EAAE,2CAA2C,qBAAqB,GACxE;AAAA,OACF;AAAA,IAEA,qBAAC,WAAM,WAAU,8BACf;AAAA,0BAAC,UAAO,WAAU,sEAAqE,eAAW,MAAC;AAAA,MACnG;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,UAAU,cAAc,MAAM,OAAO,KAAK;AAAA,UACrD,aAAa,EAAE,gDAAgD,WAAW;AAAA,UAC1E,cAAY,EAAE,8CAA8C,4BAA4B;AAAA,UACxF,WAAU;AAAA;AAAA,MACZ;AAAA,MACA,oBAAC,OAAI,WAAU,sGAAqG,qBAEpH;AAAA,OACF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,aAAa;AAAA,QACb,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,eAAe;AAAA,QACf,kBAAkB;AAAA,QAClB,gBAAgB;AAAA,QAChB,SAAS,MAAM;AACb,yBAAe,CAAC,CAAC;AACjB,4BAAkB,EAAE;AACpB,0BAAgB,EAAE;AAAA,QACpB;AAAA;AAAA,IACF;AAAA,IAEC,WAAW,eAAe,IACzB,oBAAC,SAAI,WAAU,4FACZ,YAAE,8CAA8C,0BAAqB,GACxE,IAEA,iCACE;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,YAAY;AAAA,UACZ,QAAQ;AAAA,UACR,YAAY;AAAA;AAAA,MACd;AAAA,MACC,aAAa,IACZ,qBAAC,SAAI,WAAU,0EACb;AAAA,4BAAC,UAAK,WAAU,iCACb,qBAAW,KAAK,IACb,EAAE,oCAAoC,2CAA2C;AAAA,UAC/E,SAAS;AAAA,UACT,OAAO;AAAA,QACT,CAAC,IACD,EAAE,+BAA+B,8BAA8B,EAAE,OAAO,WAAW,CAAC,GAC1F;AAAA,QACC,UACC,oBAAC,UAAO,MAAK,UAAS,SAAQ,QAAO,MAAK,MAAK,SAAS,MAAM,eAAe,CAAC,UAAU,QAAQ,CAAC,GAC9F,YAAE,iCAAiC,WAAW,GACjD,IACE;AAAA,SACN,IACE;AAAA,OACN;AAAA,KAEJ;AAEJ;AAEA,IAAO,4BAAQ;",
6
6
  "names": []
7
7
  }
@@ -197,17 +197,21 @@ function ActivityHistorySection({
197
197
  } while (nextCursor && pagesLoaded < loadedPages);
198
198
  let combined = canonicalItems;
199
199
  if (!useCanonicalInteractions) {
200
+ const legacyPageNumbers = Array.from({ length: loadedPages }, (_, index) => index + 1);
201
+ const legacyPayloads = await Promise.all(
202
+ legacyPageNumbers.map(
203
+ (legacyPage) => readApiResultOrThrow(
204
+ `/api/customers/activities?entityId=${encodeURIComponent(entityId)}&page=${legacyPage}&pageSize=20&sortField=occurredAt&sortDir=desc`,
205
+ { signal }
206
+ ).catch(() => ({ items: [], totalPages: 1 }))
207
+ )
208
+ );
209
+ if (isStale()) return;
200
210
  const legacyItems = [];
201
211
  let legacyTotalPages = 1;
202
- for (let legacyPage = 1; legacyPage <= loadedPages; legacyPage += 1) {
203
- const legacyPayload = await readApiResultOrThrow(
204
- `/api/customers/activities?entityId=${encodeURIComponent(entityId)}&page=${legacyPage}&pageSize=20&sortField=occurredAt&sortDir=desc`,
205
- { signal }
206
- ).catch(() => ({ items: [], totalPages: 1 }));
207
- if (isStale()) return;
212
+ for (const legacyPayload of legacyPayloads) {
208
213
  legacyItems.push(...Array.isArray(legacyPayload.items) ? legacyPayload.items.map(normalizeLegacyActivity) : []);
209
214
  legacyTotalPages = typeof legacyPayload.totalPages === "number" ? legacyPayload.totalPages : legacyTotalPages;
210
- if (legacyPage >= legacyTotalPages) break;
211
215
  }
212
216
  const rangeStartDate = computeRangeStart(dateRange);
213
217
  const filteredLegacy = legacyItems.filter((item) => {