@powerhousedao/service-offering 1.0.0-dev.1 → 1.0.0-dev.10
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/dist/document-models/facet/v1/actions.d.ts +3 -1
- package/dist/document-models/facet/v1/actions.d.ts.map +1 -1
- package/dist/document-models/facet/v1/gen/document-model.d.ts.map +1 -1
- package/dist/document-models/facet/v1/gen/document-model.js +31 -7
- package/dist/document-models/facet/v1/gen/document-schema.d.ts +6 -6
- package/dist/document-models/facet/v1/gen/index.d.ts.map +1 -1
- package/dist/document-models/facet/v1/gen/index.js +2 -0
- package/dist/document-models/facet/v1/gen/option-management/error.d.ts +27 -1
- package/dist/document-models/facet/v1/gen/option-management/error.d.ts.map +1 -1
- package/dist/document-models/facet/v1/gen/option-management/error.js +23 -1
- package/dist/document-models/facet/v1/gen/ph-factories.js +2 -2
- package/dist/document-models/facet/v1/gen/schema/types.d.ts +2 -2
- package/dist/document-models/facet/v1/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/facet/v1/gen/schema/zod.js +2 -2
- package/dist/document-models/facet/v1/gen/utils.js +2 -2
- package/dist/document-models/facet/v1/module.d.ts +1 -1
- package/dist/document-models/facet/v1/module.d.ts.map +1 -1
- package/dist/document-models/facet/v1/module.js +4 -1
- package/dist/document-models/resource-instance/v1/actions.d.ts +4 -1
- package/dist/document-models/resource-instance/v1/actions.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/configuration-management/error.d.ts +34 -1
- package/dist/document-models/resource-instance/v1/gen/configuration-management/error.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/configuration-management/error.js +32 -1
- package/dist/document-models/resource-instance/v1/gen/document-model.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/document-model.js +222 -70
- package/dist/document-models/resource-instance/v1/gen/document-schema.d.ts +0 -6
- package/dist/document-models/resource-instance/v1/gen/document-schema.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/index.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/index.js +2 -0
- package/dist/document-models/resource-instance/v1/gen/instance-management/actions.d.ts +6 -2
- package/dist/document-models/resource-instance/v1/gen/instance-management/actions.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/instance-management/creators.d.ts +3 -2
- package/dist/document-models/resource-instance/v1/gen/instance-management/creators.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/instance-management/creators.js +2 -1
- package/dist/document-models/resource-instance/v1/gen/instance-management/error.d.ts +98 -1
- package/dist/document-models/resource-instance/v1/gen/instance-management/error.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/instance-management/error.js +112 -1
- package/dist/document-models/resource-instance/v1/gen/instance-management/operations.d.ts +2 -1
- package/dist/document-models/resource-instance/v1/gen/instance-management/operations.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/ph-factories.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/ph-factories.js +0 -2
- package/dist/document-models/resource-instance/v1/gen/reducer.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/reducer.js +6 -1
- package/dist/document-models/resource-instance/v1/gen/schema/types.d.ts +3 -3
- package/dist/document-models/resource-instance/v1/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/schema/zod.d.ts +2 -1
- package/dist/document-models/resource-instance/v1/gen/schema/zod.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/schema/zod.js +5 -3
- package/dist/document-models/resource-instance/v1/gen/utils.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/gen/utils.js +0 -2
- package/dist/document-models/resource-instance/v1/module.d.ts +1 -1
- package/dist/document-models/resource-instance/v1/module.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/module.js +4 -1
- package/dist/document-models/resource-instance/v1/src/reducers/configuration-management.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/src/reducers/configuration-management.js +57 -53
- package/dist/document-models/resource-instance/v1/src/reducers/instance-management.d.ts.map +1 -1
- package/dist/document-models/resource-instance/v1/src/reducers/instance-management.js +57 -21
- package/dist/document-models/resource-instance/v1/tests/instance-management.test.js +11 -1
- package/dist/document-models/resource-template/v1/actions.d.ts +3 -1
- package/dist/document-models/resource-template/v1/actions.d.ts.map +1 -1
- package/dist/document-models/resource-template/v1/gen/audience-management/error.d.ts +20 -1
- package/dist/document-models/resource-template/v1/gen/audience-management/error.d.ts.map +1 -1
- package/dist/document-models/resource-template/v1/gen/audience-management/error.js +16 -1
- package/dist/document-models/resource-template/v1/gen/document-model.d.ts.map +1 -1
- package/dist/document-models/resource-template/v1/gen/document-model.js +207 -89
- package/dist/document-models/resource-template/v1/gen/document-schema.d.ts +12 -12
- package/dist/document-models/resource-template/v1/gen/facet-targeting/error.d.ts +27 -1
- package/dist/document-models/resource-template/v1/gen/facet-targeting/error.d.ts.map +1 -1
- package/dist/document-models/resource-template/v1/gen/facet-targeting/error.js +23 -1
- package/dist/document-models/resource-template/v1/gen/index.d.ts.map +1 -1
- package/dist/document-models/resource-template/v1/gen/index.js +2 -0
- package/dist/document-models/resource-template/v1/gen/option-group-management/error.d.ts +27 -1
- package/dist/document-models/resource-template/v1/gen/option-group-management/error.d.ts.map +1 -1
- package/dist/document-models/resource-template/v1/gen/option-group-management/error.js +23 -1
- package/dist/document-models/resource-template/v1/gen/ph-factories.js +3 -3
- package/dist/document-models/resource-template/v1/gen/schema/types.d.ts +4 -4
- package/dist/document-models/resource-template/v1/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/resource-template/v1/gen/schema/zod.js +4 -4
- package/dist/document-models/resource-template/v1/gen/service-management/error.d.ts +51 -1
- package/dist/document-models/resource-template/v1/gen/service-management/error.d.ts.map +1 -1
- package/dist/document-models/resource-template/v1/gen/service-management/error.js +49 -1
- package/dist/document-models/resource-template/v1/gen/utils.js +3 -3
- package/dist/document-models/resource-template/v1/module.d.ts +1 -1
- package/dist/document-models/resource-template/v1/module.d.ts.map +1 -1
- package/dist/document-models/resource-template/v1/module.js +4 -1
- package/dist/document-models/resource-template/v1/src/reducers/option-group-management.d.ts.map +1 -1
- package/dist/document-models/resource-template/v1/src/reducers/option-group-management.js +2 -21
- package/dist/document-models/service-offering/v1/actions.d.ts +3 -1
- package/dist/document-models/service-offering/v1/actions.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/gen/document-model.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/gen/document-model.js +421 -199
- package/dist/document-models/service-offering/v1/gen/document-schema.d.ts +9 -9
- package/dist/document-models/service-offering/v1/gen/index.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/gen/index.js +2 -0
- package/dist/document-models/service-offering/v1/gen/offering/error.d.ts +41 -1
- package/dist/document-models/service-offering/v1/gen/offering/error.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/gen/offering/error.js +37 -1
- package/dist/document-models/service-offering/v1/gen/option-groups/error.d.ts +55 -1
- package/dist/document-models/service-offering/v1/gen/option-groups/error.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/gen/option-groups/error.js +53 -1
- package/dist/document-models/service-offering/v1/gen/ph-factories.js +3 -3
- package/dist/document-models/service-offering/v1/gen/schema/types.d.ts +134 -61
- package/dist/document-models/service-offering/v1/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/gen/schema/zod.d.ts +35 -10
- package/dist/document-models/service-offering/v1/gen/schema/zod.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/gen/schema/zod.js +182 -64
- package/dist/document-models/service-offering/v1/gen/services/error.d.ts +20 -1
- package/dist/document-models/service-offering/v1/gen/services/error.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/gen/services/error.js +16 -1
- package/dist/document-models/service-offering/v1/gen/tiers/error.d.ts +100 -1
- package/dist/document-models/service-offering/v1/gen/tiers/error.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/gen/tiers/error.js +106 -1
- package/dist/document-models/service-offering/v1/gen/utils.js +4 -4
- package/dist/document-models/service-offering/v1/module.d.ts +1 -1
- package/dist/document-models/service-offering/v1/module.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/module.js +4 -1
- package/dist/document-models/service-offering/v1/src/reducers/offering.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/src/reducers/offering.js +20 -12
- package/dist/document-models/service-offering/v1/src/reducers/option-groups.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/src/reducers/option-groups.js +157 -39
- package/dist/document-models/service-offering/v1/src/reducers/services.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/src/reducers/services.js +17 -14
- package/dist/document-models/service-offering/v1/src/reducers/tiers.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/src/reducers/tiers.js +111 -78
- package/dist/document-models/service-offering/v1/src/utils.d.ts +60 -1
- package/dist/document-models/service-offering/v1/src/utils.d.ts.map +1 -1
- package/dist/document-models/service-offering/v1/src/utils.js +173 -1
- package/dist/document-models/service-offering/v1/tests/option-groups.test.js +1 -1
- package/dist/document-models/service-offering/v1/utils.d.ts +3 -0
- package/dist/document-models/service-offering/v1/utils.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/actions.d.ts +3 -1
- package/dist/document-models/subscription-instance/v1/actions.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/gen/document-model.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/gen/document-model.js +488 -246
- package/dist/document-models/subscription-instance/v1/gen/document-schema.d.ts +3 -3
- package/dist/document-models/subscription-instance/v1/gen/index.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/gen/index.js +2 -0
- package/dist/document-models/subscription-instance/v1/gen/metrics/error.d.ts +73 -1
- package/dist/document-models/subscription-instance/v1/gen/metrics/error.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/gen/metrics/error.js +86 -1
- package/dist/document-models/subscription-instance/v1/gen/ph-factories.js +1 -1
- package/dist/document-models/subscription-instance/v1/gen/schema/types.d.ts +199 -82
- package/dist/document-models/subscription-instance/v1/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/gen/schema/zod.d.ts +22 -12
- package/dist/document-models/subscription-instance/v1/gen/schema/zod.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/gen/schema/zod.js +230 -84
- package/dist/document-models/subscription-instance/v1/gen/service/error.d.ts +62 -1
- package/dist/document-models/subscription-instance/v1/gen/service/error.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/gen/service/error.js +60 -1
- package/dist/document-models/subscription-instance/v1/gen/service-group/error.d.ts +39 -1
- package/dist/document-models/subscription-instance/v1/gen/service-group/error.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/gen/service-group/error.js +39 -1
- package/dist/document-models/subscription-instance/v1/gen/subscription/error.d.ts +55 -1
- package/dist/document-models/subscription-instance/v1/gen/subscription/error.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/gen/subscription/error.js +51 -1
- package/dist/document-models/subscription-instance/v1/gen/utils.js +2 -2
- package/dist/document-models/subscription-instance/v1/module.d.ts +1 -1
- package/dist/document-models/subscription-instance/v1/module.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/module.js +4 -1
- package/dist/document-models/subscription-instance/v1/src/reducers/customer.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/src/reducers/customer.js +1 -0
- package/dist/document-models/subscription-instance/v1/src/reducers/metrics.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/src/reducers/metrics.js +70 -45
- package/dist/document-models/subscription-instance/v1/src/reducers/service-group.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/src/reducers/service-group.js +108 -30
- package/dist/document-models/subscription-instance/v1/src/reducers/service.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/src/reducers/service.js +108 -39
- package/dist/document-models/subscription-instance/v1/src/reducers/subscription.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/src/reducers/subscription.js +193 -35
- package/dist/document-models/upgrade-manifests.d.ts.map +1 -1
- package/dist/document-models/upgrade-manifests.js +0 -2
- package/dist/editors/resource-instance-editor/editor.d.ts.map +1 -1
- package/dist/editors/resource-instance-editor/editor.js +13 -3
- package/dist/editors/service-offering-editor/components/ResourceTemplateSelector.d.ts.map +1 -1
- package/dist/editors/service-offering-editor/components/ResourceTemplateSelector.js +4 -2
- package/dist/editors/service-offering-editor/components/ServiceCatalog.d.ts.map +1 -1
- package/dist/editors/service-offering-editor/components/ServiceCatalog.js +189 -32
- package/dist/editors/service-offering-editor/components/TheMatrix.d.ts +1 -1
- package/dist/editors/service-offering-editor/components/TheMatrix.d.ts.map +1 -1
- package/dist/editors/service-offering-editor/components/TheMatrix.js +295 -140
- package/dist/editors/service-offering-editor/components/TierDefinition.d.ts.map +1 -1
- package/dist/editors/service-offering-editor/components/TierDefinition.js +2 -0
- package/dist/editors/service-offering-editor/components/TierPricingOptionsPanel.js +3 -3
- package/dist/editors/service-offering-editor/components/pricing-utils.d.ts.map +1 -1
- package/dist/editors/service-offering-editor/components/pricing-utils.js +26 -7
- package/dist/editors/service-offering-editor/editor.css +119 -0
- package/dist/editors/subscription-instance-editor/components/BillingPanel.d.ts.map +1 -1
- package/dist/editors/subscription-instance-editor/components/BillingPanel.js +4 -4
- package/dist/editors/subscription-instance-editor/components/CustomerInfo.d.ts.map +1 -1
- package/dist/editors/subscription-instance-editor/components/CustomerInfo.js +3 -2
- package/dist/editors/subscription-instance-editor/components/ImportServiceConfigButton.d.ts +3 -0
- package/dist/editors/subscription-instance-editor/components/ImportServiceConfigButton.d.ts.map +1 -1
- package/dist/editors/subscription-instance-editor/components/ImportServiceConfigButton.js +12 -0
- package/dist/editors/subscription-instance-editor/components/MetricActions.d.ts.map +1 -1
- package/dist/editors/subscription-instance-editor/components/MetricActions.js +4 -2
- package/dist/editors/subscription-instance-editor/components/MockDataButton.d.ts.map +1 -1
- package/dist/editors/subscription-instance-editor/components/MockDataButton.js +214 -2
- package/dist/editors/subscription-instance-editor/components/OperatorNotes.js +1 -1
- package/dist/editors/subscription-instance-editor/components/ServicesPanel.d.ts.map +1 -1
- package/dist/editors/subscription-instance-editor/components/ServicesPanel.js +9 -20
- package/dist/editors/subscription-instance-editor/components/SubscriptionActions.d.ts.map +1 -1
- package/dist/editors/subscription-instance-editor/components/SubscriptionActions.js +8 -9
- package/dist/editors/subscription-instance-editor/components/SubscriptionHeader.d.ts.map +1 -1
- package/dist/editors/subscription-instance-editor/components/SubscriptionHeader.js +1 -1
- package/dist/editors/subscription-instance-editor/components/billing-utils.d.ts +14 -6
- package/dist/editors/subscription-instance-editor/components/billing-utils.d.ts.map +1 -1
- package/dist/editors/subscription-instance-editor/components/billing-utils.js +19 -23
- package/dist/editors/subscription-instance-editor/components/mapOfferingToSubscription.d.ts +16 -2
- package/dist/editors/subscription-instance-editor/components/mapOfferingToSubscription.d.ts.map +1 -1
- package/dist/editors/subscription-instance-editor/components/mapOfferingToSubscription.js +155 -6
- package/dist/powerhouse.manifest.json +29 -3
- package/dist/style.css +14 -0
- package/dist/subgraphs/resources-services/resolvers.d.ts +2 -2
- package/dist/subgraphs/resources-services/resolvers.d.ts.map +1 -1
- package/dist/subgraphs/resources-services/resolvers.js +285 -213
- package/package.json +24 -19
- package/dist/document-models/facet/gen/schema/types.d.ts +0 -195
- package/dist/document-models/facet/gen/schema/types.d.ts.map +0 -1
- package/dist/document-models/facet/gen/schema/types.js +0 -1
- package/dist/document-models/facet/gen/schema/zod.d.ts +0 -18
- package/dist/document-models/facet/gen/schema/zod.d.ts.map +0 -1
- package/dist/document-models/facet/gen/schema/zod.js +0 -69
- package/dist/document-models/resource-instance/gen/schema/types.d.ts +0 -272
- package/dist/document-models/resource-instance/gen/schema/types.d.ts.map +0 -1
- package/dist/document-models/resource-instance/gen/schema/types.js +0 -1
- package/dist/document-models/resource-instance/gen/schema/zod.d.ts +0 -43
- package/dist/document-models/resource-instance/gen/schema/zod.d.ts.map +0 -1
- package/dist/document-models/resource-instance/gen/schema/zod.js +0 -185
- package/dist/document-models/resource-template/gen/schema/types.d.ts +0 -371
- package/dist/document-models/resource-template/gen/schema/types.d.ts.map +0 -1
- package/dist/document-models/resource-template/gen/schema/types.js +0 -1
- package/dist/document-models/resource-template/gen/schema/zod.d.ts +0 -52
- package/dist/document-models/resource-template/gen/schema/zod.d.ts.map +0 -1
- package/dist/document-models/resource-template/gen/schema/zod.js +0 -312
- package/dist/document-models/service-offering/gen/schema/types.d.ts +0 -451
- package/dist/document-models/service-offering/gen/schema/types.d.ts.map +0 -1
- package/dist/document-models/service-offering/gen/schema/types.js +0 -1
- package/dist/document-models/service-offering/gen/schema/zod.d.ts +0 -89
- package/dist/document-models/service-offering/gen/schema/zod.d.ts.map +0 -1
- package/dist/document-models/service-offering/gen/schema/zod.js +0 -419
- package/dist/document-models/subscription-instance/gen/schema/types.d.ts +0 -435
- package/dist/document-models/subscription-instance/gen/schema/types.d.ts.map +0 -1
- package/dist/document-models/subscription-instance/gen/schema/types.js +0 -1
- package/dist/document-models/subscription-instance/gen/schema/zod.d.ts +0 -99
- package/dist/document-models/subscription-instance/gen/schema/zod.d.ts.map +0 -1
- package/dist/document-models/subscription-instance/gen/schema/zod.js +0 -410
|
@@ -10,28 +10,240 @@ export function MockDataButton({ document, dispatch }) {
|
|
|
10
10
|
const oneMonthAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
11
11
|
const oneMonthFromNow = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
12
12
|
const twoMonthsFromNow = new Date(Date.now() + 60 * 24 * 60 * 60 * 1000).toISOString();
|
|
13
|
+
// 1. Initialize subscription with full service group structure
|
|
14
|
+
// Pricing lives at the SERVICE GROUP level, not per-service
|
|
13
15
|
if (!document.state.global.customerId) {
|
|
14
16
|
dispatch(initializeSubscription({
|
|
15
17
|
createdAt: oneMonthAgo,
|
|
16
18
|
customerId: `phid:customer:${generateId()}`,
|
|
17
19
|
customerName: "Acme Corporation",
|
|
18
20
|
customerEmail: "billing@acme.example.com",
|
|
21
|
+
resourceId: `phid:resource:${generateId()}`,
|
|
22
|
+
resourceLabel: "Enterprise Cloud Platform",
|
|
19
23
|
tierName: "Professional",
|
|
20
24
|
tierPricingOptionId: generateId(),
|
|
21
25
|
tierPrice: 726,
|
|
22
26
|
tierCurrency: "USD",
|
|
23
|
-
tierPricingMode: "
|
|
27
|
+
tierPricingMode: "CALCULATED",
|
|
24
28
|
selectedBillingCycle: "MONTHLY",
|
|
25
29
|
globalCurrency: "USD",
|
|
30
|
+
autoRenew: true,
|
|
31
|
+
serviceGroups: [
|
|
32
|
+
// === Core Infrastructure (recurring, group-level price: $726/mo) ===
|
|
33
|
+
{
|
|
34
|
+
id: generateId(),
|
|
35
|
+
name: "Core Infrastructure",
|
|
36
|
+
optional: false,
|
|
37
|
+
costType: "RECURRING",
|
|
38
|
+
recurringAmount: 726,
|
|
39
|
+
recurringCurrency: "USD",
|
|
40
|
+
recurringBillingCycle: "MONTHLY",
|
|
41
|
+
setupAmount: 500,
|
|
42
|
+
setupCurrency: "USD",
|
|
43
|
+
services: [
|
|
44
|
+
{
|
|
45
|
+
id: generateId(),
|
|
46
|
+
name: "Cloud Compute",
|
|
47
|
+
description: "Scalable virtual machines with automatic load balancing",
|
|
48
|
+
metrics: [
|
|
49
|
+
{
|
|
50
|
+
id: generateId(),
|
|
51
|
+
name: "vCPU Hours",
|
|
52
|
+
unitName: "hours",
|
|
53
|
+
currentUsage: 1250,
|
|
54
|
+
freeLimit: 1000,
|
|
55
|
+
paidLimit: 2000,
|
|
56
|
+
unitCostAmount: 0.05,
|
|
57
|
+
unitCostCurrency: "USD",
|
|
58
|
+
unitCostBillingCycle: "MONTHLY",
|
|
59
|
+
usageResetPeriod: "MONTHLY",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: generateId(),
|
|
63
|
+
name: "RAM GB-Hours",
|
|
64
|
+
unitName: "GB-hours",
|
|
65
|
+
currentUsage: 3200,
|
|
66
|
+
freeLimit: 3000,
|
|
67
|
+
paidLimit: 5000,
|
|
68
|
+
usageResetPeriod: "MONTHLY",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: generateId(),
|
|
72
|
+
name: "Active Instances",
|
|
73
|
+
unitName: "instances",
|
|
74
|
+
currentUsage: 8,
|
|
75
|
+
freeLimit: 10,
|
|
76
|
+
paidLimit: 20,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: generateId(),
|
|
82
|
+
name: "Object Storage",
|
|
83
|
+
description: "Unlimited cloud storage with CDN integration",
|
|
84
|
+
metrics: [
|
|
85
|
+
{
|
|
86
|
+
id: generateId(),
|
|
87
|
+
name: "Storage Used",
|
|
88
|
+
unitName: "GB",
|
|
89
|
+
currentUsage: 450,
|
|
90
|
+
freeLimit: 500,
|
|
91
|
+
paidLimit: 2000,
|
|
92
|
+
unitCostAmount: 0.02,
|
|
93
|
+
unitCostCurrency: "USD",
|
|
94
|
+
unitCostBillingCycle: "MONTHLY",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: generateId(),
|
|
98
|
+
name: "Bandwidth Out",
|
|
99
|
+
unitName: "GB",
|
|
100
|
+
currentUsage: 280,
|
|
101
|
+
freeLimit: 500,
|
|
102
|
+
usageResetPeriod: "MONTHLY",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: generateId(),
|
|
106
|
+
name: "API Requests",
|
|
107
|
+
unitName: "requests",
|
|
108
|
+
currentUsage: 125000,
|
|
109
|
+
freeLimit: 100000,
|
|
110
|
+
paidLimit: 500000,
|
|
111
|
+
unitCostAmount: 0.001,
|
|
112
|
+
unitCostCurrency: "USD",
|
|
113
|
+
unitCostBillingCycle: "MONTHLY",
|
|
114
|
+
usageResetPeriod: "MONTHLY",
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: generateId(),
|
|
120
|
+
name: "Managed Database",
|
|
121
|
+
description: "PostgreSQL with automatic backups and failover",
|
|
122
|
+
metrics: [
|
|
123
|
+
{
|
|
124
|
+
id: generateId(),
|
|
125
|
+
name: "Database Size",
|
|
126
|
+
unitName: "GB",
|
|
127
|
+
currentUsage: 85,
|
|
128
|
+
freeLimit: 50,
|
|
129
|
+
paidLimit: 100,
|
|
130
|
+
unitCostAmount: 0.5,
|
|
131
|
+
unitCostCurrency: "USD",
|
|
132
|
+
unitCostBillingCycle: "MONTHLY",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: generateId(),
|
|
136
|
+
name: "Connections",
|
|
137
|
+
unitName: "connections",
|
|
138
|
+
currentUsage: 45,
|
|
139
|
+
freeLimit: 50,
|
|
140
|
+
paidLimit: 100,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: generateId(),
|
|
146
|
+
name: "Global CDN",
|
|
147
|
+
description: "Content delivery network with edge caching",
|
|
148
|
+
metrics: [
|
|
149
|
+
{
|
|
150
|
+
id: generateId(),
|
|
151
|
+
name: "Cache Hit Rate",
|
|
152
|
+
unitName: "%",
|
|
153
|
+
currentUsage: 94,
|
|
154
|
+
limit: 100,
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: generateId(),
|
|
158
|
+
name: "Edge Requests",
|
|
159
|
+
unitName: "M requests",
|
|
160
|
+
currentUsage: 12,
|
|
161
|
+
freeLimit: 10,
|
|
162
|
+
paidLimit: 50,
|
|
163
|
+
unitCostAmount: 5,
|
|
164
|
+
unitCostCurrency: "USD",
|
|
165
|
+
unitCostBillingCycle: "MONTHLY",
|
|
166
|
+
usageResetPeriod: "MONTHLY",
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
},
|
|
172
|
+
// === Security Suite (optional add-on, group-level price: $178/mo) ===
|
|
173
|
+
{
|
|
174
|
+
id: generateId(),
|
|
175
|
+
name: "Security Suite",
|
|
176
|
+
optional: true,
|
|
177
|
+
costType: "RECURRING",
|
|
178
|
+
recurringAmount: 178,
|
|
179
|
+
recurringCurrency: "USD",
|
|
180
|
+
recurringBillingCycle: "MONTHLY",
|
|
181
|
+
setupAmount: 750,
|
|
182
|
+
setupCurrency: "USD",
|
|
183
|
+
services: [
|
|
184
|
+
{
|
|
185
|
+
id: generateId(),
|
|
186
|
+
name: "DDoS Protection",
|
|
187
|
+
description: "Advanced DDoS mitigation with real-time monitoring",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: generateId(),
|
|
191
|
+
name: "WAF",
|
|
192
|
+
description: "Web Application Firewall with custom rules",
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: generateId(),
|
|
196
|
+
name: "Security Audit & Onboarding",
|
|
197
|
+
description: "Initial security assessment and configuration",
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
},
|
|
201
|
+
// === Premium Support (optional add-on, group-level price: $498/mo) ===
|
|
202
|
+
{
|
|
203
|
+
id: generateId(),
|
|
204
|
+
name: "Premium Support",
|
|
205
|
+
optional: true,
|
|
206
|
+
costType: "RECURRING",
|
|
207
|
+
recurringAmount: 498,
|
|
208
|
+
recurringCurrency: "USD",
|
|
209
|
+
recurringBillingCycle: "MONTHLY",
|
|
210
|
+
setupAmount: 1200,
|
|
211
|
+
setupCurrency: "USD",
|
|
212
|
+
services: [
|
|
213
|
+
{
|
|
214
|
+
id: generateId(),
|
|
215
|
+
name: "24/7 Priority Support",
|
|
216
|
+
description: "Direct access to senior engineers with 15-min response time",
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: generateId(),
|
|
220
|
+
name: "Dedicated Account Manager",
|
|
221
|
+
description: "Personal account manager for strategic guidance",
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
id: generateId(),
|
|
225
|
+
name: "Team Onboarding Workshop",
|
|
226
|
+
description: "Customized training sessions for your engineering team",
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
],
|
|
26
231
|
}));
|
|
27
232
|
}
|
|
233
|
+
// 2. Activate subscription
|
|
28
234
|
dispatch(activateSubscription({
|
|
29
|
-
|
|
235
|
+
activatedSince: oneMonthAgo,
|
|
30
236
|
}));
|
|
237
|
+
// 3. Set renewal date
|
|
31
238
|
dispatch(setRenewalDate({ renewalDate: twoMonthsFromNow }));
|
|
239
|
+
// 4. Set customer type
|
|
32
240
|
dispatch(setCustomerType({
|
|
33
241
|
customerType: "TEAM",
|
|
242
|
+
teamMemberCount: 12,
|
|
34
243
|
}));
|
|
244
|
+
// 5. Billing projection
|
|
245
|
+
// Core: $726/mo + Security: $178/mo + Support: $498/mo = $1,402/mo
|
|
246
|
+
// Plus metric overages: vCPU 250 × $0.05 = $12.50, API 25k × $0.001 = $25, DB 35 × $0.50 = $17.50, Edge 2M × $5 = $10
|
|
35
247
|
dispatch(updateBillingProjection({
|
|
36
248
|
nextBillingDate: oneMonthFromNow,
|
|
37
249
|
projectedBillAmount: 1467,
|
|
@@ -6,7 +6,7 @@ export function OperatorNotes({ document, dispatch }) {
|
|
|
6
6
|
const [isEditing, setIsEditing] = useState(false);
|
|
7
7
|
const [notes, setNotes] = useState(currentNotes);
|
|
8
8
|
const handleSave = useCallback(() => {
|
|
9
|
-
dispatch(setOperatorNotes({
|
|
9
|
+
dispatch(setOperatorNotes({ operatorNotes: notes || null }));
|
|
10
10
|
setIsEditing(false);
|
|
11
11
|
}, [dispatch, notes]);
|
|
12
12
|
const handleCancel = useCallback(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServicesPanel.d.ts","sourceRoot":"","sources":["../../../../editors/subscription-instance-editor/components/ServicesPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,MAAM,uEAAuE,CAAC;AAK/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ServicesPanel.d.ts","sourceRoot":"","sources":["../../../../editors/subscription-instance-editor/components/ServicesPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,MAAM,uEAAuE,CAAC;AAK/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQ5C,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,4BAA4B,CAAC;IACvC,QAAQ,EAAE,gBAAgB,CAAC,0BAA0B,CAAC,CAAC;IACvD,IAAI,EAAE,QAAQ,CAAC;CAChB;AAkKD,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,QAAQ,EACR,IAAI,GACL,EAAE,kBAAkB,2CA6KpB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { MetricActions } from "./MetricActions.js";
|
|
3
|
-
import { formatCurrency as fmtCurrency } from "./billing-utils.js";
|
|
4
|
-
function UsageBar({ serviceId, metric, dispatch, isOperator, customerName,
|
|
3
|
+
import { formatCurrency as fmtCurrency, formatBillingCycleSuffix, formatDiscountBadge, } from "./billing-utils.js";
|
|
4
|
+
function UsageBar({ serviceId, metric, dispatch, isOperator, customerName, }) {
|
|
5
5
|
const freeLimit = metric.freeLimit ?? metric.limit ?? 0;
|
|
6
6
|
const paidLimit = metric.paidLimit ?? null;
|
|
7
7
|
const displayLimit = paidLimit ?? freeLimit;
|
|
@@ -17,35 +17,24 @@ function UsageBar({ serviceId, metric, dispatch, isOperator, customerName, curre
|
|
|
17
17
|
return "si-usage-bar__fill--normal";
|
|
18
18
|
};
|
|
19
19
|
const isOverFree = metric.currentUsage > freeLimit && freeLimit > 0;
|
|
20
|
-
return (_jsxs("div", { className: "si-metric", children: [_jsxs("div", { className: "si-metric__header", children: [_jsx("span", { className: "si-metric__name", children: metric.name }), _jsxs("span", { className: "si-metric__value", children: [metric.currentUsage.toLocaleString(), displayLimit > 0 && ` / ${displayLimit.toLocaleString()}`, _jsxs("span", { className: "si-metric__unit", children: [" ", metric.unitName] })] })] }), _jsxs("div", { className: "si-metric__body", children: [displayLimit > 0 && (_jsxs("div", { className: "si-usage-bar", children: [paidLimit != null && freeLimit > 0 && freeLimit < paidLimit && (_jsx("div", { className: "si-usage-bar__free-marker", style: { left: `${freePortion}%` }, title: `${freeLimit.toLocaleString()} free` })), _jsx("div", { className: `si-usage-bar__fill ${getBarColor()}`, style: { width: `${percentage}%` }, role: "progressbar", "aria-valuenow": metric.currentUsage, "aria-valuemin": 0, "aria-valuemax": displayLimit })] })), _jsx(MetricActions, { serviceId: serviceId, metric: metric, dispatch: dispatch, isOperator: isOperator, customerName: customerName })] }), paidLimit != null && freeLimit > 0 && freeLimit !== paidLimit && (_jsxs("p", { className: "si-metric__paid-limit", children: [freeLimit.toLocaleString(), " free \u00B7 ", paidLimit.toLocaleString(), " max", metric.unitCost
|
|
20
|
+
return (_jsxs("div", { className: "si-metric", children: [_jsxs("div", { className: "si-metric__header", children: [_jsx("span", { className: "si-metric__name", children: metric.name }), _jsxs("span", { className: "si-metric__value", children: [metric.currentUsage.toLocaleString(), displayLimit > 0 && ` / ${displayLimit.toLocaleString()}`, _jsxs("span", { className: "si-metric__unit", children: [" ", metric.unitName] })] })] }), _jsxs("div", { className: "si-metric__body", children: [displayLimit > 0 && (_jsxs("div", { className: "si-usage-bar", children: [paidLimit != null && freeLimit > 0 && freeLimit < paidLimit && (_jsx("div", { className: "si-usage-bar__free-marker", style: { left: `${freePortion}%` }, title: `${freeLimit.toLocaleString()} free` })), _jsx("div", { className: `si-usage-bar__fill ${getBarColor()}`, style: { width: `${percentage}%` }, role: "progressbar", "aria-valuenow": metric.currentUsage, "aria-valuemin": 0, "aria-valuemax": displayLimit })] })), _jsx(MetricActions, { serviceId: serviceId, metric: metric, dispatch: dispatch, isOperator: isOperator, customerName: customerName })] }), paidLimit != null && freeLimit > 0 && freeLimit !== paidLimit && (_jsxs("p", { className: "si-metric__paid-limit", children: [freeLimit.toLocaleString(), " free \u00B7 ", paidLimit.toLocaleString(), " max", metric.unitCost && (_jsxs("span", { children: [" · ", "overage:", " ", fmtCurrency(metric.unitCost.amount, metric.unitCost.currency), "/", metric.unitName] }))] })), isOverFree && metric.unitCost && (_jsxs("div", { className: "si-metric__overage", children: [_jsx("strong", { children: (metric.currentUsage - freeLimit).toLocaleString() }), " ", metric.unitName, " over free limit"] })), (metric.nextUsageReset || metric.usageResetPeriod) && (_jsxs("p", { className: "si-metric__reset", children: [metric.usageResetPeriod && (_jsxs("span", { className: "si-metric__reset-period", children: [metric.usageResetPeriod.charAt(0) +
|
|
21
21
|
metric.usageResetPeriod.slice(1).toLowerCase(), " ", "reset"] })), metric.nextUsageReset && (_jsxs("span", { children: [metric.usageResetPeriod ? " · " : "Resets ", new Date(metric.nextUsageReset).toLocaleDateString()] }))] }))] }));
|
|
22
22
|
}
|
|
23
|
-
function ServiceCard({ service, mode, dispatch, customerName,
|
|
24
|
-
return (_jsxs("div", { className: "si-service-card", children: [_jsxs("div", { className: "si-service-card__header", children: [_jsx("h4", { className: "si-service-card__name", children: service.name || "Service" }), service.customValue && (_jsx("span", { className: "si-service-card__custom-value", children: service.customValue }))] }), service.description && (_jsx("p", { className: "si-service-card__desc", children: service.description })), service.metrics.length > 0 && (_jsx("div", { className: "si-service-card__metrics", children: service.metrics.map((metric) => (_jsx(UsageBar, { serviceId: service.id, metric: metric, dispatch: dispatch, isOperator: mode === "operator", customerName: customerName
|
|
23
|
+
function ServiceCard({ service, mode, dispatch, customerName, }) {
|
|
24
|
+
return (_jsxs("div", { className: "si-service-card", children: [_jsxs("div", { className: "si-service-card__header", children: [_jsx("h4", { className: "si-service-card__name", children: service.name || "Service" }), service.customValue && (_jsx("span", { className: "si-service-card__custom-value", children: service.customValue }))] }), service.description && (_jsx("p", { className: "si-service-card__desc", children: service.description })), service.metrics.length > 0 && (_jsx("div", { className: "si-service-card__metrics", children: service.metrics.map((metric) => (_jsx(UsageBar, { serviceId: service.id, metric: metric, dispatch: dispatch, isOperator: mode === "operator", customerName: customerName }, metric.id))) }))] }));
|
|
25
25
|
}
|
|
26
26
|
export function ServicesPanel({ document, dispatch, mode, }) {
|
|
27
27
|
const state = document.state.global;
|
|
28
|
-
|
|
29
|
-
const allGroupedServiceIds = new Set(state.serviceGroups.flatMap((g) => g.services));
|
|
30
|
-
const standaloneServices = state.services.filter((s) => !allGroupedServiceIds.has(s.id));
|
|
28
|
+
// Split groups into recurring (non-optional) and add-ons (optional)
|
|
31
29
|
const recurringGroups = state.serviceGroups.filter((g) => !g.optional);
|
|
32
30
|
const addonGroups = state.serviceGroups.filter((g) => g.optional);
|
|
33
|
-
const hasRecurring =
|
|
31
|
+
const hasRecurring = state.services.length > 0 || recurringGroups.length > 0;
|
|
34
32
|
const hasAddons = addonGroups.length > 0;
|
|
35
|
-
const recurringServiceCount =
|
|
33
|
+
const recurringServiceCount = state.services.length +
|
|
36
34
|
recurringGroups.reduce((acc, g) => acc + g.services.length, 0);
|
|
37
35
|
const addonServiceCount = addonGroups.reduce((acc, g) => acc + g.services.length, 0);
|
|
38
36
|
if (!hasRecurring && !hasAddons) {
|
|
39
37
|
return (_jsxs("div", { className: "si-panel", children: [_jsx("div", { className: "si-panel__header", children: _jsx("h3", { className: "si-panel__title", children: "Services" }) }), _jsxs("div", { className: "si-empty", children: [_jsx("svg", { className: "si-empty__icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" }) }), _jsx("p", { className: "si-empty__text", children: "No services configured" })] })] }));
|
|
40
38
|
}
|
|
41
|
-
|
|
42
|
-
.map((id) => state.services.find((s) => s.id === id))
|
|
43
|
-
.filter((s) => s != null);
|
|
44
|
-
return (_jsxs(_Fragment, { children: [hasRecurring && (_jsxs("div", { className: "si-panel", children: [_jsxs("div", { className: "si-panel__header", children: [_jsx("h3", { className: "si-panel__title", children: "Recurring Services" }), _jsxs("span", { className: "si-panel__count", children: [recurringServiceCount, " services"] })] }), standaloneServices.length > 0 && (_jsx("div", { className: "si-services-grid", children: standaloneServices.map((service) => (_jsx(ServiceCard, { service: service, mode: mode, dispatch: dispatch, customerName: state.customerName, currency: currency }, service.id))) })), recurringGroups.map((group) => {
|
|
45
|
-
const groupServices = resolveGroupServices(group.services);
|
|
46
|
-
return (_jsxs("div", { className: "si-service-group", children: [_jsxs("div", { className: "si-service-group__header", children: [_jsx("h4", { className: "si-service-group__name", children: group.name }), group.recurringCost && (_jsx("span", { className: "si-service-group__price", children: fmtCurrency(group.recurringCost.amount, group.recurringCost.currency) }))] }), _jsx("div", { className: "si-services-grid", children: groupServices.map((service) => (_jsx(ServiceCard, { service: service, mode: mode, dispatch: dispatch, customerName: state.customerName, currency: currency }, service.id))) })] }, group.id));
|
|
47
|
-
})] })), hasAddons && (_jsxs("div", { className: "si-panel", children: [_jsxs("div", { className: "si-panel__header", children: [_jsx("h3", { className: "si-panel__title", children: "Add-ons" }), _jsxs("span", { className: "si-panel__count", children: [addonGroups.length, " groups, ", addonServiceCount, " services"] })] }), addonGroups.map((group) => {
|
|
48
|
-
const groupServices = resolveGroupServices(group.services);
|
|
49
|
-
return (_jsxs("div", { className: "si-service-group", children: [_jsxs("div", { className: "si-service-group__header", children: [_jsx("h4", { className: "si-service-group__name", children: group.name }), _jsx("span", { className: "si-badge si-badge--violet si-badge--sm", children: "Optional" }), group.recurringCost && (_jsx("span", { className: "si-service-group__price", children: fmtCurrency(group.recurringCost.amount, group.recurringCost.currency) }))] }), _jsx("div", { className: "si-services-grid", children: groupServices.map((service) => (_jsx(ServiceCard, { service: service, mode: mode, dispatch: dispatch, customerName: state.customerName, currency: currency }, service.id))) })] }, group.id));
|
|
50
|
-
})] }))] }));
|
|
39
|
+
return (_jsxs(_Fragment, { children: [hasRecurring && (_jsxs("div", { className: "si-panel", children: [_jsxs("div", { className: "si-panel__header", children: [_jsx("h3", { className: "si-panel__title", children: "Recurring Services" }), _jsxs("span", { className: "si-panel__count", children: [recurringServiceCount, " services"] })] }), state.services.length > 0 && (_jsx("div", { className: "si-services-grid", children: state.services.map((service) => (_jsx(ServiceCard, { service: service, mode: mode, dispatch: dispatch, customerName: state.customerName }, service.id))) })), recurringGroups.map((group) => (_jsxs("div", { className: "si-service-group", children: [_jsxs("div", { className: "si-service-group__header", children: [_jsx("h4", { className: "si-service-group__name", children: group.name }), group.recurringCost && (_jsxs("span", { className: "si-service-group__price", children: [group.recurringCost.discount && (_jsxs(_Fragment, { children: [_jsx("span", { className: "si-service-group__original-price", children: fmtCurrency(group.recurringCost.discount.originalAmount, group.recurringCost.currency) }), _jsx("span", { className: "si-service-group__discount-badge", children: formatDiscountBadge(group.recurringCost.discount) })] })), fmtCurrency(group.recurringCost.amount, group.recurringCost.currency), formatBillingCycleSuffix(group.recurringCost.billingCycle)] }))] }), _jsx("div", { className: "si-services-grid", children: group.services.map((service) => (_jsx(ServiceCard, { service: service, mode: mode, dispatch: dispatch, customerName: state.customerName }, service.id))) })] }, group.id)))] })), hasAddons && (_jsxs("div", { className: "si-panel", children: [_jsxs("div", { className: "si-panel__header", children: [_jsx("h3", { className: "si-panel__title", children: "Add-ons" }), _jsxs("span", { className: "si-panel__count", children: [addonGroups.length, " groups, ", addonServiceCount, " services"] })] }), addonGroups.map((group) => (_jsxs("div", { className: "si-service-group", children: [_jsxs("div", { className: "si-service-group__header", children: [_jsx("h4", { className: "si-service-group__name", children: group.name }), _jsx("span", { className: "si-badge si-badge--violet si-badge--sm", children: "Optional" }), group.recurringCost && (_jsxs("span", { className: "si-service-group__price", children: [group.recurringCost.discount && (_jsxs(_Fragment, { children: [_jsx("span", { className: "si-service-group__original-price", children: fmtCurrency(group.recurringCost.discount.originalAmount, group.recurringCost.currency) }), _jsx("span", { className: "si-service-group__discount-badge", children: formatDiscountBadge(group.recurringCost.discount) })] })), fmtCurrency(group.recurringCost.amount, group.recurringCost.currency), formatBillingCycleSuffix(group.recurringCost.billingCycle)] }))] }), _jsx("div", { className: "si-services-grid", children: group.services.map((service) => (_jsx(ServiceCard, { service: service, mode: mode, dispatch: dispatch, customerName: state.customerName }, service.id))) })] }, group.id)))] }))] }));
|
|
51
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubscriptionActions.d.ts","sourceRoot":"","sources":["../../../../editors/subscription-instance-editor/components/SubscriptionActions.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SubscriptionActions.d.ts","sourceRoot":"","sources":["../../../../editors/subscription-instance-editor/components/SubscriptionActions.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,MAAM,uEAAuE,CAAC;AAC/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAS5C,UAAU,wBAAwB;IAChC,QAAQ,EAAE,4BAA4B,CAAC;IACvC,QAAQ,EAAE,gBAAgB,CAAC,0BAA0B,CAAC,CAAC;IACvD,IAAI,EAAE,QAAQ,CAAC;CAChB;AAsED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,QAAQ,EACR,IAAI,GACL,EAAE,wBAAwB,2CA2P1B"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useCallback } from "react";
|
|
3
|
-
import { generateId } from "document-model/core";
|
|
4
3
|
import { activateSubscription, pauseSubscription, resumeSubscription, cancelSubscription, renewExpiringSubscription, } from "../../../document-models/subscription-instance/v1/gen/subscription/creators.js";
|
|
5
4
|
function ConfirmModal({ isOpen, title, message, confirmLabel, confirmVariant = "primary", onConfirm, onCancel, showReasonInput, reason, onReasonChange, }) {
|
|
6
5
|
if (!isOpen)
|
|
@@ -19,32 +18,32 @@ export function SubscriptionActions({ document, dispatch, mode, }) {
|
|
|
19
18
|
// Operator direct actions
|
|
20
19
|
const handleActivate = useCallback(() => {
|
|
21
20
|
dispatch(activateSubscription({
|
|
22
|
-
|
|
21
|
+
activatedSince: new Date().toISOString(),
|
|
23
22
|
}));
|
|
24
23
|
}, [dispatch]);
|
|
25
24
|
const handleOperatorPause = useCallback(() => {
|
|
26
25
|
dispatch(pauseSubscription({
|
|
27
|
-
|
|
26
|
+
pausedSince: new Date().toISOString(),
|
|
28
27
|
}));
|
|
29
28
|
setConfirmAction(null);
|
|
30
29
|
}, [dispatch]);
|
|
31
30
|
const handleOperatorResume = useCallback(() => {
|
|
32
31
|
dispatch(resumeSubscription({
|
|
33
|
-
|
|
32
|
+
timestamp: new Date().toISOString(),
|
|
34
33
|
}));
|
|
35
34
|
setConfirmAction(null);
|
|
36
35
|
}, [dispatch]);
|
|
37
36
|
const handleOperatorCancel = useCallback(() => {
|
|
38
37
|
dispatch(cancelSubscription({
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
cancelledSince: new Date().toISOString(),
|
|
39
|
+
cancellationReason: reason || null,
|
|
41
40
|
}));
|
|
42
41
|
setConfirmAction(null);
|
|
43
42
|
setReason("");
|
|
44
43
|
}, [dispatch, reason]);
|
|
45
44
|
const handleOperatorRenew = useCallback(() => {
|
|
46
45
|
dispatch(renewExpiringSubscription({
|
|
47
|
-
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
48
47
|
}));
|
|
49
48
|
setConfirmAction(null);
|
|
50
49
|
}, [dispatch]);
|
|
@@ -70,12 +69,12 @@ export function SubscriptionActions({ document, dispatch, mode, }) {
|
|
|
70
69
|
handleOperatorCancel,
|
|
71
70
|
handleOperatorRenew,
|
|
72
71
|
]);
|
|
73
|
-
const
|
|
72
|
+
const isPending = state.status === "PENDING";
|
|
74
73
|
const isActive = state.status === "ACTIVE";
|
|
75
74
|
const isPaused = state.status === "PAUSED";
|
|
76
75
|
const isExpiring = state.status === "EXPIRING";
|
|
77
76
|
const isCancelled = state.status === "CANCELLED";
|
|
78
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "si-actions", children: mode === "operator" && (_jsxs("div", { className: "si-actions__buttons", children: [
|
|
77
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "si-actions", children: mode === "operator" && (_jsxs("div", { className: "si-actions__buttons", children: [isPending && (_jsxs("button", { type: "button", className: "si-btn si-btn--sm si-btn--success", onClick: handleActivate, children: [_jsx("svg", { className: "si-btn__icon", viewBox: "0 0 20 20", fill: "currentColor", children: _jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z", clipRule: "evenodd" }) }), "Activate"] })), isActive && (_jsxs("button", { type: "button", className: "si-btn si-btn--sm si-btn--warning", onClick: () => setConfirmAction("pause"), children: [_jsx("svg", { className: "si-btn__icon", viewBox: "0 0 20 20", fill: "currentColor", children: _jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z", clipRule: "evenodd" }) }), "Pause"] })), isPaused && (_jsxs("button", { type: "button", className: "si-btn si-btn--sm si-btn--success", onClick: () => setConfirmAction("resume"), children: [_jsx("svg", { className: "si-btn__icon", viewBox: "0 0 20 20", fill: "currentColor", children: _jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z", clipRule: "evenodd" }) }), "Resume"] })), isExpiring && (_jsxs("button", { type: "button", className: "si-btn si-btn--sm si-btn--primary", onClick: () => setConfirmAction("renew"), children: [_jsx("svg", { className: "si-btn__icon", viewBox: "0 0 20 20", fill: "currentColor", children: _jsx("path", { fillRule: "evenodd", d: "M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z", clipRule: "evenodd" }) }), "Renew"] })), !isCancelled && (_jsxs("button", { type: "button", className: "si-btn si-btn--sm si-btn--danger-ghost", onClick: () => setConfirmAction("cancel"), children: [_jsx("svg", { className: "si-btn__icon", viewBox: "0 0 20 20", fill: "currentColor", children: _jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z", clipRule: "evenodd" }) }), "Cancel"] }))] })) }), _jsx(ConfirmModal, { isOpen: confirmAction === "pause", title: "Pause Subscription", message: "Are you sure you want to pause this subscription? Services will be temporarily suspended.", confirmLabel: "Pause Subscription", confirmVariant: "warning", onConfirm: handleConfirm, onCancel: () => {
|
|
79
78
|
setConfirmAction(null);
|
|
80
79
|
setReason("");
|
|
81
80
|
} }), _jsx(ConfirmModal, { isOpen: confirmAction === "resume", title: "Resume Subscription", message: "Are you sure you want to resume this subscription? Services will be reactivated.", confirmLabel: "Resume Subscription", confirmVariant: "primary", onConfirm: handleConfirm, onCancel: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubscriptionHeader.d.ts","sourceRoot":"","sources":["../../../../editors/subscription-instance-editor/components/SubscriptionHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,MAAM,uEAAuE,CAAC;AAC/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAiD5C,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,4BAA4B,CAAC;IACvC,QAAQ,EAAE,gBAAgB,CAAC,0BAA0B,CAAC,CAAC;IACvD,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,QAAQ,EACR,IAAI,GACL,EAAE,uBAAuB,
|
|
1
|
+
{"version":3,"file":"SubscriptionHeader.d.ts","sourceRoot":"","sources":["../../../../editors/subscription-instance-editor/components/SubscriptionHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,MAAM,uEAAuE,CAAC;AAC/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAiD5C,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,4BAA4B,CAAC;IACvC,QAAQ,EAAE,gBAAgB,CAAC,0BAA0B,CAAC,CAAC;IACvD,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,QAAQ,EACR,IAAI,GACL,EAAE,uBAAuB,2CAkJzB"}
|
|
@@ -40,7 +40,7 @@ export function SubscriptionHeader({ document, dispatch, mode, }) {
|
|
|
40
40
|
year: "numeric",
|
|
41
41
|
});
|
|
42
42
|
};
|
|
43
|
-
return (_jsxs("div", { className: "si-header", children: [_jsxs("div", { className: "si-header__main", children: [_jsxs("div", { className: "si-header__title-row", children: [
|
|
43
|
+
return (_jsxs("div", { className: "si-header", children: [_jsxs("div", { className: "si-header__main", children: [_jsxs("div", { className: "si-header__title-row", children: [_jsxs("div", { className: "si-header__info", children: [state.resource?.thumbnailUrl && (_jsx("img", { src: state.resource.thumbnailUrl, alt: "", className: "si-header__thumbnail" })), _jsxs("div", { children: [_jsx("h1", { className: "si-header__title", children: state.resource?.label || state.tierName || "Subscription" }), state.tierName && state.resource?.label && (_jsxs("p", { className: "si-header__subtitle", children: [state.tierName, " Tier"] }))] })] }), _jsx(StatusBadge, { status: state.status })] }), _jsxs("div", { className: "si-header__meta", children: [mode === "operator" && state.customerName && (_jsxs("div", { className: "si-header__meta-item", children: [_jsx("span", { className: "si-header__meta-label", children: "Customer" }), _jsx("span", { className: "si-header__meta-value", children: state.customerName })] })), state.createdAt && (_jsxs("div", { className: "si-header__meta-item", children: [_jsx("span", { className: "si-header__meta-label", children: "Created" }), _jsx("span", { className: "si-header__meta-value", children: formatDate(state.createdAt) })] })), state.activatedSince && (_jsxs("div", { className: "si-header__meta-item", children: [_jsx("span", { className: "si-header__meta-label", children: "Active Since" }), _jsx("span", { className: "si-header__meta-value", children: formatDate(state.activatedSince) })] })), _jsxs("div", { className: "si-header__meta-item", children: [_jsx("span", { className: "si-header__meta-label", children: "Auto-Renew" }), _jsx("span", { className: "si-header__meta-value", style: {
|
|
44
44
|
color: state.autoRenew
|
|
45
45
|
? "var(--si-emerald-600)"
|
|
46
46
|
: "var(--si-slate-400)",
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import type { SubscriptionInstanceState, ServiceMetric } from "../../../document-models/subscription-instance/v1/gen/schema/types.js";
|
|
1
|
+
import type { SubscriptionInstanceState, ServiceMetric, DiscountInfo } from "../../../document-models/subscription-instance/v1/gen/schema/types.js";
|
|
2
2
|
export declare function formatCurrency(amount: number, currency: string): string;
|
|
3
3
|
export declare function formatDate(date: string | null | undefined): string;
|
|
4
4
|
export declare function formatBillingCycleSuffix(cycle: string | null | undefined): string;
|
|
5
5
|
export declare const BILLING_CYCLE_LABELS: Record<string, string>;
|
|
6
|
-
export declare function formatDiscountBadge(discount:
|
|
7
|
-
discountType: string;
|
|
8
|
-
discountValue: number;
|
|
9
|
-
}): string;
|
|
6
|
+
export declare function formatDiscountBadge(discount: DiscountInfo): string;
|
|
10
7
|
export declare function getDiscountSourceLabel(source: string): string;
|
|
11
8
|
export interface MetricOverage {
|
|
12
9
|
metricId: string;
|
|
@@ -20,13 +17,17 @@ export interface MetricOverage {
|
|
|
20
17
|
unitCostCurrency: string;
|
|
21
18
|
projectedCost: number;
|
|
22
19
|
}
|
|
23
|
-
export declare function computeMetricOverage(metric: ServiceMetric
|
|
20
|
+
export declare function computeMetricOverage(metric: ServiceMetric): MetricOverage | null;
|
|
24
21
|
export interface GroupBillingBreakdown {
|
|
25
22
|
groupId: string;
|
|
26
23
|
groupName: string;
|
|
27
24
|
optional: boolean;
|
|
25
|
+
/** Group-level recurring cost (fixed cost) */
|
|
28
26
|
recurringAmount: number | null;
|
|
29
27
|
recurringCurrency: string;
|
|
28
|
+
recurringCycle: string | null;
|
|
29
|
+
discount: DiscountInfo | null;
|
|
30
|
+
/** Dynamic costs from service metrics */
|
|
30
31
|
metricOverages: MetricOverage[];
|
|
31
32
|
dynamicTotal: number;
|
|
32
33
|
}
|
|
@@ -39,12 +40,19 @@ export interface SetupCostLine {
|
|
|
39
40
|
}
|
|
40
41
|
export interface BillingBreakdown {
|
|
41
42
|
currency: string;
|
|
43
|
+
/** Sum of group recurring costs */
|
|
42
44
|
fixedTotal: number;
|
|
45
|
+
/** Sum of metric overage projections */
|
|
43
46
|
dynamicTotal: number;
|
|
47
|
+
/** fixedTotal + dynamicTotal */
|
|
44
48
|
projectedTotal: number;
|
|
49
|
+
/** Standalone service recurring costs (not in groups) */
|
|
45
50
|
standaloneFixedTotal: number;
|
|
51
|
+
/** Breakdown per service group */
|
|
46
52
|
groupBreakdowns: GroupBillingBreakdown[];
|
|
53
|
+
/** Standalone services with metrics */
|
|
47
54
|
standaloneOverages: MetricOverage[];
|
|
55
|
+
/** All setup costs */
|
|
48
56
|
setupLines: SetupCostLine[];
|
|
49
57
|
setupTotal: number;
|
|
50
58
|
billingCycle: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"billing-utils.d.ts","sourceRoot":"","sources":["../../../../editors/subscription-instance-editor/components/billing-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,aAAa,
|
|
1
|
+
{"version":3,"file":"billing-utils.d.ts","sourceRoot":"","sources":["../../../../editors/subscription-instance-editor/components/billing-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,aAAa,EACb,YAAY,EACb,MAAM,uEAAuE,CAAC;AAI/E,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKvE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAOlE;AAUD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,MAAM,CAGR;AAED,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAMvD,CAAC;AAIF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAKlE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAW7D;AAID,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,aAAa,GACpB,aAAa,GAAG,IAAI,CAuBtB;AAID,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,8CAA8C;IAC9C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,yCAAyC;IACzC,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,yDAAyD;IACzD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kCAAkC;IAClC,eAAe,EAAE,qBAAqB,EAAE,CAAC;IACzC,uCAAuC;IACvC,kBAAkB,EAAE,aAAa,EAAE,CAAC;IACpC,sBAAsB;IACtB,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,yBAAyB,GAC/B,gBAAgB,CAuGlB"}
|
|
@@ -52,48 +52,41 @@ export function getDiscountSourceLabel(source) {
|
|
|
52
52
|
return "Discount";
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
export function computeMetricOverage(metric
|
|
55
|
+
export function computeMetricOverage(metric) {
|
|
56
56
|
const freeLimit = metric.freeLimit ?? metric.limit ?? 0;
|
|
57
57
|
const paidLimit = metric.paidLimit ?? null;
|
|
58
58
|
if (freeLimit <= 0 && paidLimit == null)
|
|
59
59
|
return null;
|
|
60
60
|
if (metric.currentUsage <= freeLimit)
|
|
61
61
|
return null;
|
|
62
|
-
if (metric.unitCost
|
|
62
|
+
if (!metric.unitCost)
|
|
63
63
|
return null;
|
|
64
64
|
const excess = metric.currentUsage - freeLimit;
|
|
65
|
-
const projectedCost = excess * metric.unitCost;
|
|
65
|
+
const projectedCost = excess * metric.unitCost.amount;
|
|
66
66
|
return {
|
|
67
67
|
metricId: metric.id,
|
|
68
68
|
metricName: metric.name,
|
|
69
|
-
unitName: metric.unitName
|
|
69
|
+
unitName: metric.unitName,
|
|
70
70
|
currentUsage: metric.currentUsage,
|
|
71
71
|
freeLimit,
|
|
72
72
|
paidLimit,
|
|
73
73
|
excess,
|
|
74
|
-
unitCostAmount: metric.unitCost,
|
|
75
|
-
unitCostCurrency: currency,
|
|
74
|
+
unitCostAmount: metric.unitCost.amount,
|
|
75
|
+
unitCostCurrency: metric.unitCost.currency,
|
|
76
76
|
projectedCost,
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
export function computeBillingBreakdown(state) {
|
|
80
80
|
const currency = state.globalCurrency || state.tierCurrency || "USD";
|
|
81
81
|
const billingCycle = state.selectedBillingCycle || null;
|
|
82
|
+
// Group breakdowns
|
|
82
83
|
const groupBreakdowns = [];
|
|
83
84
|
const setupLines = [];
|
|
84
|
-
const allGroupedServiceIds = new Set();
|
|
85
|
-
for (const group of state.serviceGroups) {
|
|
86
|
-
for (const svcId of group.services) {
|
|
87
|
-
allGroupedServiceIds.add(svcId);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
85
|
for (const group of state.serviceGroups) {
|
|
91
86
|
const metricOverages = [];
|
|
92
|
-
const
|
|
93
|
-
const groupServices = state.services.filter((svc) => groupServiceIds.has(svc.id));
|
|
94
|
-
for (const svc of groupServices) {
|
|
87
|
+
for (const svc of group.services) {
|
|
95
88
|
for (const metric of svc.metrics) {
|
|
96
|
-
const overage = computeMetricOverage(metric
|
|
89
|
+
const overage = computeMetricOverage(metric);
|
|
97
90
|
if (overage)
|
|
98
91
|
metricOverages.push(overage);
|
|
99
92
|
}
|
|
@@ -105,40 +98,43 @@ export function computeBillingBreakdown(state) {
|
|
|
105
98
|
optional: group.optional,
|
|
106
99
|
recurringAmount: group.recurringCost?.amount ?? null,
|
|
107
100
|
recurringCurrency: group.recurringCost?.currency ?? currency,
|
|
101
|
+
recurringCycle: group.recurringCost?.billingCycle ?? billingCycle,
|
|
102
|
+
discount: group.recurringCost?.discount ?? null,
|
|
108
103
|
metricOverages,
|
|
109
104
|
dynamicTotal,
|
|
110
105
|
});
|
|
106
|
+
// Collect setup costs from group level
|
|
111
107
|
if (group.setupCost) {
|
|
112
108
|
setupLines.push({
|
|
113
109
|
name: group.name,
|
|
114
110
|
amount: group.setupCost.amount,
|
|
115
111
|
currency: group.setupCost.currency,
|
|
116
|
-
paid: !!group.setupCost.
|
|
112
|
+
paid: !!group.setupCost.paymentDate,
|
|
117
113
|
source: "group",
|
|
118
114
|
});
|
|
119
115
|
}
|
|
120
|
-
|
|
116
|
+
// Collect setup costs from services in group
|
|
117
|
+
for (const svc of group.services) {
|
|
121
118
|
if (svc.setupCost) {
|
|
122
119
|
setupLines.push({
|
|
123
120
|
name: `${svc.name || "Service"} (${group.name})`,
|
|
124
121
|
amount: svc.setupCost.amount,
|
|
125
122
|
currency: svc.setupCost.currency,
|
|
126
|
-
paid: !!svc.setupCost.
|
|
123
|
+
paid: !!svc.setupCost.paymentDate,
|
|
127
124
|
source: "service",
|
|
128
125
|
});
|
|
129
126
|
}
|
|
130
127
|
}
|
|
131
128
|
}
|
|
129
|
+
// Standalone services (not in groups)
|
|
132
130
|
const standaloneOverages = [];
|
|
133
131
|
let standaloneFixedTotal = 0;
|
|
134
132
|
for (const svc of state.services) {
|
|
135
|
-
if (allGroupedServiceIds.has(svc.id))
|
|
136
|
-
continue;
|
|
137
133
|
if (svc.recurringCost) {
|
|
138
134
|
standaloneFixedTotal += svc.recurringCost.amount;
|
|
139
135
|
}
|
|
140
136
|
for (const metric of svc.metrics) {
|
|
141
|
-
const overage = computeMetricOverage(metric
|
|
137
|
+
const overage = computeMetricOverage(metric);
|
|
142
138
|
if (overage)
|
|
143
139
|
standaloneOverages.push(overage);
|
|
144
140
|
}
|
|
@@ -147,7 +143,7 @@ export function computeBillingBreakdown(state) {
|
|
|
147
143
|
name: svc.name || "Service",
|
|
148
144
|
amount: svc.setupCost.amount,
|
|
149
145
|
currency: svc.setupCost.currency,
|
|
150
|
-
paid: !!svc.setupCost.
|
|
146
|
+
paid: !!svc.setupCost.paymentDate,
|
|
151
147
|
source: "service",
|
|
152
148
|
});
|
|
153
149
|
}
|