@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.
- package/CHANGELOG.md +16 -0
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js +3 -9
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js +21 -25
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js +48 -47
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/shared.js +37 -2
- package/dist/modules/cpq/backend/cpq/inventory/shared.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +154 -178
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +168 -143
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/page.js +36 -34
- package/dist/modules/cpq/backend/cpq/offerings/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +69 -66
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/page.js +6 -6
- package/dist/modules/cpq/backend/cpq/orders/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js +41 -36
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js +76 -72
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/page.js +18 -15
- package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +23 -21
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js +22 -50
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js +74 -52
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js +20 -17
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/page.js +1 -1
- package/dist/modules/cpq/backend/cpq/pricing/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js +115 -123
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js +6 -3
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +129 -94
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/page.js +23 -19
- package/dist/modules/cpq/backend/cpq/quotes/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js +19 -15
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +171 -178
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/page.js +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js +60 -50
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js +22 -18
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/page.js +1 -1
- package/dist/modules/cpq/backend/cpq/wizards/page.js.map +2 -2
- package/dist/modules/cpq/components/ArcLineDiff.js +2 -2
- package/dist/modules/cpq/components/ArcLineDiff.js.map +2 -2
- package/dist/modules/cpq/components/ChargeBreakdownTable.js +4 -10
- package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +2 -2
- package/dist/modules/cpq/components/PriceWaterfall.js +3 -1
- package/dist/modules/cpq/components/PriceWaterfall.js.map +2 -2
- package/dist/modules/cpq/components/PricingSummaryCard.js +1 -6
- package/dist/modules/cpq/components/PricingSummaryCard.js.map +2 -2
- package/dist/modules/cpq/components/StatusPath.js +5 -3
- package/dist/modules/cpq/components/StatusPath.js.map +2 -2
- package/dist/modules/cpq/components/useCpqRowActions.js +10 -22
- package/dist/modules/cpq/components/useCpqRowActions.js.map +2 -2
- package/dist/modules/cpq/i18n/de.json +1220 -0
- package/dist/modules/cpq/i18n/en.json +1220 -0
- package/dist/modules/cpq/i18n/es.json +1220 -0
- package/dist/modules/cpq/i18n/pl.json +1220 -0
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js +39 -31
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js +13 -5
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js +41 -21
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection-table.js +2 -2
- package/dist/modules/cpq/widgets/injection-table.js.map +2 -2
- package/dist/modules/cpq/workflows/WizardRunner.js +10 -8
- package/dist/modules/cpq/workflows/WizardRunner.js.map +2 -2
- package/dist/modules/cpq/workflows/WizardTrigger.js +15 -9
- package/dist/modules/cpq/workflows/WizardTrigger.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js +9 -7
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js +9 -4
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ItemListStep.js +16 -19
- package/dist/modules/cpq/workflows/steps/ItemListStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js +19 -32
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js +8 -6
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ReviewStep.js +32 -30
- package/dist/modules/cpq/workflows/steps/ReviewStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/index.js +12 -0
- package/dist/modules/cpq/workflows/steps/index.js.map +2 -2
- package/package.json +1 -1
- package/src/modules/cpq/__tests__/i18n/dictionaries.test.ts +62 -0
- package/src/modules/cpq/__tests__/i18n/hardcodedStrings.test.ts +35 -0
- package/src/modules/cpq/__tests__/i18n/sourceScan.ts +109 -0
- package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +3 -9
- package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +22 -26
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.tsx +43 -46
- package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +2 -2
- package/src/modules/cpq/backend/cpq/inventory/shared.tsx +37 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +134 -112
- package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +169 -130
- package/src/modules/cpq/backend/cpq/offerings/page.tsx +34 -24
- package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +66 -43
- package/src/modules/cpq/backend/cpq/orders/page.tsx +6 -6
- package/src/modules/cpq/backend/cpq/price-rules/PriceRuleForm.tsx +48 -34
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.tsx +76 -70
- package/src/modules/cpq/backend/cpq/price-rules/page.tsx +25 -16
- package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +24 -18
- package/src/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.tsx +29 -19
- package/src/modules/cpq/backend/cpq/pricing/[id]/page.tsx +95 -47
- package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.ts +23 -15
- package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.tsx +111 -88
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.tsx +3 -3
- package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +134 -89
- package/src/modules/cpq/backend/cpq/quotes/page.tsx +29 -21
- package/src/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.tsx +19 -15
- package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +171 -171
- package/src/modules/cpq/backend/cpq/specifications/page.tsx +2 -2
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.tsx +51 -41
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/page.tsx +17 -13
- package/src/modules/cpq/backend/cpq/wizards/page.tsx +1 -1
- package/src/modules/cpq/components/ArcLineDiff.tsx +2 -2
- package/src/modules/cpq/components/ChargeBreakdownTable.tsx +4 -2
- package/src/modules/cpq/components/PriceWaterfall.tsx +4 -2
- package/src/modules/cpq/components/PricingSummaryCard.tsx +1 -1
- package/src/modules/cpq/components/StatusPath.tsx +7 -4
- package/src/modules/cpq/components/useCpqRowActions.tsx +17 -25
- package/src/modules/cpq/i18n/de.json +1220 -0
- package/src/modules/cpq/i18n/en.json +1220 -0
- package/src/modules/cpq/i18n/es.json +1220 -0
- package/src/modules/cpq/i18n/pl.json +1220 -0
- package/src/modules/cpq/widgets/injection/customer-inventory/widget.client.tsx +38 -31
- package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.client.tsx +7 -5
- package/src/modules/cpq/widgets/injection/quote-configurator/widget.client.tsx +36 -21
- package/src/modules/cpq/widgets/injection-table.ts +8 -2
- package/src/modules/cpq/workflows/WizardRunner.tsx +8 -5
- package/src/modules/cpq/workflows/WizardTrigger.tsx +15 -9
- package/src/modules/cpq/workflows/steps/CustomerSelectStep.tsx +11 -7
- package/src/modules/cpq/workflows/steps/InventorySelectStep.tsx +9 -5
- package/src/modules/cpq/workflows/steps/ItemListStep.tsx +18 -12
- package/src/modules/cpq/workflows/steps/OfferingSelectStep.tsx +20 -16
- package/src/modules/cpq/workflows/steps/ProductConfigureStep.tsx +8 -6
- package/src/modules/cpq/workflows/steps/ReviewStep.tsx +33 -18
- package/src/modules/cpq/workflows/steps/index.ts +12 -0
- package/src/modules/cpq/workflows/types.ts +4 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
import { Alert } from '@open-mercato/ui/primitives/alert'
|
|
4
|
+
import { useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'
|
|
4
5
|
import { NumberInput as BaseNumberInput } from '../../../../../components/NumberInput'
|
|
5
6
|
|
|
6
7
|
// XD-250 — ARC configurator drawer.
|
|
@@ -59,19 +60,32 @@ export interface ArcQuoteConfiguratorProps {
|
|
|
59
60
|
onChanged: () => void | Promise<void>
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
63
|
+
function buildReasonCodes(t: TranslateFn): Array<{ value: string; label: string }> {
|
|
64
|
+
return [
|
|
65
|
+
{ value: 'upgrade', label: t('cpq.quotes.arc.drawer.reasons.upgrade', 'Upgrade') },
|
|
66
|
+
{ value: 'downgrade', label: t('cpq.quotes.arc.drawer.reasons.downgrade', 'Downgrade') },
|
|
67
|
+
{ value: 'config-change', label: t('cpq.quotes.arc.drawer.reasons.configChange', 'Configuration change') },
|
|
68
|
+
{ value: 'price-adjustment', label: t('cpq.quotes.arc.drawer.reasons.priceAdjustment', 'Price adjustment') },
|
|
69
|
+
{ value: 'term-extension', label: t('cpq.quotes.arc.drawer.reasons.termExtension', 'Term extension') },
|
|
70
|
+
{ value: 'term-reduction', label: t('cpq.quotes.arc.drawer.reasons.termReduction', 'Term reduction') },
|
|
71
|
+
{ value: 'consolidation', label: t('cpq.quotes.arc.drawer.reasons.consolidation', 'Consolidation') },
|
|
72
|
+
{ value: 'customer-request', label: t('cpq.quotes.arc.drawer.reasons.customerRequest', 'Customer request') },
|
|
73
|
+
{ value: 'non-payment', label: t('cpq.quotes.arc.drawer.reasons.nonPayment', 'Non-payment') },
|
|
74
|
+
{ value: 'contract-breach', label: t('cpq.quotes.arc.drawer.reasons.contractBreach', 'Contract breach') },
|
|
75
|
+
{ value: 'other', label: t('cpq.quotes.arc.drawer.reasons.other', 'Other') },
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function arcTypeLabel(t: TranslateFn, type: ArcType): string {
|
|
80
|
+
switch (type) {
|
|
81
|
+
case 'amend':
|
|
82
|
+
return t('cpq.quotes.type.amend', 'amend')
|
|
83
|
+
case 'renew':
|
|
84
|
+
return t('cpq.quotes.type.renew', 'renew')
|
|
85
|
+
case 'cancel':
|
|
86
|
+
return t('cpq.quotes.type.cancel', 'cancel')
|
|
87
|
+
}
|
|
88
|
+
}
|
|
75
89
|
|
|
76
90
|
export default function ArcQuoteConfigurator(
|
|
77
91
|
props: ArcQuoteConfiguratorProps,
|
|
@@ -88,6 +102,8 @@ export default function ArcQuoteConfigurator(
|
|
|
88
102
|
onChanged,
|
|
89
103
|
} = props
|
|
90
104
|
|
|
105
|
+
const t = useT()
|
|
106
|
+
const reasonCodes = React.useMemo(() => buildReasonCodes(t), [t])
|
|
91
107
|
const [step, setStep] = React.useState<Step>(
|
|
92
108
|
initialType === 'new' ? 'pick-action' : 'pick-targets',
|
|
93
109
|
)
|
|
@@ -98,17 +114,17 @@ export default function ArcQuoteConfigurator(
|
|
|
98
114
|
const [eligibleLoading, setEligibleLoading] = React.useState(false)
|
|
99
115
|
const [picked, setPicked] = React.useState<Record<string, EligibleSub>>(() => {
|
|
100
116
|
const seed: Record<string, EligibleSub> = {}
|
|
101
|
-
for (const
|
|
102
|
-
if (
|
|
103
|
-
seed[
|
|
104
|
-
id:
|
|
105
|
-
code:
|
|
106
|
-
name:
|
|
117
|
+
for (const target of initialTargets) {
|
|
118
|
+
if (target.subscription) {
|
|
119
|
+
seed[target.subscriptionId] = {
|
|
120
|
+
id: target.subscriptionId,
|
|
121
|
+
code: target.subscription.code,
|
|
122
|
+
name: target.subscription.name,
|
|
107
123
|
status: 'active',
|
|
108
|
-
currencyCode:
|
|
109
|
-
billingCycle:
|
|
124
|
+
currencyCode: target.subscription.currencyCode,
|
|
125
|
+
billingCycle: target.subscription.billingCycle,
|
|
110
126
|
currentTermEnd: null,
|
|
111
|
-
mrcAmount:
|
|
127
|
+
mrcAmount: target.subscription.mrcAmount,
|
|
112
128
|
}
|
|
113
129
|
}
|
|
114
130
|
}
|
|
@@ -189,16 +205,16 @@ export default function ArcQuoteConfigurator(
|
|
|
189
205
|
})
|
|
190
206
|
if (!r.ok) {
|
|
191
207
|
const body = await r.json().catch(() => ({}))
|
|
192
|
-
throw new Error(body.error ??
|
|
208
|
+
throw new Error(body.error ?? t('cpq.quotes.arc.drawer.errors.patchFailed', 'PATCH /quotes/{quoteId} failed', { quoteId }))
|
|
193
209
|
}
|
|
194
210
|
}
|
|
195
211
|
|
|
196
212
|
// Step 2 — detach removed targets.
|
|
197
|
-
const initialTargetIds = new Set(initialTargets.map((
|
|
213
|
+
const initialTargetIds = new Set(initialTargets.map((target) => target.subscriptionId))
|
|
198
214
|
const pickedIds = new Set(pickedList.map((s) => s.id))
|
|
199
|
-
for (const
|
|
200
|
-
if (!pickedIds.has(
|
|
201
|
-
await fetch(`/api/cpq/quotes/${quoteId}/target-subscriptions/${
|
|
215
|
+
for (const target of initialTargets) {
|
|
216
|
+
if (!pickedIds.has(target.subscriptionId)) {
|
|
217
|
+
await fetch(`/api/cpq/quotes/${quoteId}/target-subscriptions/${target.id}`, { method: 'DELETE' })
|
|
202
218
|
}
|
|
203
219
|
}
|
|
204
220
|
|
|
@@ -224,7 +240,7 @@ export default function ArcQuoteConfigurator(
|
|
|
224
240
|
})
|
|
225
241
|
if (!r.ok) {
|
|
226
242
|
const errBody = await r.json().catch(() => ({}))
|
|
227
|
-
throw new Error(errBody.error ??
|
|
243
|
+
throw new Error(errBody.error ?? t('cpq.quotes.arc.drawer.errors.attachFailed', 'POST /target-subscriptions failed'))
|
|
228
244
|
}
|
|
229
245
|
}
|
|
230
246
|
}
|
|
@@ -245,7 +261,7 @@ export default function ArcQuoteConfigurator(
|
|
|
245
261
|
})
|
|
246
262
|
if (!r.ok) {
|
|
247
263
|
const errBody = await r.json().catch(() => ({}))
|
|
248
|
-
throw new Error(errBody.error ??
|
|
264
|
+
throw new Error(errBody.error ?? t('cpq.quotes.arc.drawer.errors.mergeMetaFailed', 'POST /merge-meta failed'))
|
|
249
265
|
}
|
|
250
266
|
}
|
|
251
267
|
|
|
@@ -262,7 +278,7 @@ export default function ArcQuoteConfigurator(
|
|
|
262
278
|
})
|
|
263
279
|
if (!r.ok) {
|
|
264
280
|
const errBody = await r.json().catch(() => ({}))
|
|
265
|
-
throw new Error(errBody.error ??
|
|
281
|
+
throw new Error(errBody.error ?? t('cpq.quotes.arc.drawer.errors.cancelMetaFailed', 'POST /cancel-meta failed'))
|
|
266
282
|
}
|
|
267
283
|
}
|
|
268
284
|
|
|
@@ -283,13 +299,14 @@ export default function ArcQuoteConfigurator(
|
|
|
283
299
|
mergeTerm,
|
|
284
300
|
cancelMeta,
|
|
285
301
|
onChanged,
|
|
302
|
+
t,
|
|
286
303
|
])
|
|
287
304
|
|
|
288
305
|
return (
|
|
289
306
|
<div className="fixed inset-0 z-50 flex" role="dialog" aria-modal="true">
|
|
290
307
|
{/* Overlay */}
|
|
291
308
|
<button
|
|
292
|
-
aria-label=
|
|
309
|
+
aria-label={t('cpq.quotes.arc.drawer.close', 'Close drawer')}
|
|
293
310
|
className="absolute inset-0 bg-black/40"
|
|
294
311
|
onClick={onClose}
|
|
295
312
|
/>
|
|
@@ -297,7 +314,7 @@ export default function ArcQuoteConfigurator(
|
|
|
297
314
|
<div className="ml-auto h-full w-full max-w-2xl bg-card shadow-xl border-l overflow-y-auto relative flex flex-col">
|
|
298
315
|
<header className="border-b px-5 py-4 flex items-start justify-between">
|
|
299
316
|
<div>
|
|
300
|
-
<h2 className="text-lg font-semibold">Modify subscription</h2>
|
|
317
|
+
<h2 className="text-lg font-semibold">{t('cpq.quotes.arc.drawer.title', 'Modify subscription')}</h2>
|
|
301
318
|
<p className="text-xs text-muted-foreground mt-0.5">
|
|
302
319
|
{customerName ?? customerId.slice(0, 12) + '…'} • {currencyCode}
|
|
303
320
|
</p>
|
|
@@ -313,14 +330,14 @@ export default function ArcQuoteConfigurator(
|
|
|
313
330
|
<div className="flex-1 px-5 py-4 space-y-4">
|
|
314
331
|
{/* Step indicator */}
|
|
315
332
|
<p className="text-xs text-muted-foreground">
|
|
316
|
-
Step: <span className="font-mono">{step}</span>
|
|
317
|
-
{type && <span> • {type}</span>}
|
|
318
|
-
{pickedList.length > 0 && <span> • {
|
|
333
|
+
{t('cpq.quotes.arc.drawer.stepLabel', 'Step:')} <span className="font-mono">{step}</span>
|
|
334
|
+
{type && <span> • {arcTypeLabel(t, type)}</span>}
|
|
335
|
+
{pickedList.length > 0 && <span> • {t('cpq.quotes.arc.drawer.targetCount', '{count} target(s)', { count: pickedList.length })}</span>}
|
|
319
336
|
</p>
|
|
320
337
|
|
|
321
338
|
{step === 'pick-action' && (
|
|
322
339
|
<div className="space-y-3">
|
|
323
|
-
<p className="text-sm">Pick the kind of change you want to make
|
|
340
|
+
<p className="text-sm">{t('cpq.quotes.arc.drawer.pickAction', 'Pick the kind of change you want to make:')}</p>
|
|
324
341
|
{(['amend', 'renew', 'cancel'] as const).map((opt) => (
|
|
325
342
|
<button
|
|
326
343
|
key={opt}
|
|
@@ -330,11 +347,11 @@ export default function ArcQuoteConfigurator(
|
|
|
330
347
|
}}
|
|
331
348
|
className="block w-full text-left rounded-md border border-input px-4 py-3 hover:bg-muted"
|
|
332
349
|
>
|
|
333
|
-
<span className="font-medium capitalize">{opt}</span>
|
|
350
|
+
<span className="font-medium capitalize">{arcTypeLabel(t, opt)}</span>
|
|
334
351
|
<span className="block text-xs text-muted-foreground mt-0.5">
|
|
335
|
-
{opt === 'amend' && 'Add or remove items on existing subscriptions. Term untouched.'}
|
|
336
|
-
{opt === 'renew' && 'Extend term. With 2+ targets, merges them into a new contract.'}
|
|
337
|
-
{opt === 'cancel' && 'Terminate one or more subscriptions.'}
|
|
352
|
+
{opt === 'amend' && t('cpq.quotes.arc.drawer.actions.amendHint', 'Add or remove items on existing subscriptions. Term untouched.')}
|
|
353
|
+
{opt === 'renew' && t('cpq.quotes.arc.drawer.actions.renewHint', 'Extend term. With 2+ targets, merges them into a new contract.')}
|
|
354
|
+
{opt === 'cancel' && t('cpq.quotes.arc.drawer.actions.cancelHint', 'Terminate one or more subscriptions.')}
|
|
338
355
|
</span>
|
|
339
356
|
</button>
|
|
340
357
|
))}
|
|
@@ -344,9 +361,9 @@ export default function ArcQuoteConfigurator(
|
|
|
344
361
|
{step === 'pick-targets' && type && (
|
|
345
362
|
<div className="space-y-3">
|
|
346
363
|
{eligibleLoading ? (
|
|
347
|
-
<div className="text-sm text-muted-foreground">Loading subscriptions
|
|
364
|
+
<div className="text-sm text-muted-foreground">{t('cpq.quotes.arc.drawer.loadingSubscriptions', 'Loading subscriptions…')}</div>
|
|
348
365
|
) : eligible.length === 0 ? (
|
|
349
|
-
<div className="text-sm text-muted-foreground">No subscriptions for this customer
|
|
366
|
+
<div className="text-sm text-muted-foreground">{t('cpq.quotes.arc.drawer.noSubscriptions', 'No subscriptions for this customer.')}</div>
|
|
350
367
|
) : (
|
|
351
368
|
<div className="space-y-1">
|
|
352
369
|
{eligible.map((s) => {
|
|
@@ -382,14 +399,15 @@ export default function ArcQuoteConfigurator(
|
|
|
382
399
|
<span className="text-xs font-mono text-muted-foreground">{s.code}</span>
|
|
383
400
|
</div>
|
|
384
401
|
<div className="text-xs text-muted-foreground">
|
|
385
|
-
{s.status} • {s.billingCycle} • MRC {s.mrcAmount.toFixed(2)} {s.currencyCode}
|
|
402
|
+
{s.status} • {s.billingCycle} • {t('cpq.lines.mrc', 'MRC')} {s.mrcAmount.toFixed(2)} {s.currencyCode}
|
|
403
|
+
{s.currentTermEnd ? ' • ' : ''}
|
|
386
404
|
{s.currentTermEnd
|
|
387
|
-
?
|
|
405
|
+
? t('cpq.quotes.arc.drawer.termEnds', 'Term ends {date}', { date: new Date(s.currentTermEnd).toLocaleDateString() })
|
|
388
406
|
: ''}
|
|
389
407
|
</div>
|
|
390
408
|
{currencyMismatch && (
|
|
391
409
|
<div className="text-xs text-status-warning-text mt-1">
|
|
392
|
-
Currency mismatch ({
|
|
410
|
+
{t('cpq.quotes.arc.drawer.currencyMismatch', 'Currency mismatch ({subscriptionCurrency} vs {quoteCurrency}) — cannot include', { subscriptionCurrency: s.currencyCode, quoteCurrency: currencyCode })}
|
|
393
411
|
</div>
|
|
394
412
|
)}
|
|
395
413
|
</div>
|
|
@@ -400,12 +418,12 @@ export default function ArcQuoteConfigurator(
|
|
|
400
418
|
)}
|
|
401
419
|
{!cyclesMatch && (
|
|
402
420
|
<Alert variant="warning" className="text-xs">
|
|
403
|
-
Picked targets have different billing cycles — merge requires matching cycles.
|
|
421
|
+
{t('cpq.quotes.arc.drawer.cyclesMismatch', 'Picked targets have different billing cycles — merge requires matching cycles.')}
|
|
404
422
|
</Alert>
|
|
405
423
|
)}
|
|
406
424
|
{isMergeRenew && (
|
|
407
425
|
<Alert variant="warning" className="text-xs">
|
|
408
|
-
Merging {
|
|
426
|
+
{t('cpq.quotes.arc.drawer.mergeWarning', 'Merging {count} contracts into a new one — sources will be marked Merged at activation.', { count: pickedList.length })}
|
|
409
427
|
</Alert>
|
|
410
428
|
)}
|
|
411
429
|
</div>
|
|
@@ -413,15 +431,15 @@ export default function ArcQuoteConfigurator(
|
|
|
413
431
|
|
|
414
432
|
{step === 'config-renew' && type === 'renew' && !isMergeRenew && (
|
|
415
433
|
<div className="space-y-3">
|
|
416
|
-
<p className="text-sm">New contract term for {pickedList[0]?.name}
|
|
434
|
+
<p className="text-sm">{t('cpq.quotes.arc.drawer.renew.singleTitle', 'New contract term for {name}:', { name: pickedList[0]?.name ?? '' })}</p>
|
|
417
435
|
<p className="text-xs text-muted-foreground">
|
|
418
|
-
Pre-filled with same length as the current term, starting at the
|
|
419
|
-
|
|
420
|
-
auto-shift the end date, or edit start/end directly.
|
|
436
|
+
{t('cpq.quotes.arc.drawer.renew.singleHintBefore', 'Pre-filled with same length as the current term, starting at the current term end. Type a new value in')}{' '}
|
|
437
|
+
<span className="font-medium">{t('cpq.quotes.arc.drawer.fields.months', 'Months')}</span>{' '}
|
|
438
|
+
{t('cpq.quotes.arc.drawer.renew.singleHintAfter', 'to auto-shift the end date, or edit start/end directly.')}
|
|
421
439
|
</p>
|
|
422
440
|
<div className="grid grid-cols-3 gap-3">
|
|
423
441
|
<DateInput
|
|
424
|
-
label=
|
|
442
|
+
label={t('cpq.quotes.arc.drawer.fields.start', 'Start')}
|
|
425
443
|
value={singleTerm.start}
|
|
426
444
|
onChange={(v) => {
|
|
427
445
|
// If months is set, slide end too so the contract length stays.
|
|
@@ -431,12 +449,12 @@ export default function ArcQuoteConfigurator(
|
|
|
431
449
|
}}
|
|
432
450
|
/>
|
|
433
451
|
<DateInput
|
|
434
|
-
label=
|
|
452
|
+
label={t('cpq.quotes.arc.drawer.fields.end', 'End')}
|
|
435
453
|
value={singleTerm.end}
|
|
436
454
|
onChange={(v) => setSingleTerm({ ...singleTerm, end: v })}
|
|
437
455
|
/>
|
|
438
456
|
<NumberInput
|
|
439
|
-
label=
|
|
457
|
+
label={t('cpq.quotes.arc.drawer.fields.months', 'Months')}
|
|
440
458
|
value={singleTerm.months}
|
|
441
459
|
onChange={(v) => {
|
|
442
460
|
// Recompute end = start + months when both are present.
|
|
@@ -454,15 +472,16 @@ export default function ArcQuoteConfigurator(
|
|
|
454
472
|
{step === 'config-renew' && type === 'renew' && isMergeRenew && (
|
|
455
473
|
<div className="space-y-3">
|
|
456
474
|
<p className="text-sm">
|
|
457
|
-
New contract details (merge of {pickedList.length}
|
|
475
|
+
{t('cpq.quotes.arc.drawer.renew.mergeTitle', 'New contract details (merge of {count} subscriptions):', { count: pickedList.length })}
|
|
458
476
|
</p>
|
|
459
477
|
<p className="text-xs text-muted-foreground">
|
|
460
|
-
Type a value into
|
|
461
|
-
|
|
478
|
+
{t('cpq.quotes.arc.drawer.renew.mergeHintBefore', 'Type a value into')}{' '}
|
|
479
|
+
<span className="font-medium">{t('cpq.quotes.arc.drawer.fields.months', 'Months')}</span>{' '}
|
|
480
|
+
{t('cpq.quotes.arc.drawer.renew.mergeHintAfter', 'to auto-fill both dates — start defaults to today, end is start + months.')}
|
|
462
481
|
</p>
|
|
463
482
|
<div className="grid grid-cols-3 gap-3">
|
|
464
483
|
<DateInput
|
|
465
|
-
label=
|
|
484
|
+
label={t('cpq.quotes.arc.drawer.fields.start', 'Start')}
|
|
466
485
|
value={mergeTerm.start}
|
|
467
486
|
onChange={(v) => {
|
|
468
487
|
const months = mergeTerm.months ? Number(mergeTerm.months) : null
|
|
@@ -471,12 +490,12 @@ export default function ArcQuoteConfigurator(
|
|
|
471
490
|
}}
|
|
472
491
|
/>
|
|
473
492
|
<DateInput
|
|
474
|
-
label=
|
|
493
|
+
label={t('cpq.quotes.arc.drawer.fields.end', 'End')}
|
|
475
494
|
value={mergeTerm.end}
|
|
476
495
|
onChange={(v) => setMergeTerm({ ...mergeTerm, end: v })}
|
|
477
496
|
/>
|
|
478
497
|
<NumberInput
|
|
479
|
-
label=
|
|
498
|
+
label={t('cpq.quotes.arc.drawer.fields.months', 'Months')}
|
|
480
499
|
value={mergeTerm.months}
|
|
481
500
|
onChange={(v) => {
|
|
482
501
|
const months = v ? Number(v) : null
|
|
@@ -488,12 +507,12 @@ export default function ArcQuoteConfigurator(
|
|
|
488
507
|
</div>
|
|
489
508
|
<div className="grid grid-cols-2 gap-3">
|
|
490
509
|
<TextInput
|
|
491
|
-
label=
|
|
510
|
+
label={t('cpq.quotes.arc.drawer.fields.newCode', 'New contract code (optional — auto-generated if blank)')}
|
|
492
511
|
value={mergeTerm.code}
|
|
493
512
|
onChange={(v) => setMergeTerm({ ...mergeTerm, code: v })}
|
|
494
513
|
/>
|
|
495
514
|
<TextInput
|
|
496
|
-
label=
|
|
515
|
+
label={t('cpq.quotes.arc.drawer.fields.newName', 'New contract name (optional)')}
|
|
497
516
|
value={mergeTerm.name}
|
|
498
517
|
onChange={(v) => setMergeTerm({ ...mergeTerm, name: v })}
|
|
499
518
|
/>
|
|
@@ -503,16 +522,16 @@ export default function ArcQuoteConfigurator(
|
|
|
503
522
|
|
|
504
523
|
{step === 'config-cancel' && type === 'cancel' && (
|
|
505
524
|
<div className="space-y-3">
|
|
506
|
-
<p className="text-sm">Reason for cancellation
|
|
525
|
+
<p className="text-sm">{t('cpq.quotes.arc.drawer.cancel.title', 'Reason for cancellation:')}</p>
|
|
507
526
|
<div className="space-y-2">
|
|
508
|
-
<label className="block text-xs text-muted-foreground">Reason code</label>
|
|
527
|
+
<label className="block text-xs text-muted-foreground">{t('cpq.quotes.arc.drawer.cancel.reasonCode', 'Reason code')}</label>
|
|
509
528
|
<select
|
|
510
529
|
value={cancelMeta.reasonCode}
|
|
511
530
|
onChange={(e) => setCancelMeta({ ...cancelMeta, reasonCode: e.target.value })}
|
|
512
531
|
className="w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm"
|
|
513
532
|
>
|
|
514
|
-
<option value=""
|
|
515
|
-
{
|
|
533
|
+
<option value="">{t('cpq.quotes.arc.drawer.cancel.pickOne', '— pick one —')}</option>
|
|
534
|
+
{reasonCodes.map((r) => (
|
|
516
535
|
<option key={r.value} value={r.value}>
|
|
517
536
|
{r.label}
|
|
518
537
|
</option>
|
|
@@ -520,18 +539,18 @@ export default function ArcQuoteConfigurator(
|
|
|
520
539
|
</select>
|
|
521
540
|
</div>
|
|
522
541
|
<TextInput
|
|
523
|
-
label=
|
|
542
|
+
label={t('cpq.quotes.arc.drawer.cancel.freeText', 'Free text (optional)')}
|
|
524
543
|
value={cancelMeta.reasonText}
|
|
525
544
|
onChange={(v) => setCancelMeta({ ...cancelMeta, reasonText: v })}
|
|
526
545
|
/>
|
|
527
546
|
<div className="grid grid-cols-2 gap-3">
|
|
528
547
|
<TextInput
|
|
529
|
-
label=
|
|
548
|
+
label={t('cpq.quotes.arc.drawer.cancel.etfAmount', 'ETF amount (optional)')}
|
|
530
549
|
value={cancelMeta.etfAmount}
|
|
531
550
|
onChange={(v) => setCancelMeta({ ...cancelMeta, etfAmount: v })}
|
|
532
551
|
/>
|
|
533
552
|
<TextInput
|
|
534
|
-
label=
|
|
553
|
+
label={t('cpq.quotes.arc.drawer.cancel.etfCurrency', 'ETF currency')}
|
|
535
554
|
value={cancelMeta.etfCurrency}
|
|
536
555
|
onChange={(v) => setCancelMeta({ ...cancelMeta, etfCurrency: v })}
|
|
537
556
|
/>
|
|
@@ -541,10 +560,10 @@ export default function ArcQuoteConfigurator(
|
|
|
541
560
|
|
|
542
561
|
{step === 'review' && (
|
|
543
562
|
<div className="space-y-3">
|
|
544
|
-
<p className="text-sm">Review and submit
|
|
563
|
+
<p className="text-sm">{t('cpq.quotes.arc.drawer.review.title', 'Review and submit:')}</p>
|
|
545
564
|
<ul className="text-sm space-y-1 list-disc pl-5">
|
|
546
|
-
<li>Action: {type}</li>
|
|
547
|
-
<li>Targets: {pickedList.length}</li>
|
|
565
|
+
<li>{t('cpq.quotes.arc.drawer.review.action', 'Action: {type}', { type: type ? arcTypeLabel(t, type) : '' })}</li>
|
|
566
|
+
<li>{t('cpq.quotes.arc.drawer.review.targets', 'Targets: {count}', { count: pickedList.length })}</li>
|
|
548
567
|
{pickedList.map((s) => (
|
|
549
568
|
<li key={s.id} className="text-muted-foreground">
|
|
550
569
|
{s.code} — {s.name}
|
|
@@ -552,28 +571,32 @@ export default function ArcQuoteConfigurator(
|
|
|
552
571
|
))}
|
|
553
572
|
{type === 'renew' && !isMergeRenew && (
|
|
554
573
|
<li>
|
|
555
|
-
|
|
556
|
-
|
|
574
|
+
{singleTerm.months
|
|
575
|
+
? t('cpq.quotes.arc.drawer.review.termWithMonths', 'Term: {start} → {end} ({months}m)', { start: singleTerm.start, end: singleTerm.end, months: singleTerm.months })
|
|
576
|
+
: t('cpq.quotes.arc.drawer.review.term', 'Term: {start} → {end}', { start: singleTerm.start, end: singleTerm.end })}
|
|
557
577
|
</li>
|
|
558
578
|
)}
|
|
559
579
|
{type === 'renew' && isMergeRenew && (
|
|
560
580
|
<li>
|
|
561
|
-
Merge term: {
|
|
562
|
-
{mergeTerm.code ?
|
|
563
|
-
{mergeTerm.
|
|
581
|
+
{t('cpq.quotes.arc.drawer.review.mergeTerm', 'Merge term: {start} → {end}', { start: mergeTerm.start, end: mergeTerm.end })}
|
|
582
|
+
{mergeTerm.code ? ' • ' : ''}
|
|
583
|
+
{mergeTerm.code ? t('cpq.quotes.arc.drawer.review.mergeCode', 'code={code}', { code: mergeTerm.code }) : ''}
|
|
584
|
+
{mergeTerm.name ? ' • ' : ''}
|
|
585
|
+
{mergeTerm.name ? t('cpq.quotes.arc.drawer.review.mergeName', 'name={name}', { name: mergeTerm.name }) : ''}
|
|
564
586
|
</li>
|
|
565
587
|
)}
|
|
566
588
|
{type === 'cancel' && (
|
|
567
589
|
<li>
|
|
568
|
-
Reason: {cancelMeta.reasonCode}
|
|
590
|
+
{t('cpq.quotes.arc.drawer.review.reason', 'Reason: {reason}', { reason: cancelMeta.reasonCode })}
|
|
591
|
+
{cancelMeta.etfAmount ? ' • ' : ''}
|
|
569
592
|
{cancelMeta.etfAmount
|
|
570
|
-
?
|
|
593
|
+
? t('cpq.quotes.arc.drawer.review.etf', 'ETF {amount} {currency}', { amount: cancelMeta.etfAmount, currency: cancelMeta.etfCurrency })
|
|
571
594
|
: ''}
|
|
572
595
|
</li>
|
|
573
596
|
)}
|
|
574
597
|
</ul>
|
|
575
598
|
<Alert variant="warning" className="text-xs">
|
|
576
|
-
Submitting locks the quote type. Lines will be edited on the host page after this completes.
|
|
599
|
+
{t('cpq.quotes.arc.drawer.review.lockWarning', 'Submitting locks the quote type. Lines will be edited on the host page after this completes.')}
|
|
577
600
|
</Alert>
|
|
578
601
|
{submitError && (
|
|
579
602
|
<Alert variant="destructive" className="text-xs">{submitError}</Alert>
|
|
@@ -582,12 +605,12 @@ export default function ArcQuoteConfigurator(
|
|
|
582
605
|
)}
|
|
583
606
|
|
|
584
607
|
{step === 'submitting' && (
|
|
585
|
-
<div className="text-sm text-muted-foreground">Submitting
|
|
608
|
+
<div className="text-sm text-muted-foreground">{t('cpq.quotes.arc.drawer.submitting', 'Submitting…')}</div>
|
|
586
609
|
)}
|
|
587
610
|
|
|
588
611
|
{step === 'done' && (
|
|
589
612
|
<div className="text-sm text-status-success-text">
|
|
590
|
-
Done — close to return to the quote.
|
|
613
|
+
{t('cpq.quotes.arc.drawer.done', 'Done — close to return to the quote.')}
|
|
591
614
|
</div>
|
|
592
615
|
)}
|
|
593
616
|
</div>
|
|
@@ -614,14 +637,14 @@ export default function ArcQuoteConfigurator(
|
|
|
614
637
|
}
|
|
615
638
|
className="text-sm text-muted-foreground hover:text-foreground disabled:opacity-30"
|
|
616
639
|
>
|
|
617
|
-
← Back
|
|
640
|
+
{t('cpq.quotes.arc.drawer.back', '← Back')}
|
|
618
641
|
</button>
|
|
619
642
|
<div className="flex gap-2">
|
|
620
643
|
<button
|
|
621
644
|
onClick={onClose}
|
|
622
645
|
className="rounded-md border border-input px-3 py-1.5 text-sm hover:bg-muted"
|
|
623
646
|
>
|
|
624
|
-
Cancel
|
|
647
|
+
{t('cpq.common.cancel', 'Cancel')}
|
|
625
648
|
</button>
|
|
626
649
|
{step === 'pick-targets' && (
|
|
627
650
|
<button
|
|
@@ -633,7 +656,7 @@ export default function ArcQuoteConfigurator(
|
|
|
633
656
|
}}
|
|
634
657
|
className="rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
|
635
658
|
>
|
|
636
|
-
Next
|
|
659
|
+
{t('cpq.quotes.arc.drawer.next', 'Next')}
|
|
637
660
|
</button>
|
|
638
661
|
)}
|
|
639
662
|
{(step === 'config-renew' || step === 'config-cancel') && (
|
|
@@ -645,7 +668,7 @@ export default function ArcQuoteConfigurator(
|
|
|
645
668
|
onClick={() => setStep('review')}
|
|
646
669
|
className="rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
|
647
670
|
>
|
|
648
|
-
Next
|
|
671
|
+
{t('cpq.quotes.arc.drawer.next', 'Next')}
|
|
649
672
|
</button>
|
|
650
673
|
)}
|
|
651
674
|
{step === 'review' && (
|
|
@@ -654,7 +677,7 @@ export default function ArcQuoteConfigurator(
|
|
|
654
677
|
onClick={submit}
|
|
655
678
|
className="rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
|
656
679
|
>
|
|
657
|
-
Submit
|
|
680
|
+
{t('cpq.quotes.arc.drawer.submit', 'Submit')}
|
|
658
681
|
</button>
|
|
659
682
|
)}
|
|
660
683
|
{step === 'done' && (
|
|
@@ -662,7 +685,7 @@ export default function ArcQuoteConfigurator(
|
|
|
662
685
|
onClick={onClose}
|
|
663
686
|
className="rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground hover:bg-primary/90"
|
|
664
687
|
>
|
|
665
|
-
Close
|
|
688
|
+
{t('cpq.quotes.arc.drawer.closeButton', 'Close')}
|
|
666
689
|
</button>
|
|
667
690
|
)}
|
|
668
691
|
</div>
|
|
@@ -228,8 +228,8 @@ export function PriceAdjustmentDialog<Q extends PreviewQuote>(props: PriceAdjust
|
|
|
228
228
|
)}
|
|
229
229
|
{draftKey && !previewing && !previewCharge && previewLine && (
|
|
230
230
|
<span>
|
|
231
|
-
{t('cpq.priceAdjustments.dialog.previewLine', 'Line totals')}: MRC <span className="font-mono font-medium">{money(previewLine.mrcTotal, currency)}</span>
|
|
232
|
-
{' · '}NRC <span className="font-mono font-medium">{money(previewLine.nrcTotal, currency)}</span>
|
|
231
|
+
{t('cpq.priceAdjustments.dialog.previewLine', 'Line totals')}: {t('cpq.lines.mrc', 'MRC')} <span className="font-mono font-medium">{money(previewLine.mrcTotal, currency)}</span>
|
|
232
|
+
{' · '}{t('cpq.lines.nrc', 'NRC')} <span className="font-mono font-medium">{money(previewLine.nrcTotal, currency)}</span>
|
|
233
233
|
</span>
|
|
234
234
|
)}
|
|
235
235
|
</div>
|
|
@@ -250,7 +250,7 @@ export function PriceAdjustmentDialog<Q extends PreviewQuote>(props: PriceAdjust
|
|
|
250
250
|
|
|
251
251
|
<DialogFooter>
|
|
252
252
|
<Button variant="outline" onClick={onClose}>{t('cpq.priceAdjustments.dialog.cancel', 'Cancel')}</Button>
|
|
253
|
-
<Button onClick={() => void save()} disabled={!canSave} title=
|
|
253
|
+
<Button onClick={() => void save()} disabled={!canSave} title={t('cpq.priceAdjustments.dialog.saveShortcut', 'Ctrl/Cmd+Enter')}>
|
|
254
254
|
{saving ? t('cpq.priceAdjustments.dialog.saving', 'Saving…') : t('cpq.priceAdjustments.dialog.save', 'Save')}
|
|
255
255
|
</Button>
|
|
256
256
|
</DialogFooter>
|