@powerhousedao/contributor-billing 0.1.41 → 0.1.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/dist/document-models/document-models.d.ts.map +1 -1
  2. package/dist/document-models/document-models.js +2 -0
  3. package/dist/document-models/resource-template/gen/document-model.js +25 -25
  4. package/dist/document-models/resource-template/gen/reducer.d.ts +1 -1
  5. package/dist/document-models/resource-template/gen/reducer.d.ts.map +1 -1
  6. package/dist/document-models/resource-template/hooks.d.ts +1 -1
  7. package/dist/document-models/resource-template/hooks.d.ts.map +1 -1
  8. package/dist/document-models/resource-template/module.d.ts +1 -1
  9. package/dist/document-models/resource-template/module.d.ts.map +1 -1
  10. package/dist/document-models/resource-template/reducers/audience-management.d.ts +3 -0
  11. package/dist/document-models/resource-template/reducers/audience-management.d.ts.map +1 -0
  12. package/dist/document-models/resource-template/reducers/audience-management.js +17 -0
  13. package/dist/document-models/resource-template/reducers/facet-preset-management.d.ts +59 -0
  14. package/dist/document-models/resource-template/reducers/facet-preset-management.d.ts.map +1 -0
  15. package/dist/document-models/resource-template/reducers/facet-preset-management.js +52 -0
  16. package/dist/document-models/resource-template/reducers/facet-targeting.d.ts +3 -0
  17. package/dist/document-models/resource-template/reducers/facet-targeting.d.ts.map +1 -0
  18. package/dist/document-models/resource-template/reducers/facet-targeting.js +47 -0
  19. package/dist/document-models/resource-template/reducers/option-group-management.d.ts +3 -0
  20. package/dist/document-models/resource-template/reducers/option-group-management.d.ts.map +1 -0
  21. package/dist/document-models/resource-template/reducers/option-group-management.js +44 -0
  22. package/dist/document-models/resource-template/reducers/service-category-management.d.ts +3 -0
  23. package/dist/document-models/resource-template/reducers/service-category-management.d.ts.map +1 -0
  24. package/dist/document-models/resource-template/reducers/service-category-management.js +10 -0
  25. package/dist/document-models/resource-template/reducers/service-management.d.ts +3 -0
  26. package/dist/document-models/resource-template/reducers/service-management.d.ts.map +1 -0
  27. package/dist/document-models/resource-template/reducers/service-management.js +71 -0
  28. package/dist/document-models/resource-template/reducers/template-management.d.ts +3 -0
  29. package/dist/document-models/resource-template/reducers/template-management.d.ts.map +1 -0
  30. package/dist/document-models/resource-template/reducers/template-management.js +32 -0
  31. package/dist/document-models/resource-template/src/reducers/audience-management.d.ts.map +1 -1
  32. package/dist/document-models/resource-template/src/reducers/audience-management.js +1 -1
  33. package/dist/document-models/resource-template/src/reducers/facet-targeting.d.ts.map +1 -1
  34. package/dist/document-models/resource-template/src/reducers/facet-targeting.js +1 -1
  35. package/dist/document-models/resource-template/src/reducers/option-group-management.d.ts.map +1 -1
  36. package/dist/document-models/resource-template/src/reducers/option-group-management.js +1 -1
  37. package/dist/document-models/resource-template/src/reducers/service-category-management.d.ts.map +1 -1
  38. package/dist/document-models/resource-template/src/reducers/service-category-management.js +1 -1
  39. package/dist/document-models/resource-template/src/reducers/service-management.d.ts.map +1 -1
  40. package/dist/document-models/resource-template/src/reducers/service-management.js +1 -1
  41. package/dist/document-models/resource-template/src/reducers/template-management.d.ts.map +1 -1
  42. package/dist/document-models/resource-template/src/reducers/template-management.js +1 -1
  43. package/dist/document-models/service-offering/gen/document-model.js +5 -5
  44. package/dist/document-models/service-offering/gen/document-schema.d.ts.map +1 -1
  45. package/dist/document-models/service-offering/gen/reducer.d.ts +1 -1
  46. package/dist/document-models/service-offering/gen/reducer.d.ts.map +1 -1
  47. package/dist/document-models/service-offering/gen/schema/types.d.ts +3 -0
  48. package/dist/document-models/service-offering/gen/schema/types.d.ts.map +1 -1
  49. package/dist/document-models/service-offering/gen/schema/zod.d.ts.map +1 -1
  50. package/dist/document-models/service-offering/gen/schema/zod.js +3 -0
  51. package/dist/document-models/service-offering/src/reducers/option-group-management.d.ts.map +1 -1
  52. package/dist/document-models/service-offering/src/reducers/option-group-management.js +11 -8
  53. package/dist/document-models/service-offering/src/reducers/service-management.d.ts.map +1 -1
  54. package/dist/document-models/service-offering/src/reducers/service-management.js +20 -12
  55. package/dist/document-models/service-offering/src/reducers/tier-management.d.ts.map +1 -1
  56. package/dist/document-models/service-offering/src/reducers/tier-management.js +21 -19
  57. package/dist/editors/resource-template-editor/components/FacetTargeting.d.ts +1 -1
  58. package/dist/editors/resource-template-editor/components/FacetTargeting.d.ts.map +1 -1
  59. package/dist/editors/resource-template-editor/components/FacetTargeting.js +526 -19
  60. package/dist/editors/resource-template-editor/components/TemplateInfo.d.ts +1 -1
  61. package/dist/editors/resource-template-editor/components/TemplateInfo.d.ts.map +1 -1
  62. package/dist/editors/resource-template-editor/components/TemplateInfo.js +302 -2
  63. package/dist/editors/resource-template-editor/editor.d.ts.map +1 -1
  64. package/dist/editors/service-offering-editor/components/EditName.js +1 -1
  65. package/dist/editors/service-offering-editor/components/OfferingInfo.d.ts +1 -1
  66. package/dist/editors/service-offering-editor/components/OfferingInfo.d.ts.map +1 -1
  67. package/dist/editors/service-offering-editor/components/OfferingProgress.d.ts +10 -0
  68. package/dist/editors/service-offering-editor/components/OfferingProgress.d.ts.map +1 -0
  69. package/dist/editors/service-offering-editor/components/OfferingProgress.js +240 -0
  70. package/dist/editors/service-offering-editor/components/ResourceTemplateSelector.d.ts +1 -1
  71. package/dist/editors/service-offering-editor/components/ResourceTemplateSelector.d.ts.map +1 -1
  72. package/dist/editors/service-offering-editor/components/ResourceTemplateSelector.js +111 -4
  73. package/dist/editors/service-offering-editor/components/ScopeAndFacets.d.ts +1 -1
  74. package/dist/editors/service-offering-editor/components/ScopeAndFacets.d.ts.map +1 -1
  75. package/dist/editors/service-offering-editor/components/ServiceCatalog.d.ts +1 -1
  76. package/dist/editors/service-offering-editor/components/ServiceCatalog.d.ts.map +1 -1
  77. package/dist/editors/service-offering-editor/components/ServiceCatalog.js +318 -1
  78. package/dist/editors/service-offering-editor/components/ServicesList.d.ts +1 -1
  79. package/dist/editors/service-offering-editor/components/ServicesList.d.ts.map +1 -1
  80. package/dist/editors/service-offering-editor/components/ServicesList.js +1 -1
  81. package/dist/editors/service-offering-editor/components/TheMatrix.d.ts +1 -1
  82. package/dist/editors/service-offering-editor/components/TheMatrix.d.ts.map +1 -1
  83. package/dist/editors/service-offering-editor/components/TheMatrix.js +548 -13
  84. package/dist/editors/service-offering-editor/components/TierDefinition.d.ts +1 -1
  85. package/dist/editors/service-offering-editor/components/TierDefinition.d.ts.map +1 -1
  86. package/dist/editors/service-offering-editor/components/TierDefinition.js +406 -4
  87. package/dist/editors/service-offering-editor/components/TiersList.d.ts +1 -1
  88. package/dist/editors/service-offering-editor/components/TiersList.d.ts.map +1 -1
  89. package/dist/editors/service-offering-editor/editor.d.ts.map +1 -1
  90. package/dist/editors/service-offering-editor/editor.js +2 -2
  91. package/dist/powerhouse.manifest.json +18 -0
  92. package/dist/subgraphs/index.d.ts +1 -0
  93. package/dist/subgraphs/index.d.ts.map +1 -1
  94. package/dist/subgraphs/index.js +1 -0
  95. package/dist/subgraphs/resource-template/index.d.ts +11 -0
  96. package/dist/subgraphs/resource-template/index.d.ts.map +1 -0
  97. package/dist/subgraphs/resource-template/index.js +11 -0
  98. package/dist/subgraphs/resource-template/resolvers.d.ts +3 -0
  99. package/dist/subgraphs/resource-template/resolvers.d.ts.map +1 -0
  100. package/dist/subgraphs/resource-template/resolvers.js +312 -0
  101. package/dist/subgraphs/resource-template/schema.d.ts +3 -0
  102. package/dist/subgraphs/resource-template/schema.d.ts.map +1 -0
  103. package/dist/subgraphs/resource-template/schema.js +262 -0
  104. package/dist/subgraphs/service-offering/resolvers.d.ts.map +1 -1
  105. package/dist/subgraphs/service-offering/resolvers.js +24 -0
  106. package/dist/subgraphs/service-offering/schema.d.ts.map +1 -1
  107. package/dist/subgraphs/service-offering/schema.js +21 -0
  108. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState, useMemo, useEffect, useRef, useCallback } from "react";
3
3
  import { generateId } from "document-model/core";
4
- import { addServiceLevel, updateServiceLevel, addUsageLimit, updateUsageLimit, removeUsageLimit, addService, } from "../../../document-models/service-offering/gen/creators.js";
4
+ import { addServiceLevel, updateServiceLevel, addUsageLimit, updateUsageLimit, removeUsageLimit, addService, updateService, } from "../../../document-models/service-offering/gen/creators.js";
5
5
  const SERVICE_LEVELS = [
6
6
  {
7
7
  value: "INCLUDED",
@@ -437,6 +437,319 @@ const matrixStyles = `
437
437
  color: var(--so-slate-700);
438
438
  }
439
439
 
440
+ /* Premium Only Badge - Exclusivity Signal (Toggle Button) */
441
+ .matrix__premium-badge {
442
+ display: inline-flex;
443
+ align-items: center;
444
+ gap: 0.25rem;
445
+ padding: 0.125rem 0.5rem;
446
+ margin-left: 0.5rem;
447
+ font-size: 0.5625rem;
448
+ font-weight: 700;
449
+ text-transform: uppercase;
450
+ letter-spacing: 0.04em;
451
+ border-radius: 100px;
452
+ cursor: pointer;
453
+ transition: all var(--so-transition-fast);
454
+ font-family: var(--so-font-sans);
455
+ }
456
+
457
+ .matrix__premium-badge--active {
458
+ color: var(--so-amber-700);
459
+ background: linear-gradient(135deg, var(--so-amber-100) 0%, var(--so-amber-50) 100%);
460
+ border: 1px solid var(--so-amber-200);
461
+ }
462
+
463
+ .matrix__premium-badge--active:hover {
464
+ background: linear-gradient(135deg, var(--so-amber-200) 0%, var(--so-amber-100) 100%);
465
+ border-color: var(--so-amber-300);
466
+ }
467
+
468
+ .matrix__premium-badge--inactive {
469
+ color: var(--so-slate-400);
470
+ background: var(--so-slate-50);
471
+ border: 1px dashed var(--so-slate-200);
472
+ opacity: 0.6;
473
+ }
474
+
475
+ .matrix__premium-badge--inactive:hover {
476
+ opacity: 1;
477
+ color: var(--so-amber-600);
478
+ background: var(--so-amber-50);
479
+ border: 1px dashed var(--so-amber-300);
480
+ }
481
+
482
+ .matrix__premium-badge--active svg {
483
+ width: 0.625rem;
484
+ height: 0.625rem;
485
+ fill: var(--so-amber-500);
486
+ stroke: var(--so-amber-600);
487
+ }
488
+
489
+ .matrix__premium-badge--inactive svg {
490
+ width: 0.625rem;
491
+ height: 0.625rem;
492
+ fill: none;
493
+ stroke: currentColor;
494
+ }
495
+
496
+ /* Incomplete Services Warning */
497
+ .matrix__incomplete-warning {
498
+ display: flex;
499
+ align-items: flex-start;
500
+ gap: 0.875rem;
501
+ padding: 1rem 1.25rem;
502
+ margin-bottom: 1rem;
503
+ background: var(--so-amber-50);
504
+ border: 1px solid var(--so-amber-200);
505
+ border-radius: var(--so-radius-md);
506
+ animation: matrix-warning-pulse 2s ease-in-out infinite;
507
+ }
508
+
509
+ @keyframes matrix-warning-pulse {
510
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0); }
511
+ 50% { box-shadow: 0 0 0 4px rgba(245, 158, 11, 0.1); }
512
+ }
513
+
514
+ .matrix__incomplete-icon {
515
+ flex-shrink: 0;
516
+ width: 1.5rem;
517
+ height: 1.5rem;
518
+ color: var(--so-amber-600);
519
+ }
520
+
521
+ .matrix__incomplete-icon svg {
522
+ width: 100%;
523
+ height: 100%;
524
+ }
525
+
526
+ .matrix__incomplete-content {
527
+ display: flex;
528
+ flex-direction: column;
529
+ gap: 0.25rem;
530
+ }
531
+
532
+ .matrix__incomplete-title {
533
+ font-size: 0.875rem;
534
+ font-weight: 600;
535
+ color: var(--so-amber-800);
536
+ }
537
+
538
+ .matrix__incomplete-text {
539
+ font-size: 0.8125rem;
540
+ color: var(--so-amber-700);
541
+ line-height: 1.5;
542
+ }
543
+
544
+ .matrix__incomplete-text strong {
545
+ font-weight: 600;
546
+ color: var(--so-amber-900);
547
+ }
548
+
549
+ /* Bulk Actions Toolbar */
550
+ .matrix__bulk-actions {
551
+ margin-bottom: 1rem;
552
+ }
553
+
554
+ .matrix__bulk-toggle {
555
+ display: flex;
556
+ align-items: center;
557
+ gap: 0.5rem;
558
+ padding: 0.625rem 1rem;
559
+ font-family: var(--so-font-sans);
560
+ font-size: 0.8125rem;
561
+ font-weight: 600;
562
+ color: var(--so-slate-600);
563
+ background: var(--so-slate-100);
564
+ border: 1px solid var(--so-slate-200);
565
+ border-radius: var(--so-radius-md);
566
+ cursor: pointer;
567
+ transition: all var(--so-transition-fast);
568
+ }
569
+
570
+ .matrix__bulk-toggle:hover {
571
+ background: var(--so-slate-200);
572
+ color: var(--so-slate-700);
573
+ }
574
+
575
+ .matrix__bulk-toggle svg:first-child {
576
+ width: 1rem;
577
+ height: 1rem;
578
+ }
579
+
580
+ .matrix__bulk-toggle-arrow {
581
+ width: 0.875rem;
582
+ height: 0.875rem;
583
+ margin-left: auto;
584
+ transition: transform var(--so-transition-fast);
585
+ }
586
+
587
+ .matrix__bulk-toggle-arrow--open {
588
+ transform: rotate(180deg);
589
+ }
590
+
591
+ .matrix__bulk-panel {
592
+ margin-top: 0.75rem;
593
+ padding: 1rem;
594
+ background: var(--so-slate-50);
595
+ border: 1px solid var(--so-slate-200);
596
+ border-radius: var(--so-radius-md);
597
+ display: flex;
598
+ flex-direction: column;
599
+ gap: 1rem;
600
+ animation: so-scale-in var(--so-transition-fast) ease-out;
601
+ }
602
+
603
+ .matrix__bulk-section {
604
+ display: flex;
605
+ flex-direction: column;
606
+ gap: 0.5rem;
607
+ }
608
+
609
+ .matrix__bulk-label {
610
+ font-size: 0.6875rem;
611
+ font-weight: 600;
612
+ text-transform: uppercase;
613
+ letter-spacing: 0.05em;
614
+ color: var(--so-slate-500);
615
+ }
616
+
617
+ .matrix__bulk-buttons,
618
+ .matrix__bulk-copy {
619
+ display: flex;
620
+ flex-wrap: wrap;
621
+ gap: 0.5rem;
622
+ }
623
+
624
+ .matrix__bulk-btn {
625
+ display: flex;
626
+ align-items: center;
627
+ gap: 0.375rem;
628
+ padding: 0.5rem 0.875rem;
629
+ font-family: var(--so-font-sans);
630
+ font-size: 0.75rem;
631
+ font-weight: 500;
632
+ border-radius: var(--so-radius-sm);
633
+ border: 1px solid;
634
+ cursor: pointer;
635
+ transition: all var(--so-transition-fast);
636
+ }
637
+
638
+ .matrix__bulk-btn svg {
639
+ width: 0.875rem;
640
+ height: 0.875rem;
641
+ }
642
+
643
+ .matrix__bulk-btn--include {
644
+ background: var(--so-emerald-50);
645
+ border-color: var(--so-emerald-200);
646
+ color: var(--so-emerald-700);
647
+ }
648
+
649
+ .matrix__bulk-btn--include:hover {
650
+ background: var(--so-emerald-100);
651
+ border-color: var(--so-emerald-300);
652
+ }
653
+
654
+ .matrix__bulk-btn--clear {
655
+ background: var(--so-rose-50);
656
+ border-color: var(--so-rose-200);
657
+ color: var(--so-rose-700);
658
+ }
659
+
660
+ .matrix__bulk-btn--clear:hover {
661
+ background: var(--so-rose-100);
662
+ border-color: var(--so-rose-300);
663
+ }
664
+
665
+ .matrix__bulk-btn--copy {
666
+ background: var(--so-sky-50);
667
+ border-color: var(--so-sky-200);
668
+ color: var(--so-sky-700);
669
+ }
670
+
671
+ .matrix__bulk-btn--copy:hover {
672
+ background: var(--so-sky-100);
673
+ border-color: var(--so-sky-300);
674
+ }
675
+
676
+ /* Pattern Presets Section */
677
+ .matrix__bulk-patterns {
678
+ border-top: 1px dashed var(--so-slate-200);
679
+ padding-top: 1rem;
680
+ margin-top: 0.5rem;
681
+ }
682
+
683
+ .matrix__bulk-label-icon {
684
+ width: 1rem;
685
+ height: 1rem;
686
+ vertical-align: middle;
687
+ margin-right: 0.25rem;
688
+ }
689
+
690
+ .matrix__pattern-grid {
691
+ display: grid;
692
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
693
+ gap: 0.75rem;
694
+ margin-top: 0.5rem;
695
+ }
696
+
697
+ .matrix__pattern-btn {
698
+ display: flex;
699
+ flex-direction: column;
700
+ align-items: center;
701
+ gap: 0.25rem;
702
+ padding: 0.875rem 0.75rem;
703
+ background: linear-gradient(135deg, var(--so-white) 0%, var(--so-slate-50) 100%);
704
+ border: 1px solid var(--so-slate-200);
705
+ border-radius: var(--so-radius-md);
706
+ cursor: pointer;
707
+ transition: all var(--so-transition-fast);
708
+ text-align: center;
709
+ }
710
+
711
+ .matrix__pattern-btn:hover {
712
+ transform: translateY(-2px);
713
+ box-shadow: var(--so-shadow-md);
714
+ border-color: var(--so-violet-300);
715
+ background: linear-gradient(135deg, var(--so-violet-50) 0%, var(--so-white) 100%);
716
+ }
717
+
718
+ .matrix__pattern-btn:active {
719
+ transform: translateY(0);
720
+ }
721
+
722
+ .matrix__pattern-icon {
723
+ font-size: 1.5rem;
724
+ line-height: 1;
725
+ }
726
+
727
+ .matrix__pattern-name {
728
+ font-size: 0.8125rem;
729
+ font-weight: 600;
730
+ color: var(--so-slate-700);
731
+ }
732
+
733
+ .matrix__pattern-desc {
734
+ font-size: 0.6875rem;
735
+ color: var(--so-slate-500);
736
+ line-height: 1.3;
737
+ }
738
+
739
+ .matrix__pattern-btn--simple {
740
+ padding: 0.625rem 0.75rem;
741
+ flex-direction: row;
742
+ gap: 0.5rem;
743
+ }
744
+
745
+ .matrix__pattern-btn--simple .matrix__pattern-icon {
746
+ font-size: 1rem;
747
+ }
748
+
749
+ .matrix__pattern-btn--simple .matrix__pattern-name {
750
+ font-size: 0.75rem;
751
+ }
752
+
440
753
  .matrix__level-cell {
441
754
  padding: 0.625rem 1rem;
442
755
  text-align: center;
@@ -462,6 +775,41 @@ const matrixStyles = `
462
775
  font-weight: 500;
463
776
  }
464
777
 
778
+ /* Loss Aversion Styling for NOT_INCLUDED */
779
+ .matrix__level-cell--not-included {
780
+ position: relative;
781
+ background: repeating-linear-gradient(
782
+ 135deg,
783
+ transparent,
784
+ transparent 8px,
785
+ rgba(148, 163, 184, 0.08) 8px,
786
+ rgba(148, 163, 184, 0.08) 16px
787
+ );
788
+ }
789
+
790
+ .matrix__level-cell--not-included:hover .matrix__upgrade-hint {
791
+ opacity: 1;
792
+ transform: translateY(0);
793
+ }
794
+
795
+ .matrix__upgrade-hint {
796
+ position: absolute;
797
+ bottom: 2px;
798
+ left: 50%;
799
+ transform: translateX(-50%) translateY(4px);
800
+ font-size: 0.5625rem;
801
+ font-weight: 500;
802
+ color: var(--so-violet-600);
803
+ white-space: nowrap;
804
+ opacity: 0;
805
+ transition: all 0.15s ease-out;
806
+ pointer-events: none;
807
+ }
808
+
809
+ .matrix__level-value--not-included {
810
+ opacity: 0.6;
811
+ }
812
+
465
813
  /* Metric Row */
466
814
  .matrix__metric-row {
467
815
  background: inherit;
@@ -1358,6 +1706,8 @@ export function TheMatrix({ document, dispatch, groupSetupFees = {}, }) {
1358
1706
  const [metricName, setMetricName] = useState("");
1359
1707
  const [metricLimits, setMetricLimits] = useState({});
1360
1708
  const [metricEnabledTiers, setMetricEnabledTiers] = useState(new Set());
1709
+ // Bulk actions state
1710
+ const [showBulkActions, setShowBulkActions] = useState(false);
1361
1711
  const getServiceGroup = (service) => {
1362
1712
  // Services now have optionGroupId directly on them
1363
1713
  return service.optionGroupId || null;
@@ -1404,6 +1754,16 @@ export function TheMatrix({ document, dispatch, groupSetupFees = {}, }) {
1404
1754
  });
1405
1755
  return Array.from(metricsSet);
1406
1756
  };
1757
+ // Incomplete services detection - services not assigned to any tier
1758
+ const incompleteServices = useMemo(() => {
1759
+ if (tiers.length === 0)
1760
+ return [];
1761
+ return services.filter((service) => {
1762
+ // Check if service is included in at least one tier
1763
+ const isIncludedAnywhere = tiers.some((tier) => tier.serviceLevels.some((sl) => sl.serviceId === service.id && sl.level === "INCLUDED"));
1764
+ return !isIncludedAnywhere;
1765
+ });
1766
+ }, [services, tiers]);
1407
1767
  const getUsageLimitForMetric = (serviceId, metric, tier) => {
1408
1768
  return tier.usageLimits.find((ul) => ul.serviceId === serviceId && ul.metric === metric);
1409
1769
  };
@@ -1439,6 +1799,150 @@ export function TheMatrix({ document, dispatch, groupSetupFees = {}, }) {
1439
1799
  return next;
1440
1800
  });
1441
1801
  };
1802
+ // Bulk action handlers
1803
+ const handleBulkIncludeAllInTier = (tierId) => {
1804
+ const now = new Date().toISOString();
1805
+ const tier = tiers.find((t) => t.id === tierId);
1806
+ if (!tier)
1807
+ return;
1808
+ services.forEach((service) => {
1809
+ const existingLevel = tier.serviceLevels.find((sl) => sl.serviceId === service.id);
1810
+ if (!existingLevel) {
1811
+ dispatch(addServiceLevel({
1812
+ tierId,
1813
+ serviceLevelId: generateId(),
1814
+ serviceId: service.id,
1815
+ level: "INCLUDED",
1816
+ optionGroupId: service.optionGroupId || undefined,
1817
+ lastModified: now,
1818
+ }));
1819
+ }
1820
+ else if (existingLevel.level !== "INCLUDED") {
1821
+ dispatch(updateServiceLevel({
1822
+ tierId,
1823
+ serviceLevelId: existingLevel.id,
1824
+ level: "INCLUDED",
1825
+ lastModified: now,
1826
+ }));
1827
+ }
1828
+ });
1829
+ };
1830
+ const handleBulkClearTier = (tierId) => {
1831
+ const now = new Date().toISOString();
1832
+ const tier = tiers.find((t) => t.id === tierId);
1833
+ if (!tier)
1834
+ return;
1835
+ tier.serviceLevels.forEach((sl) => {
1836
+ dispatch(updateServiceLevel({
1837
+ tierId,
1838
+ serviceLevelId: sl.id,
1839
+ level: "NOT_INCLUDED",
1840
+ lastModified: now,
1841
+ }));
1842
+ });
1843
+ };
1844
+ const handleBulkCopyFromTier = (sourceTierId, targetTierId) => {
1845
+ const now = new Date().toISOString();
1846
+ const sourceTier = tiers.find((t) => t.id === sourceTierId);
1847
+ const targetTier = tiers.find((t) => t.id === targetTierId);
1848
+ if (!sourceTier || !targetTier)
1849
+ return;
1850
+ services.forEach((service) => {
1851
+ const sourceLevel = sourceTier.serviceLevels.find((sl) => sl.serviceId === service.id);
1852
+ const targetLevel = targetTier.serviceLevels.find((sl) => sl.serviceId === service.id);
1853
+ const newLevel = sourceLevel?.level || "NOT_INCLUDED";
1854
+ if (!targetLevel) {
1855
+ dispatch(addServiceLevel({
1856
+ tierId: targetTierId,
1857
+ serviceLevelId: generateId(),
1858
+ serviceId: service.id,
1859
+ level: newLevel,
1860
+ optionGroupId: service.optionGroupId || undefined,
1861
+ lastModified: now,
1862
+ }));
1863
+ }
1864
+ else if (targetLevel.level !== newLevel) {
1865
+ dispatch(updateServiceLevel({
1866
+ tierId: targetTierId,
1867
+ serviceLevelId: targetLevel.id,
1868
+ level: newLevel,
1869
+ lastModified: now,
1870
+ }));
1871
+ }
1872
+ });
1873
+ };
1874
+ // Common Pattern Presets - apply marketing psychology patterns across all tiers
1875
+ const handleApplyPattern = (patternId) => {
1876
+ const now = new Date().toISOString();
1877
+ const tierCount = tiers.length;
1878
+ if (tierCount === 0)
1879
+ return;
1880
+ // Pattern definitions
1881
+ const patterns = {
1882
+ // Good-Better-Best: Progressive inclusion (50% → 75% → 100%)
1883
+ "good-better-best": (serviceIdx, tierIdx, totalServices, totalTiers) => {
1884
+ const servicePosition = serviceIdx / totalServices;
1885
+ const tierPosition = tierIdx / (totalTiers - 1 || 1);
1886
+ // Lower tiers get fewer services
1887
+ const threshold = 0.5 + tierPosition * 0.5;
1888
+ return servicePosition < threshold ? "INCLUDED" : "NOT_INCLUDED";
1889
+ },
1890
+ // Premium Only: Top services only in top tier
1891
+ "premium-only": (serviceIdx, tierIdx, totalServices, totalTiers) => {
1892
+ const isTopTier = tierIdx === totalTiers - 1;
1893
+ const isPremiumService = serviceIdx < Math.ceil(totalServices * 0.3);
1894
+ if (isPremiumService) {
1895
+ return isTopTier ? "INCLUDED" : "NOT_INCLUDED";
1896
+ }
1897
+ return "INCLUDED";
1898
+ },
1899
+ // Core + Upgrades: Core included everywhere, extras are optional
1900
+ "core-upgrades": (serviceIdx, tierIdx, totalServices) => {
1901
+ const isCoreService = serviceIdx < Math.ceil(totalServices * 0.5);
1902
+ if (isCoreService) {
1903
+ return "INCLUDED";
1904
+ }
1905
+ return "OPTIONAL";
1906
+ },
1907
+ // Ascending: Each tier adds more services
1908
+ ascending: (serviceIdx, tierIdx, totalServices, totalTiers) => {
1909
+ const servicesPerTier = Math.ceil(totalServices / totalTiers);
1910
+ const includedUpTo = (tierIdx + 1) * servicesPerTier;
1911
+ return serviceIdx < includedUpTo ? "INCLUDED" : "NOT_INCLUDED";
1912
+ },
1913
+ // All Included: Everything included in all tiers
1914
+ "all-included": () => "INCLUDED",
1915
+ // All Optional: Everything optional in all tiers
1916
+ "all-optional": () => "OPTIONAL",
1917
+ };
1918
+ const pattern = patterns[patternId];
1919
+ if (!pattern)
1920
+ return;
1921
+ services.forEach((service, serviceIdx) => {
1922
+ tiers.forEach((tier, tierIdx) => {
1923
+ const newLevel = pattern(serviceIdx, tierIdx, services.length, tierCount);
1924
+ const existingLevel = tier.serviceLevels.find((sl) => sl.serviceId === service.id);
1925
+ if (!existingLevel) {
1926
+ dispatch(addServiceLevel({
1927
+ tierId: tier.id,
1928
+ serviceLevelId: generateId(),
1929
+ serviceId: service.id,
1930
+ level: newLevel,
1931
+ optionGroupId: service.optionGroupId || undefined,
1932
+ lastModified: now,
1933
+ }));
1934
+ }
1935
+ else if (existingLevel.level !== newLevel) {
1936
+ dispatch(updateServiceLevel({
1937
+ tierId: tier.id,
1938
+ serviceLevelId: existingLevel.id,
1939
+ level: newLevel,
1940
+ lastModified: now,
1941
+ }));
1942
+ }
1943
+ });
1944
+ });
1945
+ };
1442
1946
  const handleAddService = () => {
1443
1947
  if (!addServiceModal || !newServiceName.trim())
1444
1948
  return;
@@ -1525,6 +2029,16 @@ export function TheMatrix({ document, dispatch, groupSetupFees = {}, }) {
1525
2029
  }
1526
2030
  });
1527
2031
  };
2032
+ const handleTogglePremiumExclusive = (serviceId) => {
2033
+ const service = services.find((s) => s.id === serviceId);
2034
+ if (!service)
2035
+ return;
2036
+ dispatch(updateService({
2037
+ id: serviceId,
2038
+ isPremiumExclusive: !service.isPremiumExclusive,
2039
+ lastModified: new Date().toISOString(),
2040
+ }));
2041
+ };
1528
2042
  const handleSaveMetric = () => {
1529
2043
  if (!metricModal || !metricName.trim())
1530
2044
  return;
@@ -1608,27 +2122,33 @@ export function TheMatrix({ document, dispatch, groupSetupFees = {}, }) {
1608
2122
  : ""}`, children: option.label }, option.id))) })) : (_jsx("select", { value: selectedFacets[key] || "", onChange: (e) => setSelectedFacets((prev) => ({
1609
2123
  ...prev,
1610
2124
  [key]: e.target.value,
1611
- })), className: "matrix__facet-select", children: category.options.map((option) => (_jsx("option", { value: option.id, children: option.label }, option.id))) }))] }, key))) }), selectedFacets.ANONYMITY === "highest-anonymity" && (_jsx("div", { className: "matrix__facet-notice", children: _jsxs("p", { className: "matrix__facet-notice-text", children: [_jsx("strong", { children: "Highest Anonymity:" }), " Additional setup services may be required for enhanced privacy configurations."] }) }))] }), _jsx("div", { className: "matrix__table-wrap", children: _jsxs("table", { className: "matrix__table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { className: "matrix__corner-cell" }), tiers.map((tier, idx) => (_jsx("th", { onClick: () => setSelectedTierIdx(idx), className: `matrix__tier-header ${idx === selectedTierIdx
2125
+ })), className: "matrix__facet-select", children: category.options.map((option) => (_jsx("option", { value: option.id, children: option.label }, option.id))) }))] }, key))) }), selectedFacets.ANONYMITY === "highest-anonymity" && (_jsx("div", { className: "matrix__facet-notice", children: _jsxs("p", { className: "matrix__facet-notice-text", children: [_jsx("strong", { children: "Highest Anonymity:" }), " Additional setup services may be required for enhanced privacy configurations."] }) }))] }), services.length > 0 && tiers.length > 0 && (_jsxs("div", { className: "matrix__bulk-actions", children: [_jsxs("button", { type: "button", onClick: () => setShowBulkActions(!showBulkActions), className: "matrix__bulk-toggle", children: [_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M4 6h16M4 12h16M4 18h7" }) }), "Bulk Actions", _jsx("svg", { className: `matrix__bulk-toggle-arrow ${showBulkActions ? "matrix__bulk-toggle-arrow--open" : ""}`, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M19 9l-7 7-7-7" }) })] }), showBulkActions && (_jsxs("div", { className: "matrix__bulk-panel", children: [_jsxs("div", { className: "matrix__bulk-section", children: [_jsx("span", { className: "matrix__bulk-label", children: "Include all services in:" }), _jsx("div", { className: "matrix__bulk-buttons", children: tiers.map((tier) => (_jsxs("button", { type: "button", onClick: () => handleBulkIncludeAllInTier(tier.id), className: "matrix__bulk-btn matrix__bulk-btn--include", children: [_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M5 12l5 5L20 7" }) }), tier.name] }, tier.id))) })] }), _jsxs("div", { className: "matrix__bulk-section", children: [_jsx("span", { className: "matrix__bulk-label", children: "Clear all from:" }), _jsx("div", { className: "matrix__bulk-buttons", children: tiers.map((tier) => (_jsxs("button", { type: "button", onClick: () => handleBulkClearTier(tier.id), className: "matrix__bulk-btn matrix__bulk-btn--clear", children: [_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M6 18L18 6M6 6l12 12" }) }), tier.name] }, tier.id))) })] }), tiers.length >= 2 && (_jsxs("div", { className: "matrix__bulk-section", children: [_jsx("span", { className: "matrix__bulk-label", children: "Copy configuration:" }), _jsx("div", { className: "matrix__bulk-copy", children: tiers.map((sourceTier, idx) => tiers
2126
+ .filter((_, i) => i !== idx)
2127
+ .map((targetTier) => (_jsxs("button", { type: "button", onClick: () => handleBulkCopyFromTier(sourceTier.id, targetTier.id), className: "matrix__bulk-btn matrix__bulk-btn--copy", children: [sourceTier.name, " \u2192 ", targetTier.name] }, `${sourceTier.id}-${targetTier.id}`)))) })] })), _jsxs("div", { className: "matrix__bulk-section matrix__bulk-patterns", children: [_jsxs("span", { className: "matrix__bulk-label", children: [_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", className: "matrix__bulk-label-icon", children: _jsx("path", { d: "M13 10V3L4 14h7v7l9-11h-7z" }) }), "Apply Pattern Preset:"] }), _jsxs("div", { className: "matrix__pattern-grid", children: [_jsxs("button", { type: "button", onClick: () => handleApplyPattern("good-better-best"), className: "matrix__pattern-btn", title: "Progressive inclusion: Basic tier gets 50%, mid-tier 75%, top tier 100%", children: [_jsx("span", { className: "matrix__pattern-icon", children: "\uD83D\uDCC8" }), _jsx("span", { className: "matrix__pattern-name", children: "Good-Better-Best" }), _jsx("span", { className: "matrix__pattern-desc", children: "Progressive inclusion" })] }), _jsxs("button", { type: "button", onClick: () => handleApplyPattern("premium-only"), className: "matrix__pattern-btn", title: "Top 30% services exclusive to premium tier, rest included everywhere", children: [_jsx("span", { className: "matrix__pattern-icon", children: "\u2B50" }), _jsx("span", { className: "matrix__pattern-name", children: "Premium Exclusives" }), _jsx("span", { className: "matrix__pattern-desc", children: "Top services in top tier only" })] }), _jsxs("button", { type: "button", onClick: () => handleApplyPattern("core-upgrades"), className: "matrix__pattern-btn", title: "Core services included, extras as optional add-ons", children: [_jsx("span", { className: "matrix__pattern-icon", children: "\uD83C\uDFAF" }), _jsx("span", { className: "matrix__pattern-name", children: "Core + Add-ons" }), _jsx("span", { className: "matrix__pattern-desc", children: "Half included, half optional" })] }), _jsxs("button", { type: "button", onClick: () => handleApplyPattern("ascending"), className: "matrix__pattern-btn", title: "Each tier unlocks more services progressively", children: [_jsx("span", { className: "matrix__pattern-icon", children: "\uD83E\uDE9C" }), _jsx("span", { className: "matrix__pattern-name", children: "Ascending Tiers" }), _jsx("span", { className: "matrix__pattern-desc", children: "Each tier unlocks more" })] }), _jsxs("button", { type: "button", onClick: () => handleApplyPattern("all-included"), className: "matrix__pattern-btn matrix__pattern-btn--simple", title: "Everything included in all tiers", children: [_jsx("span", { className: "matrix__pattern-icon", children: "\u2713" }), _jsx("span", { className: "matrix__pattern-name", children: "All Included" })] }), _jsxs("button", { type: "button", onClick: () => handleApplyPattern("all-optional"), className: "matrix__pattern-btn matrix__pattern-btn--simple", title: "Everything optional in all tiers", children: [_jsx("span", { className: "matrix__pattern-icon", children: "\u25D0" }), _jsx("span", { className: "matrix__pattern-name", children: "All Optional" })] })] })] })] }))] })), incompleteServices.length > 0 && (_jsxs("div", { className: "matrix__incomplete-warning", children: [_jsx("div", { className: "matrix__incomplete-icon", children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }) }), _jsxs("div", { className: "matrix__incomplete-content", children: [_jsxs("span", { className: "matrix__incomplete-title", children: [incompleteServices.length, " service", incompleteServices.length !== 1 ? "s" : "", " not configured"] }), _jsxs("span", { className: "matrix__incomplete-text", children: ["The following services are not included in any tier:", " ", _jsx("strong", { children: incompleteServices
2128
+ .slice(0, 3)
2129
+ .map((s) => s.title)
2130
+ .join(", ") }), incompleteServices.length > 3 &&
2131
+ ` and ${incompleteServices.length - 3} more`] })] })] })), _jsx("div", { className: "matrix__table-wrap", children: _jsxs("table", { className: "matrix__table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { className: "matrix__corner-cell" }), tiers.map((tier, idx) => (_jsx("th", { onClick: () => setSelectedTierIdx(idx), className: `matrix__tier-header ${idx === selectedTierIdx
1612
2132
  ? "matrix__tier-header--selected"
1613
2133
  : ""}`, children: _jsxs("div", { className: "matrix__tier-header-inner", children: [_jsx("div", { className: "matrix__tier-radio" }), _jsx("span", { className: "matrix__tier-name", children: tier.name }), _jsx("span", { className: "matrix__tier-price", children: tier.isCustomPricing
1614
2134
  ? "Custom"
1615
2135
  : `$${tier.pricing.amount}/mo` })] }) }, tier.id)))] }) }), _jsxs("tbody", { children: [_jsx("tr", { children: _jsx("td", { colSpan: tiers.length + 1, className: "matrix__section-header", children: "Service Catalog" }) }), (setupGroups.length > 0 ||
1616
- ungroupedSetupServices.length > 0) && (_jsx("tr", { children: _jsxs("td", { colSpan: tiers.length + 1, className: "matrix__category-header", children: [_jsx("span", { className: "matrix__category-icon", children: _jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", children: [_jsx("path", { d: "M19 21V5a2 2 0 0 0-2-2H7a2 2 0 0 0-2 2v16" }), _jsx("path", { d: "M9 21v-6h6v6" }), _jsx("path", { d: "M9 7h.01M9 11h.01M15 7h.01M15 11h.01" })] }) }), "Setup & Formation"] }) })), setupGroups.map((group) => (_jsx(ServiceGroupSection, { group: group, services: groupedServices.get(group.id) || [], tiers: tiers, isSetupFormation: true, isOptional: false, isEnabled: true, onToggle: () => { }, getServiceLevelForTier: getServiceLevelForTier, getUniqueMetricsForService: getUniqueMetricsForService, getUsageLimitForMetric: getUsageLimitForMetric, getLevelDisplay: getLevelDisplay, selectedCell: selectedCell, setSelectedCell: setSelectedCell, handleSetServiceLevel: handleSetServiceLevel, dispatch: dispatch, setupFee: groupSetupFees[group.id], onAddService: openAddServiceModal, selectedTierIdx: selectedTierIdx, onAddMetric: handleAddMetric, onEditMetric: handleEditMetric, onRemoveMetric: handleRemoveMetric }, group.id))), ungroupedSetupServices.length > 0 && (_jsx(ServiceGroupSection, { group: {
2136
+ ungroupedSetupServices.length > 0) && (_jsx("tr", { children: _jsxs("td", { colSpan: tiers.length + 1, className: "matrix__category-header", children: [_jsx("span", { className: "matrix__category-icon", children: _jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", children: [_jsx("path", { d: "M19 21V5a2 2 0 0 0-2-2H7a2 2 0 0 0-2 2v16" }), _jsx("path", { d: "M9 21v-6h6v6" }), _jsx("path", { d: "M9 7h.01M9 11h.01M15 7h.01M15 11h.01" })] }) }), "Setup & Formation"] }) })), setupGroups.map((group) => (_jsx(ServiceGroupSection, { group: group, services: groupedServices.get(group.id) || [], tiers: tiers, isSetupFormation: true, isOptional: false, isEnabled: true, onToggle: () => { }, getServiceLevelForTier: getServiceLevelForTier, getUniqueMetricsForService: getUniqueMetricsForService, getUsageLimitForMetric: getUsageLimitForMetric, getLevelDisplay: getLevelDisplay, selectedCell: selectedCell, setSelectedCell: setSelectedCell, handleSetServiceLevel: handleSetServiceLevel, dispatch: dispatch, setupFee: groupSetupFees[group.id], onAddService: openAddServiceModal, selectedTierIdx: selectedTierIdx, onAddMetric: handleAddMetric, onEditMetric: handleEditMetric, onRemoveMetric: handleRemoveMetric, onTogglePremiumExclusive: handleTogglePremiumExclusive }, group.id))), ungroupedSetupServices.length > 0 && (_jsx(ServiceGroupSection, { group: {
1617
2137
  id: UNGROUPED_ID,
1618
2138
  name: "Setup & Formation",
1619
2139
  description: null,
1620
2140
  isAddOn: false,
1621
2141
  defaultSelected: true,
1622
- }, services: ungroupedSetupServices, tiers: tiers, isSetupFormation: true, isOptional: false, isEnabled: true, onToggle: () => { }, getServiceLevelForTier: getServiceLevelForTier, getUniqueMetricsForService: getUniqueMetricsForService, getUsageLimitForMetric: getUsageLimitForMetric, getLevelDisplay: getLevelDisplay, selectedCell: selectedCell, setSelectedCell: setSelectedCell, handleSetServiceLevel: handleSetServiceLevel, dispatch: dispatch, setupFee: groupSetupFees[UNGROUPED_ID], selectedTierIdx: selectedTierIdx, onAddMetric: handleAddMetric, onEditMetric: handleEditMetric, onRemoveMetric: handleRemoveMetric }, "ungrouped-setup")), (regularGroups.length > 0 ||
1623
- ungroupedRegularServices.length > 0) && (_jsx("tr", { children: _jsxs("td", { colSpan: tiers.length + 1, className: "matrix__category-header", children: [_jsx("span", { className: "matrix__category-icon", children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", children: _jsx("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }) }), "Recurring Services"] }) })), regularGroups.map((group) => (_jsx(ServiceGroupSection, { group: group, services: groupedServices.get(group.id) || [], tiers: tiers, isSetupFormation: false, isOptional: false, isEnabled: true, onToggle: () => { }, getServiceLevelForTier: getServiceLevelForTier, getUniqueMetricsForService: getUniqueMetricsForService, getUsageLimitForMetric: getUsageLimitForMetric, getLevelDisplay: getLevelDisplay, selectedCell: selectedCell, setSelectedCell: setSelectedCell, handleSetServiceLevel: handleSetServiceLevel, onAddService: openAddServiceModal, selectedTierIdx: selectedTierIdx, dispatch: dispatch, onAddMetric: handleAddMetric, onEditMetric: handleEditMetric, onRemoveMetric: handleRemoveMetric }, group.id))), ungroupedRegularServices.length > 0 && (_jsx(ServiceGroupSection, { group: {
2142
+ }, services: ungroupedSetupServices, tiers: tiers, isSetupFormation: true, isOptional: false, isEnabled: true, onToggle: () => { }, getServiceLevelForTier: getServiceLevelForTier, getUniqueMetricsForService: getUniqueMetricsForService, getUsageLimitForMetric: getUsageLimitForMetric, getLevelDisplay: getLevelDisplay, selectedCell: selectedCell, setSelectedCell: setSelectedCell, handleSetServiceLevel: handleSetServiceLevel, dispatch: dispatch, setupFee: groupSetupFees[UNGROUPED_ID], selectedTierIdx: selectedTierIdx, onAddMetric: handleAddMetric, onEditMetric: handleEditMetric, onRemoveMetric: handleRemoveMetric, onTogglePremiumExclusive: handleTogglePremiumExclusive }, "ungrouped-setup")), (regularGroups.length > 0 ||
2143
+ ungroupedRegularServices.length > 0) && (_jsx("tr", { children: _jsxs("td", { colSpan: tiers.length + 1, className: "matrix__category-header", children: [_jsx("span", { className: "matrix__category-icon", children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", children: _jsx("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }) }), "Recurring Services"] }) })), regularGroups.map((group) => (_jsx(ServiceGroupSection, { group: group, services: groupedServices.get(group.id) || [], tiers: tiers, isSetupFormation: false, isOptional: false, isEnabled: true, onToggle: () => { }, getServiceLevelForTier: getServiceLevelForTier, getUniqueMetricsForService: getUniqueMetricsForService, getUsageLimitForMetric: getUsageLimitForMetric, getLevelDisplay: getLevelDisplay, selectedCell: selectedCell, setSelectedCell: setSelectedCell, handleSetServiceLevel: handleSetServiceLevel, onAddService: openAddServiceModal, selectedTierIdx: selectedTierIdx, dispatch: dispatch, onAddMetric: handleAddMetric, onEditMetric: handleEditMetric, onRemoveMetric: handleRemoveMetric, onTogglePremiumExclusive: handleTogglePremiumExclusive }, group.id))), ungroupedRegularServices.length > 0 && (_jsx(ServiceGroupSection, { group: {
1624
2144
  id: UNGROUPED_ID,
1625
2145
  name: "Recurring Services",
1626
2146
  description: null,
1627
2147
  isAddOn: false,
1628
2148
  defaultSelected: true,
1629
- }, services: ungroupedRegularServices, tiers: tiers, isSetupFormation: false, isOptional: false, isEnabled: true, onToggle: () => { }, getServiceLevelForTier: getServiceLevelForTier, getUniqueMetricsForService: getUniqueMetricsForService, getUsageLimitForMetric: getUsageLimitForMetric, getLevelDisplay: getLevelDisplay, selectedCell: selectedCell, setSelectedCell: setSelectedCell, handleSetServiceLevel: handleSetServiceLevel, dispatch: dispatch, selectedTierIdx: selectedTierIdx, onAddMetric: handleAddMetric, onEditMetric: handleEditMetric, onRemoveMetric: handleRemoveMetric }, "ungrouped-regular")), _jsxs("tr", { className: "matrix__total-row", children: [_jsx("td", { children: "SUBTOTAL" }), tiers.map((tier) => (_jsx("td", { style: { textAlign: "center" }, children: tier.isCustomPricing
2149
+ }, services: ungroupedRegularServices, tiers: tiers, isSetupFormation: false, isOptional: false, isEnabled: true, onToggle: () => { }, getServiceLevelForTier: getServiceLevelForTier, getUniqueMetricsForService: getUniqueMetricsForService, getUsageLimitForMetric: getUsageLimitForMetric, getLevelDisplay: getLevelDisplay, selectedCell: selectedCell, setSelectedCell: setSelectedCell, handleSetServiceLevel: handleSetServiceLevel, dispatch: dispatch, selectedTierIdx: selectedTierIdx, onAddMetric: handleAddMetric, onEditMetric: handleEditMetric, onRemoveMetric: handleRemoveMetric, onTogglePremiumExclusive: handleTogglePremiumExclusive }, "ungrouped-regular")), _jsxs("tr", { className: "matrix__total-row", children: [_jsx("td", { children: "SUBTOTAL" }), tiers.map((tier) => (_jsx("td", { style: { textAlign: "center" }, children: tier.isCustomPricing
1630
2150
  ? "Custom"
1631
- : `$${tier.pricing.amount}` }, tier.id)))] }), addonGroups.map((group) => (_jsx(ServiceGroupSection, { group: group, services: groupedServices.get(group.id) || [], tiers: tiers, isSetupFormation: false, isOptional: true, isEnabled: enabledOptionalGroups.has(group.id), onToggle: () => toggleOptionalGroup(group.id), getServiceLevelForTier: getServiceLevelForTier, getUniqueMetricsForService: getUniqueMetricsForService, getUsageLimitForMetric: getUsageLimitForMetric, getLevelDisplay: getLevelDisplay, selectedCell: selectedCell, setSelectedCell: setSelectedCell, handleSetServiceLevel: handleSetServiceLevel, dispatch: dispatch, onAddService: openAddServiceModal, selectedTierIdx: selectedTierIdx, onAddMetric: handleAddMetric, onEditMetric: handleEditMetric, onRemoveMetric: handleRemoveMetric }, group.id))), _jsxs("tr", { className: "matrix__grand-total-row", children: [_jsx("td", { children: "Grand Total (Recurring)" }), tiers.map((tier, idx) => {
2151
+ : `$${tier.pricing.amount}` }, tier.id)))] }), addonGroups.map((group) => (_jsx(ServiceGroupSection, { group: group, services: groupedServices.get(group.id) || [], tiers: tiers, isSetupFormation: false, isOptional: true, isEnabled: enabledOptionalGroups.has(group.id), onToggle: () => toggleOptionalGroup(group.id), getServiceLevelForTier: getServiceLevelForTier, getUniqueMetricsForService: getUniqueMetricsForService, getUsageLimitForMetric: getUsageLimitForMetric, getLevelDisplay: getLevelDisplay, selectedCell: selectedCell, setSelectedCell: setSelectedCell, handleSetServiceLevel: handleSetServiceLevel, dispatch: dispatch, onAddService: openAddServiceModal, selectedTierIdx: selectedTierIdx, onAddMetric: handleAddMetric, onEditMetric: handleEditMetric, onRemoveMetric: handleRemoveMetric, onTogglePremiumExclusive: handleTogglePremiumExclusive }, group.id))), _jsxs("tr", { className: "matrix__grand-total-row", children: [_jsx("td", { children: "Grand Total (Recurring)" }), tiers.map((tier, idx) => {
1632
2152
  const tierPrice = tier.pricing.amount || 0;
1633
2153
  const grandTotal = tierPrice;
1634
2154
  return (_jsx("td", { className: idx === selectedTierIdx
@@ -1716,7 +2236,7 @@ export function TheMatrix({ document, dispatch, groupSetupFees = {}, }) {
1716
2236
  setMetricEnabledTiers(new Set());
1717
2237
  }, className: "matrix__modal-btn matrix__modal-btn--cancel", children: "Cancel" }), _jsx("button", { onClick: handleSaveMetric, disabled: !metricName.trim() || metricEnabledTiers.size === 0, className: "matrix__modal-btn matrix__modal-btn--primary", children: metricModal.metric ? "Save Changes" : "Add Metric" })] })] }) }))] })] }));
1718
2238
  }
1719
- function ServiceGroupSection({ group, services, tiers, isSetupFormation, isOptional, isEnabled, onToggle, getServiceLevelForTier, getUniqueMetricsForService, getUsageLimitForMetric, getLevelDisplay, selectedCell, setSelectedCell, setupFee, onAddService, selectedTierIdx, onAddMetric, onEditMetric, onRemoveMetric, }) {
2239
+ function ServiceGroupSection({ group, services, tiers, isSetupFormation, isOptional, isEnabled, onToggle, getServiceLevelForTier, getUniqueMetricsForService, getUsageLimitForMetric, getLevelDisplay, selectedCell, setSelectedCell, setupFee, onAddService, selectedTierIdx, onAddMetric, onEditMetric, onRemoveMetric, onTogglePremiumExclusive, }) {
1720
2240
  const showGroup = services.length > 0 || onAddService;
1721
2241
  if (!showGroup)
1722
2242
  return null;
@@ -1738,7 +2258,7 @@ function ServiceGroupSection({ group, services, tiers, isSetupFormation, isOptio
1738
2258
  ? "OPTIONAL"
1739
2259
  : "INCLUDED" }) })] }), services.map((service) => {
1740
2260
  const metrics = getUniqueMetricsForService(service.id);
1741
- return (_jsx(ServiceRowWithMetrics, { service: service, metrics: metrics, tiers: tiers, rowClass: rowClass, getServiceLevelForTier: getServiceLevelForTier, getUsageLimitForMetric: getUsageLimitForMetric, getLevelDisplay: getLevelDisplay, selectedCell: selectedCell, setSelectedCell: setSelectedCell, selectedTierIdx: selectedTierIdx, onAddMetric: onAddMetric, onEditMetric: onEditMetric, onRemoveMetric: onRemoveMetric }, service.id));
2261
+ return (_jsx(ServiceRowWithMetrics, { service: service, metrics: metrics, tiers: tiers, rowClass: rowClass, getServiceLevelForTier: getServiceLevelForTier, getUsageLimitForMetric: getUsageLimitForMetric, getLevelDisplay: getLevelDisplay, selectedCell: selectedCell, setSelectedCell: setSelectedCell, selectedTierIdx: selectedTierIdx, onAddMetric: onAddMetric, onEditMetric: onEditMetric, onRemoveMetric: onRemoveMetric, onTogglePremiumExclusive: onTogglePremiumExclusive }, service.id));
1742
2262
  }), onAddService && group.id !== "__ungrouped__" && (_jsxs("tr", { className: `matrix__add-service-row ${rowClass}`, children: [_jsx("td", { className: rowClass, children: _jsxs("button", { onClick: () => onAddService(group.id, isSetupFormation), className: "matrix__add-service-btn", children: [_jsx("svg", { className: "matrix__add-service-icon", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 4v16m8-8H4" }) }), "+ Add a Service"] }) }), _jsx("td", { colSpan: tiers.length, className: rowClass })] })), isSetupFormation && (_jsxs("tr", { className: "matrix__setup-total-row", children: [_jsx("td", { children: "TOTAL SETUP FEE" }), _jsx("td", { colSpan: tiers.length, style: { textAlign: "center" }, children: setupFee
1743
2263
  ? `$${setupFee} flat fee (applied to all tiers)`
1744
2264
  : "No setup fee configured" })] })), isOptional && (_jsxs("tr", { className: `matrix__total-row ${headerClass}`, children: [_jsx("td", { className: headerClass, children: "SUBTOTAL" }), tiers.map((tier, tierIdx) => {
@@ -1754,8 +2274,15 @@ function ServiceGroupSection({ group, services, tiers, isSetupFormation, isOptio
1754
2274
  }, children: isEnabled ? priceDisplay : "$0" }, tier.id));
1755
2275
  })] }))] }));
1756
2276
  }
1757
- function ServiceRowWithMetrics({ service, metrics, tiers, rowClass, getServiceLevelForTier, getUsageLimitForMetric, getLevelDisplay, selectedCell, setSelectedCell, selectedTierIdx, onAddMetric, onEditMetric, onRemoveMetric, }) {
1758
- return (_jsxs(_Fragment, { children: [_jsxs("tr", { className: `matrix__service-row ${rowClass}`, children: [_jsx("td", { className: `matrix__service-cell ${rowClass}`, children: _jsxs("div", { className: "matrix__service-cell-wrapper", children: [_jsx("span", { className: "matrix__service-title", children: service.title }), _jsx("button", { onClick: (e) => {
2277
+ function ServiceRowWithMetrics({ service, metrics, tiers, rowClass, getServiceLevelForTier, getUsageLimitForMetric, getLevelDisplay, selectedCell, setSelectedCell, selectedTierIdx, onAddMetric, onEditMetric, onRemoveMetric, onTogglePremiumExclusive, }) {
2278
+ // Use persisted isPremiumExclusive field from document state
2279
+ const isPremiumExclusive = service.isPremiumExclusive;
2280
+ return (_jsxs(_Fragment, { children: [_jsxs("tr", { className: `matrix__service-row ${rowClass}`, children: [_jsx("td", { className: `matrix__service-cell ${rowClass}`, children: _jsxs("div", { className: "matrix__service-cell-wrapper", children: [_jsx("span", { className: "matrix__service-title", children: service.title }), _jsxs("button", { onClick: (e) => {
2281
+ e.stopPropagation();
2282
+ onTogglePremiumExclusive(service.id);
2283
+ }, className: `matrix__premium-badge ${isPremiumExclusive ? "matrix__premium-badge--active" : "matrix__premium-badge--inactive"}`, title: isPremiumExclusive
2284
+ ? "Click to remove premium exclusive status"
2285
+ : "Click to mark as premium exclusive", children: [_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" }) }), "Premium"] }), _jsx("button", { onClick: (e) => {
1759
2286
  e.stopPropagation();
1760
2287
  onAddMetric(service.id);
1761
2288
  }, className: "matrix__add-metric-btn", title: "Add metric to this service", children: "+ Metric" })] }) }), tiers.map((tier, tierIdx) => {
@@ -1763,9 +2290,17 @@ function ServiceRowWithMetrics({ service, metrics, tiers, rowClass, getServiceLe
1763
2290
  const display = getLevelDisplay(serviceLevel);
1764
2291
  const isSelected = selectedCell?.serviceId === service.id &&
1765
2292
  selectedCell?.tierId === tier.id;
1766
- return (_jsx("td", { className: `matrix__level-cell ${isSelected ? "matrix__level-cell--selected" : ""} ${tierIdx === selectedTierIdx ? "matrix__level-cell--highlight" : ""}`, onClick: () => setSelectedCell(isSelected
2293
+ const isNotIncluded = !serviceLevel || serviceLevel.level === "NOT_INCLUDED";
2294
+ // Find next tier that has this service included (for upgrade hint)
2295
+ const nextTierWithService = isNotIncluded
2296
+ ? tiers.slice(tierIdx + 1).find((t) => {
2297
+ const sl = getServiceLevelForTier(service.id, t);
2298
+ return sl && sl.level === "INCLUDED";
2299
+ })
2300
+ : null;
2301
+ return (_jsxs("td", { className: `matrix__level-cell ${isSelected ? "matrix__level-cell--selected" : ""} ${tierIdx === selectedTierIdx ? "matrix__level-cell--highlight" : ""} ${isNotIncluded ? "matrix__level-cell--not-included" : ""}`, onClick: () => setSelectedCell(isSelected
1767
2302
  ? null
1768
- : { serviceId: service.id, tierId: tier.id }), children: _jsx("span", { className: "matrix__level-value", style: { color: display.color }, children: display.label }) }, tier.id));
2303
+ : { serviceId: service.id, tierId: tier.id }), children: [_jsx("span", { className: `matrix__level-value ${isNotIncluded ? "matrix__level-value--not-included" : ""}`, style: { color: display.color }, children: display.label }), isNotIncluded && nextTierWithService && (_jsxs("span", { className: "matrix__upgrade-hint", children: ["In ", nextTierWithService.name, " \u2192"] }))] }, tier.id));
1769
2304
  })] }), metrics.map((metric) => (_jsxs("tr", { className: `matrix__metric-row ${rowClass}`, onClick: () => onEditMetric(service.id, metric), children: [_jsx("td", { className: `matrix__metric-cell ${rowClass}`, children: _jsxs("div", { className: "matrix__metric-name-wrapper", children: [_jsx("span", { className: "matrix__metric-name", children: metric }), _jsxs("div", { className: "matrix__metric-actions", children: [_jsx("button", { onClick: (e) => {
1770
2305
  e.stopPropagation();
1771
2306
  onEditMetric(service.id, metric);