@dainamite/cpq 0.6.0 → 0.7.0

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 (164) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js +3 -9
  3. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +2 -2
  4. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js +21 -25
  5. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +2 -2
  6. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js +48 -47
  7. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js.map +2 -2
  8. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js +1 -1
  9. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js.map +2 -2
  10. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +2 -2
  11. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +2 -2
  12. package/dist/modules/cpq/backend/cpq/inventory/shared.js +37 -2
  13. package/dist/modules/cpq/backend/cpq/inventory/shared.js.map +2 -2
  14. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +154 -178
  15. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
  16. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js +1 -1
  17. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js.map +2 -2
  18. package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +168 -143
  19. package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +2 -2
  20. package/dist/modules/cpq/backend/cpq/offerings/page.js +36 -34
  21. package/dist/modules/cpq/backend/cpq/offerings/page.js.map +2 -2
  22. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +69 -66
  23. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +2 -2
  24. package/dist/modules/cpq/backend/cpq/orders/page.js +6 -6
  25. package/dist/modules/cpq/backend/cpq/orders/page.js.map +2 -2
  26. package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js +41 -36
  27. package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js.map +2 -2
  28. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js +76 -72
  29. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js.map +2 -2
  30. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js +1 -1
  31. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js.map +2 -2
  32. package/dist/modules/cpq/backend/cpq/price-rules/page.js +18 -15
  33. package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +2 -2
  34. package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +23 -21
  35. package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +2 -2
  36. package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js +22 -50
  37. package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js.map +2 -2
  38. package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js +74 -52
  39. package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js.map +2 -2
  40. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js +20 -17
  41. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js.map +2 -2
  42. package/dist/modules/cpq/backend/cpq/pricing/page.js +1 -1
  43. package/dist/modules/cpq/backend/cpq/pricing/page.js.map +2 -2
  44. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js +115 -123
  45. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js.map +2 -2
  46. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js +6 -3
  47. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js.map +2 -2
  48. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +129 -94
  49. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +2 -2
  50. package/dist/modules/cpq/backend/cpq/quotes/page.js +23 -19
  51. package/dist/modules/cpq/backend/cpq/quotes/page.js.map +2 -2
  52. package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js +19 -15
  53. package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js.map +2 -2
  54. package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +171 -178
  55. package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +2 -2
  56. package/dist/modules/cpq/backend/cpq/specifications/page.js +2 -2
  57. package/dist/modules/cpq/backend/cpq/specifications/page.js.map +2 -2
  58. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js +60 -50
  59. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js.map +2 -2
  60. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js +22 -18
  61. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js.map +2 -2
  62. package/dist/modules/cpq/backend/cpq/wizards/page.js +1 -1
  63. package/dist/modules/cpq/backend/cpq/wizards/page.js.map +2 -2
  64. package/dist/modules/cpq/components/ArcLineDiff.js +2 -2
  65. package/dist/modules/cpq/components/ArcLineDiff.js.map +2 -2
  66. package/dist/modules/cpq/components/ChargeBreakdownTable.js +4 -10
  67. package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +2 -2
  68. package/dist/modules/cpq/components/PriceWaterfall.js +3 -1
  69. package/dist/modules/cpq/components/PriceWaterfall.js.map +2 -2
  70. package/dist/modules/cpq/components/PricingSummaryCard.js +1 -6
  71. package/dist/modules/cpq/components/PricingSummaryCard.js.map +2 -2
  72. package/dist/modules/cpq/components/StatusPath.js +5 -3
  73. package/dist/modules/cpq/components/StatusPath.js.map +2 -2
  74. package/dist/modules/cpq/components/useCpqRowActions.js +10 -22
  75. package/dist/modules/cpq/components/useCpqRowActions.js.map +2 -2
  76. package/dist/modules/cpq/i18n/de.json +1220 -0
  77. package/dist/modules/cpq/i18n/en.json +1220 -0
  78. package/dist/modules/cpq/i18n/es.json +1220 -0
  79. package/dist/modules/cpq/i18n/pl.json +1220 -0
  80. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js +39 -31
  81. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js.map +2 -2
  82. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js +13 -5
  83. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js.map +2 -2
  84. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js +41 -21
  85. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js.map +2 -2
  86. package/dist/modules/cpq/widgets/injection-table.js +2 -2
  87. package/dist/modules/cpq/widgets/injection-table.js.map +2 -2
  88. package/dist/modules/cpq/workflows/WizardRunner.js +10 -8
  89. package/dist/modules/cpq/workflows/WizardRunner.js.map +2 -2
  90. package/dist/modules/cpq/workflows/WizardTrigger.js +15 -9
  91. package/dist/modules/cpq/workflows/WizardTrigger.js.map +2 -2
  92. package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js +9 -7
  93. package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js.map +2 -2
  94. package/dist/modules/cpq/workflows/steps/InventorySelectStep.js +9 -4
  95. package/dist/modules/cpq/workflows/steps/InventorySelectStep.js.map +2 -2
  96. package/dist/modules/cpq/workflows/steps/ItemListStep.js +16 -19
  97. package/dist/modules/cpq/workflows/steps/ItemListStep.js.map +2 -2
  98. package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js +19 -32
  99. package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js.map +2 -2
  100. package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js +8 -6
  101. package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js.map +2 -2
  102. package/dist/modules/cpq/workflows/steps/ReviewStep.js +32 -30
  103. package/dist/modules/cpq/workflows/steps/ReviewStep.js.map +2 -2
  104. package/dist/modules/cpq/workflows/steps/index.js +12 -0
  105. package/dist/modules/cpq/workflows/steps/index.js.map +2 -2
  106. package/package.json +1 -1
  107. package/src/modules/cpq/__tests__/i18n/dictionaries.test.ts +62 -0
  108. package/src/modules/cpq/__tests__/i18n/hardcodedStrings.test.ts +35 -0
  109. package/src/modules/cpq/__tests__/i18n/sourceScan.ts +109 -0
  110. package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +3 -9
  111. package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +22 -26
  112. package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.ts +1 -1
  113. package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.tsx +43 -46
  114. package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +2 -2
  115. package/src/modules/cpq/backend/cpq/inventory/shared.tsx +37 -1
  116. package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.ts +1 -1
  117. package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +134 -112
  118. package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +169 -130
  119. package/src/modules/cpq/backend/cpq/offerings/page.tsx +34 -24
  120. package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +66 -43
  121. package/src/modules/cpq/backend/cpq/orders/page.tsx +6 -6
  122. package/src/modules/cpq/backend/cpq/price-rules/PriceRuleForm.tsx +48 -34
  123. package/src/modules/cpq/backend/cpq/price-rules/[id]/page.meta.ts +1 -1
  124. package/src/modules/cpq/backend/cpq/price-rules/[id]/page.tsx +76 -70
  125. package/src/modules/cpq/backend/cpq/price-rules/page.tsx +25 -16
  126. package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +24 -18
  127. package/src/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.tsx +29 -19
  128. package/src/modules/cpq/backend/cpq/pricing/[id]/page.tsx +95 -47
  129. package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.ts +23 -15
  130. package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
  131. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.tsx +111 -88
  132. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.tsx +3 -3
  133. package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +134 -89
  134. package/src/modules/cpq/backend/cpq/quotes/page.tsx +29 -21
  135. package/src/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.tsx +19 -15
  136. package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +171 -171
  137. package/src/modules/cpq/backend/cpq/specifications/page.tsx +2 -2
  138. package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.tsx +51 -41
  139. package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/page.tsx +17 -13
  140. package/src/modules/cpq/backend/cpq/wizards/page.tsx +1 -1
  141. package/src/modules/cpq/components/ArcLineDiff.tsx +2 -2
  142. package/src/modules/cpq/components/ChargeBreakdownTable.tsx +4 -2
  143. package/src/modules/cpq/components/PriceWaterfall.tsx +4 -2
  144. package/src/modules/cpq/components/PricingSummaryCard.tsx +1 -1
  145. package/src/modules/cpq/components/StatusPath.tsx +7 -4
  146. package/src/modules/cpq/components/useCpqRowActions.tsx +17 -25
  147. package/src/modules/cpq/i18n/de.json +1220 -0
  148. package/src/modules/cpq/i18n/en.json +1220 -0
  149. package/src/modules/cpq/i18n/es.json +1220 -0
  150. package/src/modules/cpq/i18n/pl.json +1220 -0
  151. package/src/modules/cpq/widgets/injection/customer-inventory/widget.client.tsx +38 -31
  152. package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.client.tsx +7 -5
  153. package/src/modules/cpq/widgets/injection/quote-configurator/widget.client.tsx +36 -21
  154. package/src/modules/cpq/widgets/injection-table.ts +8 -2
  155. package/src/modules/cpq/workflows/WizardRunner.tsx +8 -5
  156. package/src/modules/cpq/workflows/WizardTrigger.tsx +15 -9
  157. package/src/modules/cpq/workflows/steps/CustomerSelectStep.tsx +11 -7
  158. package/src/modules/cpq/workflows/steps/InventorySelectStep.tsx +9 -5
  159. package/src/modules/cpq/workflows/steps/ItemListStep.tsx +18 -12
  160. package/src/modules/cpq/workflows/steps/OfferingSelectStep.tsx +20 -16
  161. package/src/modules/cpq/workflows/steps/ProductConfigureStep.tsx +8 -6
  162. package/src/modules/cpq/workflows/steps/ReviewStep.tsx +33 -18
  163. package/src/modules/cpq/workflows/steps/index.ts +12 -0
  164. package/src/modules/cpq/workflows/types.ts +4 -0
@@ -27,13 +27,15 @@ function buildFilterParams(values, params) {
27
27
  params.set("currencyCode", values.currencyCode.trim().toUpperCase());
28
28
  }
29
29
  }
30
- function getCustomerLabel(c) {
31
- return c.display_name || c.displayName || c.name || c.companyName || "Unnamed";
30
+ function getCustomerLabel(c, unnamedLabel) {
31
+ return c.display_name || c.displayName || c.name || c.companyName || unnamedLabel;
32
32
  }
33
33
  function getCustomerEmail(c) {
34
34
  return c.primary_email || c.primaryEmail || "";
35
35
  }
36
36
  function CustomerPickerModal({ open, onClose, onSelect, creating, error }) {
37
+ const t = useT();
38
+ const unnamedLabel = t("cpq.quotes.customerPicker.unnamed", "Unnamed");
37
39
  const [customers, setCustomers] = React.useState([]);
38
40
  const [customersLoading, setCustomersLoading] = React.useState(false);
39
41
  const [search, setSearch] = React.useState("");
@@ -57,7 +59,7 @@ function CustomerPickerModal({ open, onClose, onSelect, creating, error }) {
57
59
  const filtered = customers.filter((c) => {
58
60
  if (!search.trim()) return true;
59
61
  const q = search.toLowerCase();
60
- return getCustomerLabel(c).toLowerCase().includes(q) || getCustomerEmail(c).toLowerCase().includes(q) || c.id.toLowerCase().includes(q);
62
+ return getCustomerLabel(c, unnamedLabel).toLowerCase().includes(q) || getCustomerEmail(c).toLowerCase().includes(q) || c.id.toLowerCase().includes(q);
61
63
  });
62
64
  return /* @__PURE__ */ jsx(
63
65
  "div",
@@ -71,7 +73,7 @@ function CustomerPickerModal({ open, onClose, onSelect, creating, error }) {
71
73
  onClick: (e) => e.stopPropagation(),
72
74
  children: [
73
75
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
74
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: "Select Customer for New Quote" }),
76
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: t("cpq.quotes.customerPicker.title", "Select Customer for New Quote") }),
75
77
  /* @__PURE__ */ jsx(
76
78
  "button",
77
79
  {
@@ -90,7 +92,7 @@ function CustomerPickerModal({ open, onClose, onSelect, creating, error }) {
90
92
  /* @__PURE__ */ jsx(
91
93
  Input,
92
94
  {
93
- placeholder: "Search customers\u2026",
95
+ placeholder: t("cpq.quotes.customerPicker.searchPlaceholder", "Search customers\u2026"),
94
96
  value: search,
95
97
  onChange: (e) => setSearch(e.target.value),
96
98
  autoFocus: true,
@@ -98,7 +100,7 @@ function CustomerPickerModal({ open, onClose, onSelect, creating, error }) {
98
100
  }
99
101
  )
100
102
  ] }),
101
- /* @__PURE__ */ jsx("div", { className: "max-h-72 overflow-y-auto space-y-1", children: customersLoading ? /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-sm text-muted-foreground", children: "Loading customers\u2026" }) : filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-sm text-muted-foreground", children: search ? "No customers match your search." : "No customers found." }) : filtered.map((c) => /* @__PURE__ */ jsxs(
103
+ /* @__PURE__ */ jsx("div", { className: "max-h-72 overflow-y-auto space-y-1", children: customersLoading ? /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-sm text-muted-foreground", children: t("cpq.quotes.customerPicker.loading", "Loading customers\u2026") }) : filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-sm text-muted-foreground", children: search ? t("cpq.quotes.customerPicker.noMatch", "No customers match your search.") : t("cpq.quotes.customerPicker.empty", "No customers found.") }) : filtered.map((c) => /* @__PURE__ */ jsxs(
102
104
  "button",
103
105
  {
104
106
  type: "button",
@@ -106,7 +108,7 @@ function CustomerPickerModal({ open, onClose, onSelect, creating, error }) {
106
108
  disabled: creating,
107
109
  className: "w-full rounded-md border px-3 py-2.5 text-left hover:border-primary hover:bg-primary/5 transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed",
108
110
  children: [
109
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: getCustomerLabel(c) }),
111
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: getCustomerLabel(c, unnamedLabel) }),
110
112
  getCustomerEmail(c) && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: getCustomerEmail(c) }),
111
113
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground font-mono mt-0.5", children: c.id })
112
114
  ]
@@ -134,17 +136,17 @@ function CpqQuotesListPage() {
134
136
  const [createError, setCreateError] = React.useState(null);
135
137
  const statusOptions = React.useMemo(
136
138
  () => [
137
- { value: "new", label: "New" },
138
- { value: "incomplete", label: "Incomplete" },
139
- { value: "ready", label: "Ready" },
140
- { value: "in_approval", label: "In Approval" },
141
- { value: "approved", label: "Approved" },
142
- { value: "with_customer", label: "With Customer" },
143
- { value: "accepted", label: "Accepted" },
144
- { value: "rejected", label: "Rejected" },
145
- { value: "cancelled", label: "Cancelled" }
139
+ { value: "new", label: t("cpq.quotes.status.new", "New") },
140
+ { value: "incomplete", label: t("cpq.quotes.status.incomplete", "Incomplete") },
141
+ { value: "ready", label: t("cpq.quotes.status.ready", "Ready") },
142
+ { value: "in_approval", label: t("cpq.quotes.status.in_approval", "In Approval") },
143
+ { value: "approved", label: t("cpq.quotes.status.approved", "Approved") },
144
+ { value: "with_customer", label: t("cpq.quotes.status.with_customer", "With Customer") },
145
+ { value: "accepted", label: t("cpq.quotes.status.accepted", "Accepted") },
146
+ { value: "rejected", label: t("cpq.quotes.status.rejected", "Rejected") },
147
+ { value: "cancelled", label: t("cpq.quotes.status.cancelled", "Cancelled") }
146
148
  ],
147
- []
149
+ [t]
148
150
  );
149
151
  const filters = React.useMemo(
150
152
  () => [
@@ -177,7 +179,9 @@ function CpqQuotesListPage() {
177
179
  setShowCustomerPicker(false);
178
180
  router.push(`/backend/cpq/quotes/${body.id}`);
179
181
  } else {
180
- setCreateError(body.error ?? `Failed to create quote (${res.status})`);
182
+ setCreateError(
183
+ body.error ?? t("cpq.quotes.create.error", "Failed to create quote ({status})", { status: res.status })
184
+ );
181
185
  }
182
186
  } catch (err) {
183
187
  setCreateError(err.message);
@@ -185,7 +189,7 @@ function CpqQuotesListPage() {
185
189
  setCreating(false);
186
190
  }
187
191
  },
188
- [router]
192
+ [router, t]
189
193
  );
190
194
  const columns = React.useMemo(
191
195
  () => [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/cpq/backend/cpq/quotes/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Plus, Search as SearchIcon } from 'lucide-react'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { CpqListView, useCpqListData } from '../../../components/CpqListView'\nimport {\n formatStatusLabel,\n quoteCpqStatusMap,\n type QuoteCpqStatus,\n} from '../../../components/statusMaps'\n\ntype QuoteConfig = {\n id: string\n quoteId: string\n /** Resolved from SalesQuote.quoteNumber by the list API. */\n quoteNumber: string | null\n customerId: string\n cpqStatus: string\n version: number\n currencyCode: string\n pricingSummary: {\n nrcTotal?: number\n mrcTotal?: number\n } | null\n createdAt: string\n updatedAt: string\n}\n\ntype Customer = {\n id: string\n display_name?: string\n displayName?: string\n name?: string\n companyName?: string\n primary_email?: string\n primaryEmail?: string\n}\n\nconst PAGE_SIZE = 50\n\nfunction fmt(amount: number | undefined, currency: string): string {\n if (amount == null || amount === 0) return '\u2014'\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.cpqStatus === 'string' && values.cpqStatus) {\n params.set('cpqStatus', values.cpqStatus)\n }\n if (typeof values.currencyCode === 'string' && values.currencyCode.trim()) {\n params.set('currencyCode', values.currencyCode.trim().toUpperCase())\n }\n}\n\n// \u2500\u2500\u2500 Customer picker (extracted to keep main page focused) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype CustomerPickerProps = {\n open: boolean\n onClose: () => void\n onSelect: (customer: Customer) => void | Promise<void>\n creating: boolean\n error: string | null\n}\n\nfunction getCustomerLabel(c: Customer) {\n return c.display_name || c.displayName || c.name || c.companyName || 'Unnamed'\n}\n\nfunction getCustomerEmail(c: Customer) {\n return c.primary_email || c.primaryEmail || ''\n}\n\nfunction CustomerPickerModal({ open, onClose, onSelect, creating, error }: CustomerPickerProps) {\n const [customers, setCustomers] = React.useState<Customer[]>([])\n const [customersLoading, setCustomersLoading] = React.useState(false)\n const [search, setSearch] = React.useState('')\n\n React.useEffect(() => {\n if (!open) return\n let cancelled = false\n setSearch('')\n setCustomersLoading(true)\n fetch('/api/customers/companies?pageSize=100')\n .then((r) => (r.ok ? r.json() : { items: [] }))\n .then((data) => {\n if (!cancelled) setCustomers(data.items ?? data ?? [])\n })\n .catch(() => {\n if (!cancelled) setCustomers([])\n })\n .finally(() => {\n if (!cancelled) setCustomersLoading(false)\n })\n return () => {\n cancelled = true\n }\n }, [open])\n\n if (!open) return null\n\n const filtered = customers.filter((c) => {\n if (!search.trim()) return true\n const q = search.toLowerCase()\n return (\n getCustomerLabel(c).toLowerCase().includes(q) ||\n getCustomerEmail(c).toLowerCase().includes(q) ||\n c.id.toLowerCase().includes(q)\n )\n })\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\"\n onClick={() => !creating && onClose()}\n >\n <div\n className=\"w-full max-w-lg rounded-lg border bg-card shadow-xl\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"flex items-center justify-between border-b px-4 py-3\">\n <h3 className=\"text-sm font-medium\">Select Customer for New Quote</h3>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={creating}\n className=\"text-muted-foreground hover:text-foreground text-lg font-bold leading-none cursor-pointer disabled:cursor-not-allowed\"\n >\n &times;\n </button>\n </div>\n <div className=\"p-4 space-y-3\">\n {error && <Alert variant=\"destructive\">{error}</Alert>}\n <div className=\"relative\">\n <SearchIcon className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <Input\n placeholder=\"Search customers\u2026\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n autoFocus\n className=\"pl-10\"\n />\n </div>\n <div className=\"max-h-72 overflow-y-auto space-y-1\">\n {customersLoading ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">\n Loading customers\u2026\n </div>\n ) : filtered.length === 0 ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">\n {search ? 'No customers match your search.' : 'No customers found.'}\n </div>\n ) : (\n filtered.map((c) => (\n <button\n key={c.id}\n type=\"button\"\n onClick={() => onSelect(c)}\n disabled={creating}\n className=\"w-full rounded-md border px-3 py-2.5 text-left hover:border-primary hover:bg-primary/5 transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed\"\n >\n <p className=\"text-sm font-medium\">{getCustomerLabel(c)}</p>\n {getCustomerEmail(c) && (\n <p className=\"text-xs text-muted-foreground mt-0.5\">{getCustomerEmail(c)}</p>\n )}\n <p className=\"text-xs text-muted-foreground font-mono mt-0.5\">{c.id}</p>\n </button>\n ))\n )}\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport default function CpqQuotesListPage() {\n const router = useRouter()\n const t = useT()\n\n const data = useCpqListData<QuoteConfig>({\n endpoint: '/api/cpq/quotes',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.quotes.list.error.load', 'Failed to load quotes'),\n })\n\n const [showCustomerPicker, setShowCustomerPicker] = React.useState(false)\n const [creating, setCreating] = React.useState(false)\n const [createError, setCreateError] = React.useState<string | null>(null)\n\n const statusOptions = React.useMemo(\n () => [\n { value: 'new', label: 'New' },\n { value: 'incomplete', label: 'Incomplete' },\n { value: 'ready', label: 'Ready' },\n { value: 'in_approval', label: 'In Approval' },\n { value: 'approved', label: 'Approved' },\n { value: 'with_customer', label: 'With Customer' },\n { value: 'accepted', label: 'Accepted' },\n { value: 'rejected', label: 'Rejected' },\n { value: 'cancelled', label: 'Cancelled' },\n ],\n [],\n )\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'cpqStatus',\n label: t('cpq.quotes.filters.status', 'Status'),\n type: 'select',\n options: statusOptions,\n },\n {\n id: 'currencyCode',\n label: t('cpq.quotes.filters.currency', 'Currency'),\n type: 'text',\n },\n ],\n [statusOptions, t],\n )\n\n const selectCustomer = React.useCallback(\n async (customer: Customer) => {\n setCreating(true)\n setCreateError(null)\n try {\n const res = await fetch('/api/cpq/quotes', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ customerId: customer.id }),\n })\n const body = await res.json()\n if (res.ok) {\n setShowCustomerPicker(false)\n router.push(`/backend/cpq/quotes/${body.id}`)\n } else {\n setCreateError(body.error ?? `Failed to create quote (${res.status})`)\n }\n } catch (err) {\n setCreateError((err as Error).message)\n } finally {\n setCreating(false)\n }\n },\n [router],\n )\n\n const columns = React.useMemo<ColumnDef<QuoteConfig>[]>(\n () => [\n {\n accessorKey: 'quoteNumber',\n header: t('cpq.quotes.table.quoteNumber', 'Quote Number'),\n cell: ({ row }) => (\n <span className=\"font-medium\">\n {row.original.quoteNumber ?? (\n <span className=\"font-mono text-xs text-muted-foreground\">\n {(row.original.quoteId ?? row.original.id).slice(0, 8)}\u2026\n </span>\n )}\n </span>\n ),\n },\n {\n accessorKey: 'cpqStatus',\n header: t('cpq.quotes.table.status', 'Status'),\n cell: ({ row }) => (\n <Tag variant={quoteCpqStatusMap[row.original.cpqStatus as QuoteCpqStatus] ?? 'neutral'} dot>\n {formatStatusLabel(row.original.cpqStatus)}\n </Tag>\n ),\n },\n {\n accessorKey: 'version',\n header: t('cpq.quotes.table.version', 'Version'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">v{row.original.version}</span>,\n },\n {\n accessorKey: 'currencyCode',\n header: t('cpq.quotes.table.currency', 'Currency'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">{row.original.currencyCode}</span>,\n },\n {\n id: 'nrcTotal',\n header: t('cpq.quotes.table.nrcTotal', 'NRC Total'),\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">\n {fmt(row.original.pricingSummary?.nrcTotal, row.original.currencyCode)}\n </span>\n ),\n },\n {\n id: 'mrcTotal',\n header: t('cpq.quotes.table.mrcTotal', 'MRC Total'),\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">\n {fmt(row.original.pricingSummary?.mrcTotal, row.original.currencyCode)}\n </span>\n ),\n },\n {\n accessorKey: 'createdAt',\n header: t('cpq.quotes.table.createdAt', 'Created'),\n cell: ({ row }) => (\n <span className=\"text-xs text-muted-foreground\">\n {new Date(row.original.createdAt).toLocaleDateString()}\n </span>\n ),\n },\n ],\n [t],\n )\n\n return (\n <CpqListView<QuoteConfig>\n title={t('cpq.quotes.list.title', 'CPQ Quotes')}\n tableId=\"cpq.quotes.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.quotes.search.placeholder', 'Search by quote / customer id...')}\n actions={\n <Button type=\"button\" onClick={() => setShowCustomerPicker(true)} className=\"gap-1.5\">\n <Plus className=\"h-4 w-4\" />\n {t('cpq.quotes.add', 'New Quote')}\n </Button>\n }\n onRowClick={(row) => router.push(`/backend/cpq/quotes/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'open',\n label: t('cpq.quotes.actions.open', 'Open'),\n href: `/backend/cpq/quotes/${row.id}`,\n },\n ]}\n />\n )}\n toolbarContent={\n <CustomerPickerModal\n open={showCustomerPicker}\n onClose={() => setShowCustomerPicker(false)}\n onSelect={selectCustomer}\n creating={creating}\n error={createError}\n />\n }\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t(\n 'cpq.quotes.empty',\n 'No CPQ quotes found. Create one to start configuring offerings for a customer.',\n )}\n </div>\n }\n />\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Plus, Search as SearchIcon } from 'lucide-react'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { CpqListView, useCpqListData } from '../../../components/CpqListView'\nimport {\n formatStatusLabel,\n quoteCpqStatusMap,\n type QuoteCpqStatus,\n} from '../../../components/statusMaps'\n\ntype QuoteConfig = {\n id: string\n quoteId: string\n /** Resolved from SalesQuote.quoteNumber by the list API. */\n quoteNumber: string | null\n customerId: string\n cpqStatus: string\n version: number\n currencyCode: string\n pricingSummary: {\n nrcTotal?: number\n mrcTotal?: number\n } | null\n createdAt: string\n updatedAt: string\n}\n\ntype Customer = {\n id: string\n display_name?: string\n displayName?: string\n name?: string\n companyName?: string\n primary_email?: string\n primaryEmail?: string\n}\n\nconst PAGE_SIZE = 50\n\nfunction fmt(amount: number | undefined, currency: string): string {\n if (amount == null || amount === 0) return '\u2014'\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.cpqStatus === 'string' && values.cpqStatus) {\n params.set('cpqStatus', values.cpqStatus)\n }\n if (typeof values.currencyCode === 'string' && values.currencyCode.trim()) {\n params.set('currencyCode', values.currencyCode.trim().toUpperCase())\n }\n}\n\n// \u2500\u2500\u2500 Customer picker (extracted to keep main page focused) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype CustomerPickerProps = {\n open: boolean\n onClose: () => void\n onSelect: (customer: Customer) => void | Promise<void>\n creating: boolean\n error: string | null\n}\n\nfunction getCustomerLabel(c: Customer, unnamedLabel: string) {\n return c.display_name || c.displayName || c.name || c.companyName || unnamedLabel\n}\n\nfunction getCustomerEmail(c: Customer) {\n return c.primary_email || c.primaryEmail || ''\n}\n\nfunction CustomerPickerModal({ open, onClose, onSelect, creating, error }: CustomerPickerProps) {\n const t = useT()\n const unnamedLabel = t('cpq.quotes.customerPicker.unnamed', 'Unnamed')\n const [customers, setCustomers] = React.useState<Customer[]>([])\n const [customersLoading, setCustomersLoading] = React.useState(false)\n const [search, setSearch] = React.useState('')\n\n React.useEffect(() => {\n if (!open) return\n let cancelled = false\n setSearch('')\n setCustomersLoading(true)\n fetch('/api/customers/companies?pageSize=100')\n .then((r) => (r.ok ? r.json() : { items: [] }))\n .then((data) => {\n if (!cancelled) setCustomers(data.items ?? data ?? [])\n })\n .catch(() => {\n if (!cancelled) setCustomers([])\n })\n .finally(() => {\n if (!cancelled) setCustomersLoading(false)\n })\n return () => {\n cancelled = true\n }\n }, [open])\n\n if (!open) return null\n\n const filtered = customers.filter((c) => {\n if (!search.trim()) return true\n const q = search.toLowerCase()\n return (\n getCustomerLabel(c, unnamedLabel).toLowerCase().includes(q) ||\n getCustomerEmail(c).toLowerCase().includes(q) ||\n c.id.toLowerCase().includes(q)\n )\n })\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\"\n onClick={() => !creating && onClose()}\n >\n <div\n className=\"w-full max-w-lg rounded-lg border bg-card shadow-xl\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"flex items-center justify-between border-b px-4 py-3\">\n <h3 className=\"text-sm font-medium\">\n {t('cpq.quotes.customerPicker.title', 'Select Customer for New Quote')}\n </h3>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={creating}\n className=\"text-muted-foreground hover:text-foreground text-lg font-bold leading-none cursor-pointer disabled:cursor-not-allowed\"\n >\n {'\\u00d7'}\n </button>\n </div>\n <div className=\"p-4 space-y-3\">\n {error && <Alert variant=\"destructive\">{error}</Alert>}\n <div className=\"relative\">\n <SearchIcon className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <Input\n placeholder={t('cpq.quotes.customerPicker.searchPlaceholder', 'Search customers\u2026')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n autoFocus\n className=\"pl-10\"\n />\n </div>\n <div className=\"max-h-72 overflow-y-auto space-y-1\">\n {customersLoading ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">\n {t('cpq.quotes.customerPicker.loading', 'Loading customers\u2026')}\n </div>\n ) : filtered.length === 0 ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">\n {search\n ? t('cpq.quotes.customerPicker.noMatch', 'No customers match your search.')\n : t('cpq.quotes.customerPicker.empty', 'No customers found.')}\n </div>\n ) : (\n filtered.map((c) => (\n <button\n key={c.id}\n type=\"button\"\n onClick={() => onSelect(c)}\n disabled={creating}\n className=\"w-full rounded-md border px-3 py-2.5 text-left hover:border-primary hover:bg-primary/5 transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed\"\n >\n <p className=\"text-sm font-medium\">{getCustomerLabel(c, unnamedLabel)}</p>\n {getCustomerEmail(c) && (\n <p className=\"text-xs text-muted-foreground mt-0.5\">{getCustomerEmail(c)}</p>\n )}\n <p className=\"text-xs text-muted-foreground font-mono mt-0.5\">{c.id}</p>\n </button>\n ))\n )}\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport default function CpqQuotesListPage() {\n const router = useRouter()\n const t = useT()\n\n const data = useCpqListData<QuoteConfig>({\n endpoint: '/api/cpq/quotes',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.quotes.list.error.load', 'Failed to load quotes'),\n })\n\n const [showCustomerPicker, setShowCustomerPicker] = React.useState(false)\n const [creating, setCreating] = React.useState(false)\n const [createError, setCreateError] = React.useState<string | null>(null)\n\n const statusOptions = React.useMemo(\n () => [\n { value: 'new', label: t('cpq.quotes.status.new', 'New') },\n { value: 'incomplete', label: t('cpq.quotes.status.incomplete', 'Incomplete') },\n { value: 'ready', label: t('cpq.quotes.status.ready', 'Ready') },\n { value: 'in_approval', label: t('cpq.quotes.status.in_approval', 'In Approval') },\n { value: 'approved', label: t('cpq.quotes.status.approved', 'Approved') },\n { value: 'with_customer', label: t('cpq.quotes.status.with_customer', 'With Customer') },\n { value: 'accepted', label: t('cpq.quotes.status.accepted', 'Accepted') },\n { value: 'rejected', label: t('cpq.quotes.status.rejected', 'Rejected') },\n { value: 'cancelled', label: t('cpq.quotes.status.cancelled', 'Cancelled') },\n ],\n [t],\n )\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'cpqStatus',\n label: t('cpq.quotes.filters.status', 'Status'),\n type: 'select',\n options: statusOptions,\n },\n {\n id: 'currencyCode',\n label: t('cpq.quotes.filters.currency', 'Currency'),\n type: 'text',\n },\n ],\n [statusOptions, t],\n )\n\n const selectCustomer = React.useCallback(\n async (customer: Customer) => {\n setCreating(true)\n setCreateError(null)\n try {\n const res = await fetch('/api/cpq/quotes', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ customerId: customer.id }),\n })\n const body = await res.json()\n if (res.ok) {\n setShowCustomerPicker(false)\n router.push(`/backend/cpq/quotes/${body.id}`)\n } else {\n setCreateError(\n body.error ?? t('cpq.quotes.create.error', 'Failed to create quote ({status})', { status: res.status }),\n )\n }\n } catch (err) {\n setCreateError((err as Error).message)\n } finally {\n setCreating(false)\n }\n },\n [router, t],\n )\n\n const columns = React.useMemo<ColumnDef<QuoteConfig>[]>(\n () => [\n {\n accessorKey: 'quoteNumber',\n header: t('cpq.quotes.table.quoteNumber', 'Quote Number'),\n cell: ({ row }) => (\n <span className=\"font-medium\">\n {row.original.quoteNumber ?? (\n <span className=\"font-mono text-xs text-muted-foreground\">\n {(row.original.quoteId ?? row.original.id).slice(0, 8)}\u2026\n </span>\n )}\n </span>\n ),\n },\n {\n accessorKey: 'cpqStatus',\n header: t('cpq.quotes.table.status', 'Status'),\n cell: ({ row }) => (\n <Tag variant={quoteCpqStatusMap[row.original.cpqStatus as QuoteCpqStatus] ?? 'neutral'} dot>\n {formatStatusLabel(row.original.cpqStatus)}\n </Tag>\n ),\n },\n {\n accessorKey: 'version',\n header: t('cpq.quotes.table.version', 'Version'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">v{row.original.version}</span>,\n },\n {\n accessorKey: 'currencyCode',\n header: t('cpq.quotes.table.currency', 'Currency'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">{row.original.currencyCode}</span>,\n },\n {\n id: 'nrcTotal',\n header: t('cpq.quotes.table.nrcTotal', 'NRC Total'),\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">\n {fmt(row.original.pricingSummary?.nrcTotal, row.original.currencyCode)}\n </span>\n ),\n },\n {\n id: 'mrcTotal',\n header: t('cpq.quotes.table.mrcTotal', 'MRC Total'),\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">\n {fmt(row.original.pricingSummary?.mrcTotal, row.original.currencyCode)}\n </span>\n ),\n },\n {\n accessorKey: 'createdAt',\n header: t('cpq.quotes.table.createdAt', 'Created'),\n cell: ({ row }) => (\n <span className=\"text-xs text-muted-foreground\">\n {new Date(row.original.createdAt).toLocaleDateString()}\n </span>\n ),\n },\n ],\n [t],\n )\n\n return (\n <CpqListView<QuoteConfig>\n title={t('cpq.quotes.list.title', 'CPQ Quotes')}\n tableId=\"cpq.quotes.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.quotes.search.placeholder', 'Search by quote / customer id...')}\n actions={\n <Button type=\"button\" onClick={() => setShowCustomerPicker(true)} className=\"gap-1.5\">\n <Plus className=\"h-4 w-4\" />\n {t('cpq.quotes.add', 'New Quote')}\n </Button>\n }\n onRowClick={(row) => router.push(`/backend/cpq/quotes/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'open',\n label: t('cpq.quotes.actions.open', 'Open'),\n href: `/backend/cpq/quotes/${row.id}`,\n },\n ]}\n />\n )}\n toolbarContent={\n <CustomerPickerModal\n open={showCustomerPicker}\n onClose={() => setShowCustomerPicker(false)}\n onSelect={selectCustomer}\n creating={creating}\n error={createError}\n />\n }\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t(\n 'cpq.quotes.empty',\n 'No CPQ quotes found. Create one to start configuring offerings for a customer.',\n )}\n </div>\n }\n />\n )\n}\n"],
5
+ "mappings": ";AAiIQ,SACE,KADF;AAhIR,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,YAAY;AAErB,SAAS,cAAc;AACvB,SAAS,aAAa;AAEtB,SAAS,MAAM,UAAU,kBAAkB;AAC3C,SAAS,aAAa;AACtB,SAAS,kBAAkB;AAC3B,SAAS,WAAW;AACpB,SAAS,aAAa,sBAAsB;AAC5C;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AA6BP,MAAM,YAAY;AAElB,SAAS,IAAI,QAA4B,UAA0B;AACjE,MAAI,UAAU,QAAQ,WAAW,EAAG,QAAO;AAC3C,SAAO,IAAI,KAAK,aAAa,SAAS,EAAE,OAAO,YAAY,SAAS,CAAC,EAAE,OAAO,MAAM;AACtF;AAEA,SAAS,kBAAkB,QAAsB,QAAyB;AACxE,MAAI,OAAO,OAAO,cAAc,YAAY,OAAO,WAAW;AAC5D,WAAO,IAAI,aAAa,OAAO,SAAS;AAAA,EAC1C;AACA,MAAI,OAAO,OAAO,iBAAiB,YAAY,OAAO,aAAa,KAAK,GAAG;AACzE,WAAO,IAAI,gBAAgB,OAAO,aAAa,KAAK,EAAE,YAAY,CAAC;AAAA,EACrE;AACF;AAYA,SAAS,iBAAiB,GAAa,cAAsB;AAC3D,SAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,QAAQ,EAAE,eAAe;AACvE;AAEA,SAAS,iBAAiB,GAAa;AACrC,SAAO,EAAE,iBAAiB,EAAE,gBAAgB;AAC9C;AAEA,SAAS,oBAAoB,EAAE,MAAM,SAAS,UAAU,UAAU,MAAM,GAAwB;AAC9F,QAAM,IAAI,KAAK;AACf,QAAM,eAAe,EAAE,qCAAqC,SAAS;AACrE,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAqB,CAAC,CAAC;AAC/D,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAS,KAAK;AACpE,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,EAAE;AAE7C,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,KAAM;AACX,QAAI,YAAY;AAChB,cAAU,EAAE;AACZ,wBAAoB,IAAI;AACxB,UAAM,uCAAuC,EAC1C,KAAK,CAAC,MAAO,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,CAAE,EAC7C,KAAK,CAAC,SAAS;AACd,UAAI,CAAC,UAAW,cAAa,KAAK,SAAS,QAAQ,CAAC,CAAC;AAAA,IACvD,CAAC,EACA,MAAM,MAAM;AACX,UAAI,CAAC,UAAW,cAAa,CAAC,CAAC;AAAA,IACjC,CAAC,EACA,QAAQ,MAAM;AACb,UAAI,CAAC,UAAW,qBAAoB,KAAK;AAAA,IAC3C,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAET,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,WAAW,UAAU,OAAO,CAAC,MAAM;AACvC,QAAI,CAAC,OAAO,KAAK,EAAG,QAAO;AAC3B,UAAM,IAAI,OAAO,YAAY;AAC7B,WACE,iBAAiB,GAAG,YAAY,EAAE,YAAY,EAAE,SAAS,CAAC,KAC1D,iBAAiB,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,KAC5C,EAAE,GAAG,YAAY,EAAE,SAAS,CAAC;AAAA,EAEjC,CAAC;AAED,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,SAAS,MAAM,CAAC,YAAY,QAAQ;AAAA,MAEpC;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,UAElC;AAAA,iCAAC,SAAI,WAAU,wDACb;AAAA,kCAAC,QAAG,WAAU,uBACX,YAAE,mCAAmC,+BAA+B,GACvE;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,UAAU;AAAA,kBACV,WAAU;AAAA,kBAET;AAAA;AAAA,cACH;AAAA,eACF;AAAA,YACA,qBAAC,SAAI,WAAU,iBACZ;AAAA,uBAAS,oBAAC,SAAM,SAAQ,eAAe,iBAAM;AAAA,cAC9C,qBAAC,SAAI,WAAU,YACb;AAAA,oCAAC,cAAW,WAAU,0EAAyE;AAAA,gBAC/F;AAAA,kBAAC;AAAA;AAAA,oBACC,aAAa,EAAE,+CAA+C,wBAAmB;AAAA,oBACjF,OAAO;AAAA,oBACP,UAAU,CAAC,MAAM,UAAU,EAAE,OAAO,KAAK;AAAA,oBACzC,WAAS;AAAA,oBACT,WAAU;AAAA;AAAA,gBACZ;AAAA,iBACF;AAAA,cACA,oBAAC,SAAI,WAAU,sCACZ,6BACC,oBAAC,SAAI,WAAU,kDACZ,YAAE,qCAAqC,yBAAoB,GAC9D,IACE,SAAS,WAAW,IACtB,oBAAC,SAAI,WAAU,kDACZ,mBACG,EAAE,qCAAqC,iCAAiC,IACxE,EAAE,mCAAmC,qBAAqB,GAChE,IAEA,SAAS,IAAI,CAAC,MACZ;AAAA,gBAAC;AAAA;AAAA,kBAEC,MAAK;AAAA,kBACL,SAAS,MAAM,SAAS,CAAC;AAAA,kBACzB,UAAU;AAAA,kBACV,WAAU;AAAA,kBAEV;AAAA,wCAAC,OAAE,WAAU,uBAAuB,2BAAiB,GAAG,YAAY,GAAE;AAAA,oBACrE,iBAAiB,CAAC,KACjB,oBAAC,OAAE,WAAU,wCAAwC,2BAAiB,CAAC,GAAE;AAAA,oBAE3E,oBAAC,OAAE,WAAU,kDAAkD,YAAE,IAAG;AAAA;AAAA;AAAA,gBAV/D,EAAE;AAAA,cAWT,CACD,GAEL;AAAA,eACF;AAAA;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;AAIe,SAAR,oBAAqC;AAC1C,QAAM,SAAS,UAAU;AACzB,QAAM,IAAI,KAAK;AAEf,QAAM,OAAO,eAA4B;AAAA,IACvC,UAAU;AAAA,IACV,UAAU;AAAA,IACV;AAAA,IACA,kBAAkB,EAAE,8BAA8B,uBAAuB;AAAA,EAC3E,CAAC;AAED,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,MAAM,SAAS,KAAK;AACxE,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,KAAK;AACpD,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAwB,IAAI;AAExE,QAAM,gBAAgB,MAAM;AAAA,IAC1B,MAAM;AAAA,MACJ,EAAE,OAAO,OAAO,OAAO,EAAE,yBAAyB,KAAK,EAAE;AAAA,MACzD,EAAE,OAAO,cAAc,OAAO,EAAE,gCAAgC,YAAY,EAAE;AAAA,MAC9E,EAAE,OAAO,SAAS,OAAO,EAAE,2BAA2B,OAAO,EAAE;AAAA,MAC/D,EAAE,OAAO,eAAe,OAAO,EAAE,iCAAiC,aAAa,EAAE;AAAA,MACjF,EAAE,OAAO,YAAY,OAAO,EAAE,8BAA8B,UAAU,EAAE;AAAA,MACxE,EAAE,OAAO,iBAAiB,OAAO,EAAE,mCAAmC,eAAe,EAAE;AAAA,MACvF,EAAE,OAAO,YAAY,OAAO,EAAE,8BAA8B,UAAU,EAAE;AAAA,MACxE,EAAE,OAAO,YAAY,OAAO,EAAE,8BAA8B,UAAU,EAAE;AAAA,MACxE,EAAE,OAAO,aAAa,OAAO,EAAE,+BAA+B,WAAW,EAAE;AAAA,IAC7E;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB,MAAM;AAAA,MACJ;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,6BAA6B,QAAQ;AAAA,QAC9C,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,+BAA+B,UAAU;AAAA,QAClD,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,eAAe,CAAC;AAAA,EACnB;AAEA,QAAM,iBAAiB,MAAM;AAAA,IAC3B,OAAO,aAAuB;AAC5B,kBAAY,IAAI;AAChB,qBAAe,IAAI;AACnB,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,mBAAmB;AAAA,UACzC,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,EAAE,YAAY,SAAS,GAAG,CAAC;AAAA,QAClD,CAAC;AACD,cAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,YAAI,IAAI,IAAI;AACV,gCAAsB,KAAK;AAC3B,iBAAO,KAAK,uBAAuB,KAAK,EAAE,EAAE;AAAA,QAC9C,OAAO;AACL;AAAA,YACE,KAAK,SAAS,EAAE,2BAA2B,qCAAqC,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,UACxG;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AACZ,uBAAgB,IAAc,OAAO;AAAA,MACvC,UAAE;AACA,oBAAY,KAAK;AAAA,MACnB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,CAAC;AAAA,EACZ;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB,MAAM;AAAA,MACJ;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,gCAAgC,cAAc;AAAA,QACxD,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,UAAK,WAAU,eACb,cAAI,SAAS,eACZ,qBAAC,UAAK,WAAU,2CACZ;AAAA,eAAI,SAAS,WAAW,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC;AAAA,UAAE;AAAA,WACzD,GAEJ;AAAA,MAEJ;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,2BAA2B,QAAQ;AAAA,QAC7C,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,OAAI,SAAS,kBAAkB,IAAI,SAAS,SAA2B,KAAK,WAAW,KAAG,MACxF,4BAAkB,IAAI,SAAS,SAAS,GAC3C;AAAA,MAEJ;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,4BAA4B,SAAS;AAAA,QAC/C,MAAM,CAAC,EAAE,IAAI,MAAM,qBAAC,UAAK,WAAU,yBAAwB;AAAA;AAAA,UAAE,IAAI,SAAS;AAAA,WAAQ;AAAA,MACpF;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,6BAA6B,UAAU;AAAA,QACjD,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,UAAK,WAAU,yBAAyB,cAAI,SAAS,cAAa;AAAA,MACxF;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,6BAA6B,WAAW;AAAA,QAClD,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,UAAK,WAAU,qBACb,cAAI,IAAI,SAAS,gBAAgB,UAAU,IAAI,SAAS,YAAY,GACvE;AAAA,MAEJ;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,6BAA6B,WAAW;AAAA,QAClD,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,UAAK,WAAU,qBACb,cAAI,IAAI,SAAS,gBAAgB,UAAU,IAAI,SAAS,YAAY,GACvE;AAAA,MAEJ;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,8BAA8B,SAAS;AAAA,QACjD,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,UAAK,WAAU,iCACb,cAAI,KAAK,IAAI,SAAS,SAAS,EAAE,mBAAmB,GACvD;AAAA,MAEJ;AAAA,IACF;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,yBAAyB,YAAY;AAAA,MAC9C,SAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV,mBAAmB,EAAE,iCAAiC,kCAAkC;AAAA,MACxF,SACE,qBAAC,UAAO,MAAK,UAAS,SAAS,MAAM,sBAAsB,IAAI,GAAG,WAAU,WAC1E;AAAA,4BAAC,QAAK,WAAU,WAAU;AAAA,QACzB,EAAE,kBAAkB,WAAW;AAAA,SAClC;AAAA,MAEF,YAAY,CAAC,QAAQ,OAAO,KAAK,uBAAuB,IAAI,EAAE,EAAE;AAAA,MAChE,YAAY,CAAC,QACX;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,YACL;AAAA,cACE,IAAI;AAAA,cACJ,OAAO,EAAE,2BAA2B,MAAM;AAAA,cAC1C,MAAM,uBAAuB,IAAI,EAAE;AAAA,YACrC;AAAA,UACF;AAAA;AAAA,MACF;AAAA,MAEF,gBACE;AAAA,QAAC;AAAA;AAAA,UACC,MAAM;AAAA,UACN,SAAS,MAAM,sBAAsB,KAAK;AAAA,UAC1C,UAAU;AAAA,UACV;AAAA,UACA,OAAO;AAAA;AAAA,MACT;AAAA,MAEF,YACE,oBAAC,SAAI,WAAU,2EACZ;AAAA,QACC;AAAA,QACA;AAAA,MACF,GACF;AAAA;AAAA,EAEJ;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -5,6 +5,7 @@ import { Button } from "@open-mercato/ui/primitives/button";
5
5
  import { Input } from "@open-mercato/ui/primitives/input";
6
6
  import { Checkbox } from "@open-mercato/ui/primitives/checkbox";
7
7
  import { Tag } from "@open-mercato/ui/primitives/tag";
8
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
8
9
  function thenInitialFor(effect) {
9
10
  switch (effect) {
10
11
  case "filter_options":
@@ -30,6 +31,7 @@ function DependsOnRulesEditor({
30
31
  parentOptions,
31
32
  childOptions
32
33
  }) {
34
+ const t = useT();
33
35
  const updateRule = (index, patch) => {
34
36
  const next = rules.map((r, i) => i === index ? { ...r, ...patch } : r);
35
37
  onChange(next);
@@ -42,10 +44,10 @@ function DependsOnRulesEditor({
42
44
  };
43
45
  if (rules.length === 0) {
44
46
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
45
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No rules yet. Add at least one to make this dependency effective." }),
47
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("cpq.specifications.dependsOn.empty", "No rules yet. Add at least one to make this dependency effective.") }),
46
48
  /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addRule, children: [
47
49
  /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
48
- "Add rule"
50
+ t("cpq.specifications.dependsOn.addRule", "Add rule")
49
51
  ] })
50
52
  ] });
51
53
  }
@@ -65,7 +67,7 @@ function DependsOnRulesEditor({
65
67
  )) }),
66
68
  /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addRule, children: [
67
69
  /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
68
- "Add rule"
70
+ t("cpq.specifications.dependsOn.addRule", "Add rule")
69
71
  ] })
70
72
  ] });
71
73
  }
@@ -78,9 +80,10 @@ function RuleRow({
78
80
  onThenChange,
79
81
  onRemove
80
82
  }) {
83
+ const t = useT();
81
84
  return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_auto_1fr_auto] items-start gap-2 rounded-md border bg-muted/20 p-2", children: [
82
85
  /* @__PURE__ */ jsxs("div", { children: [
83
- /* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: "When parent =" }),
86
+ /* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: t("cpq.specifications.dependsOn.whenParent", "When parent =") }),
84
87
  parentOptions && parentOptions.length > 0 ? /* @__PURE__ */ jsxs(
85
88
  "select",
86
89
  {
@@ -88,7 +91,7 @@ function RuleRow({
88
91
  onChange: (e) => onWhenChange(e.target.value),
89
92
  className: "w-full rounded-md border bg-background px-2 py-1.5 text-sm",
90
93
  children: [
91
- /* @__PURE__ */ jsx("option", { value: "", children: "\u2014 Select value \u2014" }),
94
+ /* @__PURE__ */ jsx("option", { value: "", children: t("cpq.specifications.dependsOn.selectValue", "\u2014 Select value \u2014") }),
92
95
  parentOptions.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
93
96
  ]
94
97
  }
@@ -97,7 +100,7 @@ function RuleRow({
97
100
  {
98
101
  value: rule.when,
99
102
  onChange: (e) => onWhenChange(e.target.value),
100
- placeholder: "Parent value"
103
+ placeholder: t("cpq.specifications.dependsOn.parentValuePlaceholder", "Parent value")
101
104
  }
102
105
  )
103
106
  ] }),
@@ -117,17 +120,18 @@ function RuleRow({
117
120
  type: "button",
118
121
  onClick: onRemove,
119
122
  className: "mt-5 self-start rounded p-1.5 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors",
120
- title: "Remove rule",
123
+ title: t("cpq.specifications.dependsOn.removeRule", "Remove rule"),
121
124
  children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
122
125
  }
123
126
  )
124
127
  ] });
125
128
  }
126
129
  function ThenInput({ effect, value, childOptions, onChange }) {
130
+ const t = useT();
127
131
  if (effect === "toggle_required") {
128
132
  const isRequired = value === true;
129
133
  return /* @__PURE__ */ jsxs("div", { children: [
130
- /* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: "Then required is" }),
134
+ /* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: t("cpq.specifications.dependsOn.thenRequiredIs", "Then required is") }),
131
135
  /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 rounded-md border bg-background px-2 py-1.5 text-sm cursor-pointer", children: [
132
136
  /* @__PURE__ */ jsx(
133
137
  Checkbox,
@@ -136,13 +140,13 @@ function ThenInput({ effect, value, childOptions, onChange }) {
136
140
  onCheckedChange: (checked) => onChange(checked === true)
137
141
  }
138
142
  ),
139
- /* @__PURE__ */ jsx("span", { children: isRequired ? "Required" : "Optional" })
143
+ /* @__PURE__ */ jsx("span", { children: isRequired ? t("cpq.specifications.dependsOn.required", "Required") : t("cpq.specifications.dependsOn.optional", "Optional") })
140
144
  ] })
141
145
  ] });
142
146
  }
143
147
  if (effect === "set_value") {
144
148
  return /* @__PURE__ */ jsxs("div", { children: [
145
- /* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: "Then set value to" }),
149
+ /* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: t("cpq.specifications.dependsOn.thenSetValueTo", "Then set value to") }),
146
150
  childOptions && childOptions.length > 0 ? /* @__PURE__ */ jsxs(
147
151
  "select",
148
152
  {
@@ -150,7 +154,7 @@ function ThenInput({ effect, value, childOptions, onChange }) {
150
154
  onChange: (e) => onChange(e.target.value),
151
155
  className: "w-full rounded-md border bg-background px-2 py-1.5 text-sm",
152
156
  children: [
153
- /* @__PURE__ */ jsx("option", { value: "", children: "\u2014 Select value \u2014" }),
157
+ /* @__PURE__ */ jsx("option", { value: "", children: t("cpq.specifications.dependsOn.selectValue", "\u2014 Select value \u2014") }),
154
158
  childOptions.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
155
159
  ]
156
160
  }
@@ -159,7 +163,7 @@ function ThenInput({ effect, value, childOptions, onChange }) {
159
163
  {
160
164
  value: typeof value === "string" ? value : "",
161
165
  onChange: (e) => onChange(e.target.value),
162
- placeholder: "Value"
166
+ placeholder: t("cpq.specifications.dependsOn.valuePlaceholder", "Value")
163
167
  }
164
168
  )
165
169
  ] });
@@ -172,7 +176,7 @@ function ThenInput({ effect, value, childOptions, onChange }) {
172
176
  );
173
177
  };
174
178
  return /* @__PURE__ */ jsxs("div", { children: [
175
- /* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: "Then allow only" }),
179
+ /* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: t("cpq.specifications.dependsOn.thenAllowOnly", "Then allow only") }),
176
180
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 rounded-md border bg-background px-2 py-1.5 text-sm min-h-[34px]", children: childOptions.map((opt) => {
177
181
  const isOn = selected.includes(opt.value);
178
182
  return /* @__PURE__ */ jsx(
@@ -197,13 +201,13 @@ function ThenInput({ effect, value, childOptions, onChange }) {
197
201
  ] });
198
202
  }
199
203
  return /* @__PURE__ */ jsxs("div", { children: [
200
- /* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: "Then allow only (comma-separated)" }),
204
+ /* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: t("cpq.specifications.dependsOn.thenAllowOnlyCsv", "Then allow only (comma-separated)") }),
201
205
  /* @__PURE__ */ jsx(
202
206
  Input,
203
207
  {
204
208
  value: thenAsCsv(value),
205
209
  onChange: (e) => onChange(csvAsArray(e.target.value)),
206
- placeholder: "value1, value2, value3"
210
+ placeholder: t("cpq.specifications.dependsOn.csvPlaceholder", "value1, value2, value3")
207
211
  }
208
212
  )
209
213
  ] });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/cpq/backend/cpq/specifications/%5Bid%5D/DependsOnRulesEditor.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Plus, X } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport type { DependsOnEffect, DependsOnRule } from './page'\n\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype AttributeOption = { value: string; label: string }\n\nfunction thenInitialFor(effect: DependsOnEffect): unknown {\n switch (effect) {\n case 'filter_options':\n return [] as string[]\n case 'set_value':\n return ''\n case 'toggle_required':\n return false\n }\n}\n\nfunction thenAsCsv(value: unknown): string {\n if (Array.isArray(value)) return value.join(', ')\n if (typeof value === 'string') return value\n return ''\n}\n\nfunction csvAsArray(input: string): string[] {\n return input\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part.length > 0)\n}\n\n// \u2500\u2500\u2500 Component \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type DependsOnRulesEditorProps = {\n effect: DependsOnEffect\n rules: DependsOnRule[]\n onChange: (rules: DependsOnRule[]) => void\n /** Options of the parent attribute (used as \"When\" dropdown if available). */\n parentOptions?: AttributeOption[] | null\n /**\n * Options of the **child** (= this attribute being edited). Only used by\n * `filter_options` so the \"then\" picker offers checkboxes instead of CSV.\n */\n childOptions?: AttributeOption[] | null\n}\n\n/**\n * Renders the rule rows for an attribute dependency. The shape of each\n * rule's `then` field depends on the effect type \u2014 see `page.tsx`\n * `DependsOnRule` doc for the full contract.\n *\n * Each effect type has tailored input affordances:\n * - `filter_options`: child options shown as checkboxes (or CSV input if\n * the child has no enumerated options).\n * - `set_value`: free-text value input.\n * - `toggle_required`: required on/off checkbox.\n */\nexport function DependsOnRulesEditor({\n effect,\n rules,\n onChange,\n parentOptions,\n childOptions,\n}: DependsOnRulesEditorProps) {\n const updateRule = (index: number, patch: Partial<DependsOnRule>) => {\n const next = rules.map((r, i) => (i === index ? { ...r, ...patch } : r))\n onChange(next)\n }\n\n const removeRule = (index: number) => {\n onChange(rules.filter((_, i) => i !== index))\n }\n\n const addRule = () => {\n onChange([...rules, { when: '', then: thenInitialFor(effect) }])\n }\n\n if (rules.length === 0) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-xs text-muted-foreground\">\n No rules yet. Add at least one to make this dependency effective.\n </p>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addRule}>\n <Plus className=\"h-3.5 w-3.5\" />\n Add rule\n </Button>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-2\">\n <div className=\"space-y-2\">\n {rules.map((rule, idx) => (\n <RuleRow\n key={idx}\n effect={effect}\n rule={rule}\n parentOptions={parentOptions}\n childOptions={childOptions}\n onWhenChange={(when) => updateRule(idx, { when })}\n onThenChange={(then) => updateRule(idx, { then })}\n onRemove={() => removeRule(idx)}\n />\n ))}\n </div>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addRule}>\n <Plus className=\"h-3.5 w-3.5\" />\n Add rule\n </Button>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Single rule row (decomposed for readability) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype RuleRowProps = {\n effect: DependsOnEffect\n rule: DependsOnRule\n parentOptions?: AttributeOption[] | null\n childOptions?: AttributeOption[] | null\n onWhenChange: (when: string) => void\n onThenChange: (then: unknown) => void\n onRemove: () => void\n}\n\nfunction RuleRow({\n effect,\n rule,\n parentOptions,\n childOptions,\n onWhenChange,\n onThenChange,\n onRemove,\n}: RuleRowProps) {\n return (\n <div className=\"grid grid-cols-[1fr_auto_1fr_auto] items-start gap-2 rounded-md border bg-muted/20 p-2\">\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n When parent =\n </label>\n {parentOptions && parentOptions.length > 0 ? (\n <select\n value={rule.when}\n onChange={(e) => onWhenChange(e.target.value)}\n className=\"w-full rounded-md border bg-background px-2 py-1.5 text-sm\"\n >\n <option value=\"\">\u2014 Select value \u2014</option>\n {parentOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n ) : (\n <Input\n value={rule.when}\n onChange={(e) => onWhenChange(e.target.value)}\n placeholder=\"Parent value\"\n />\n )}\n </div>\n <div className=\"mt-5 self-center text-muted-foreground\">\u2192</div>\n <ThenInput\n effect={effect}\n value={rule.then}\n childOptions={childOptions}\n onChange={onThenChange}\n />\n <button\n type=\"button\"\n onClick={onRemove}\n className=\"mt-5 self-start rounded p-1.5 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors\"\n title=\"Remove rule\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Effect-specific \"then\" inputs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype ThenInputProps = {\n effect: DependsOnEffect\n value: unknown\n childOptions?: AttributeOption[] | null\n onChange: (next: unknown) => void\n}\n\nfunction ThenInput({ effect, value, childOptions, onChange }: ThenInputProps) {\n if (effect === 'toggle_required') {\n const isRequired = value === true\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n Then required is\n </label>\n <label className=\"flex items-center gap-2 rounded-md border bg-background px-2 py-1.5 text-sm cursor-pointer\">\n <Checkbox\n checked={isRequired}\n onCheckedChange={(checked) => onChange(checked === true)}\n />\n <span>{isRequired ? 'Required' : 'Optional'}</span>\n </label>\n </div>\n )\n }\n\n if (effect === 'set_value') {\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n Then set value to\n </label>\n {childOptions && childOptions.length > 0 ? (\n <select\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value)}\n className=\"w-full rounded-md border bg-background px-2 py-1.5 text-sm\"\n >\n <option value=\"\">\u2014 Select value \u2014</option>\n {childOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n ) : (\n <Input\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder=\"Value\"\n />\n )}\n </div>\n )\n }\n\n // effect === 'filter_options'\n const selected = Array.isArray(value) ? (value as string[]) : []\n if (childOptions && childOptions.length > 0) {\n const toggle = (optValue: string) => {\n onChange(\n selected.includes(optValue)\n ? selected.filter((v) => v !== optValue)\n : [...selected, optValue],\n )\n }\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n Then allow only\n </label>\n <div className=\"flex flex-wrap gap-1.5 rounded-md border bg-background px-2 py-1.5 text-sm min-h-[34px]\">\n {childOptions.map((opt) => {\n const isOn = selected.includes(opt.value)\n return (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => toggle(opt.value)}\n className=\"cursor-pointer\"\n >\n <Tag\n variant={isOn ? 'success' : 'neutral'}\n dot={isOn}\n className=\"px-2 text-xs\"\n >\n {opt.label}\n </Tag>\n </button>\n )\n })}\n </div>\n </div>\n )\n }\n\n // No enumerated child options \u2014 fall back to CSV.\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n Then allow only (comma-separated)\n </label>\n <Input\n value={thenAsCsv(value)}\n onChange={(e) => onChange(csvAsArray(e.target.value))}\n placeholder=\"value1, value2, value3\"\n />\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Plus, X } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { DependsOnEffect, DependsOnRule } from './page'\n\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype AttributeOption = { value: string; label: string }\n\nfunction thenInitialFor(effect: DependsOnEffect): unknown {\n switch (effect) {\n case 'filter_options':\n return [] as string[]\n case 'set_value':\n return ''\n case 'toggle_required':\n return false\n }\n}\n\nfunction thenAsCsv(value: unknown): string {\n if (Array.isArray(value)) return value.join(', ')\n if (typeof value === 'string') return value\n return ''\n}\n\nfunction csvAsArray(input: string): string[] {\n return input\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part.length > 0)\n}\n\n// \u2500\u2500\u2500 Component \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type DependsOnRulesEditorProps = {\n effect: DependsOnEffect\n rules: DependsOnRule[]\n onChange: (rules: DependsOnRule[]) => void\n /** Options of the parent attribute (used as \"When\" dropdown if available). */\n parentOptions?: AttributeOption[] | null\n /**\n * Options of the **child** (= this attribute being edited). Only used by\n * `filter_options` so the \"then\" picker offers checkboxes instead of CSV.\n */\n childOptions?: AttributeOption[] | null\n}\n\n/**\n * Renders the rule rows for an attribute dependency. The shape of each\n * rule's `then` field depends on the effect type \u2014 see `page.tsx`\n * `DependsOnRule` doc for the full contract.\n *\n * Each effect type has tailored input affordances:\n * - `filter_options`: child options shown as checkboxes (or CSV input if\n * the child has no enumerated options).\n * - `set_value`: free-text value input.\n * - `toggle_required`: required on/off checkbox.\n */\nexport function DependsOnRulesEditor({\n effect,\n rules,\n onChange,\n parentOptions,\n childOptions,\n}: DependsOnRulesEditorProps) {\n const t = useT()\n const updateRule = (index: number, patch: Partial<DependsOnRule>) => {\n const next = rules.map((r, i) => (i === index ? { ...r, ...patch } : r))\n onChange(next)\n }\n\n const removeRule = (index: number) => {\n onChange(rules.filter((_, i) => i !== index))\n }\n\n const addRule = () => {\n onChange([...rules, { when: '', then: thenInitialFor(effect) }])\n }\n\n if (rules.length === 0) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-xs text-muted-foreground\">\n {t('cpq.specifications.dependsOn.empty', 'No rules yet. Add at least one to make this dependency effective.')}\n </p>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addRule}>\n <Plus className=\"h-3.5 w-3.5\" />\n {t('cpq.specifications.dependsOn.addRule', 'Add rule')}\n </Button>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-2\">\n <div className=\"space-y-2\">\n {rules.map((rule, idx) => (\n <RuleRow\n key={idx}\n effect={effect}\n rule={rule}\n parentOptions={parentOptions}\n childOptions={childOptions}\n onWhenChange={(when) => updateRule(idx, { when })}\n onThenChange={(then) => updateRule(idx, { then })}\n onRemove={() => removeRule(idx)}\n />\n ))}\n </div>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addRule}>\n <Plus className=\"h-3.5 w-3.5\" />\n {t('cpq.specifications.dependsOn.addRule', 'Add rule')}\n </Button>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Single rule row (decomposed for readability) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype RuleRowProps = {\n effect: DependsOnEffect\n rule: DependsOnRule\n parentOptions?: AttributeOption[] | null\n childOptions?: AttributeOption[] | null\n onWhenChange: (when: string) => void\n onThenChange: (then: unknown) => void\n onRemove: () => void\n}\n\nfunction RuleRow({\n effect,\n rule,\n parentOptions,\n childOptions,\n onWhenChange,\n onThenChange,\n onRemove,\n}: RuleRowProps) {\n const t = useT()\n return (\n <div className=\"grid grid-cols-[1fr_auto_1fr_auto] items-start gap-2 rounded-md border bg-muted/20 p-2\">\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n {t('cpq.specifications.dependsOn.whenParent', 'When parent =')}\n </label>\n {parentOptions && parentOptions.length > 0 ? (\n <select\n value={rule.when}\n onChange={(e) => onWhenChange(e.target.value)}\n className=\"w-full rounded-md border bg-background px-2 py-1.5 text-sm\"\n >\n <option value=\"\">{t('cpq.specifications.dependsOn.selectValue', '\u2014 Select value \u2014')}</option>\n {parentOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n ) : (\n <Input\n value={rule.when}\n onChange={(e) => onWhenChange(e.target.value)}\n placeholder={t('cpq.specifications.dependsOn.parentValuePlaceholder', 'Parent value')}\n />\n )}\n </div>\n <div className=\"mt-5 self-center text-muted-foreground\">\u2192</div>\n <ThenInput\n effect={effect}\n value={rule.then}\n childOptions={childOptions}\n onChange={onThenChange}\n />\n <button\n type=\"button\"\n onClick={onRemove}\n className=\"mt-5 self-start rounded p-1.5 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors\"\n title={t('cpq.specifications.dependsOn.removeRule', 'Remove rule')}\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Effect-specific \"then\" inputs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype ThenInputProps = {\n effect: DependsOnEffect\n value: unknown\n childOptions?: AttributeOption[] | null\n onChange: (next: unknown) => void\n}\n\nfunction ThenInput({ effect, value, childOptions, onChange }: ThenInputProps) {\n const t = useT()\n if (effect === 'toggle_required') {\n const isRequired = value === true\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n {t('cpq.specifications.dependsOn.thenRequiredIs', 'Then required is')}\n </label>\n <label className=\"flex items-center gap-2 rounded-md border bg-background px-2 py-1.5 text-sm cursor-pointer\">\n <Checkbox\n checked={isRequired}\n onCheckedChange={(checked) => onChange(checked === true)}\n />\n <span>{isRequired ? t('cpq.specifications.dependsOn.required', 'Required') : t('cpq.specifications.dependsOn.optional', 'Optional')}</span>\n </label>\n </div>\n )\n }\n\n if (effect === 'set_value') {\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n {t('cpq.specifications.dependsOn.thenSetValueTo', 'Then set value to')}\n </label>\n {childOptions && childOptions.length > 0 ? (\n <select\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value)}\n className=\"w-full rounded-md border bg-background px-2 py-1.5 text-sm\"\n >\n <option value=\"\">{t('cpq.specifications.dependsOn.selectValue', '\u2014 Select value \u2014')}</option>\n {childOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n ) : (\n <Input\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder={t('cpq.specifications.dependsOn.valuePlaceholder', 'Value')}\n />\n )}\n </div>\n )\n }\n\n // effect === 'filter_options'\n const selected = Array.isArray(value) ? (value as string[]) : []\n if (childOptions && childOptions.length > 0) {\n const toggle = (optValue: string) => {\n onChange(\n selected.includes(optValue)\n ? selected.filter((v) => v !== optValue)\n : [...selected, optValue],\n )\n }\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n {t('cpq.specifications.dependsOn.thenAllowOnly', 'Then allow only')}\n </label>\n <div className=\"flex flex-wrap gap-1.5 rounded-md border bg-background px-2 py-1.5 text-sm min-h-[34px]\">\n {childOptions.map((opt) => {\n const isOn = selected.includes(opt.value)\n return (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => toggle(opt.value)}\n className=\"cursor-pointer\"\n >\n <Tag\n variant={isOn ? 'success' : 'neutral'}\n dot={isOn}\n className=\"px-2 text-xs\"\n >\n {opt.label}\n </Tag>\n </button>\n )\n })}\n </div>\n </div>\n )\n }\n\n // No enumerated child options \u2014 fall back to CSV.\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n {t('cpq.specifications.dependsOn.thenAllowOnlyCsv', 'Then allow only (comma-separated)')}\n </label>\n <Input\n value={thenAsCsv(value)}\n onChange={(e) => onChange(csvAsArray(e.target.value))}\n placeholder={t('cpq.specifications.dependsOn.csvPlaceholder', 'value1, value2, value3')}\n />\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }