@flamingo-stack/openframe-frontend-core 0.0.668 → 0.0.669-snapshot.20260927145656

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 (148) hide show
  1. package/dist/{chunk-B5UP36KY.cjs → chunk-22E6CSIS.cjs} +12 -12
  2. package/dist/{chunk-B5UP36KY.cjs.map → chunk-22E6CSIS.cjs.map} +1 -1
  3. package/dist/{chunk-6KJV6GAJ.js → chunk-5IP7FYUN.js} +4 -4
  4. package/dist/{chunk-GF6WJ4MG.js → chunk-5Q6BE56A.js} +6 -6
  5. package/dist/{chunk-NZD2WTJ2.cjs → chunk-5ZVYPAFQ.cjs} +3 -3
  6. package/dist/{chunk-NZD2WTJ2.cjs.map → chunk-5ZVYPAFQ.cjs.map} +1 -1
  7. package/dist/{chunk-Z3HUP4G7.cjs → chunk-635CAJ36.cjs} +32 -32
  8. package/dist/{chunk-Z3HUP4G7.cjs.map → chunk-635CAJ36.cjs.map} +1 -1
  9. package/dist/{chunk-GO63IT6K.js → chunk-6P335PRA.js} +5 -5
  10. package/dist/{chunk-5V3LERVC.js → chunk-6X66J23J.js} +4 -4
  11. package/dist/{chunk-6W4LUHMY.cjs → chunk-7VZZ5S5V.cjs} +206 -206
  12. package/dist/{chunk-6W4LUHMY.cjs.map → chunk-7VZZ5S5V.cjs.map} +1 -1
  13. package/dist/{chunk-EV2KJ4D2.cjs → chunk-7WK544AC.cjs} +7 -7
  14. package/dist/{chunk-EV2KJ4D2.cjs.map → chunk-7WK544AC.cjs.map} +1 -1
  15. package/dist/{chunk-FOH4MNOZ.js → chunk-C5CTLTKJ.js} +3 -3
  16. package/dist/{chunk-TFVUM6KK.cjs → chunk-CK4BZRVJ.cjs} +10 -10
  17. package/dist/{chunk-TFVUM6KK.cjs.map → chunk-CK4BZRVJ.cjs.map} +1 -1
  18. package/dist/{chunk-Z3GN3MIW.cjs → chunk-D2HYPVYQ.cjs} +81 -81
  19. package/dist/{chunk-Z3GN3MIW.cjs.map → chunk-D2HYPVYQ.cjs.map} +1 -1
  20. package/dist/{chunk-WVCDX7FE.cjs → chunk-DAMA3A5Z.cjs} +15 -15
  21. package/dist/{chunk-WVCDX7FE.cjs.map → chunk-DAMA3A5Z.cjs.map} +1 -1
  22. package/dist/{chunk-P6IJXAEE.js → chunk-DLJGCDHX.js} +2 -1
  23. package/dist/{chunk-P6IJXAEE.js.map → chunk-DLJGCDHX.js.map} +1 -1
  24. package/dist/{chunk-IJPV7OMA.js → chunk-EHB5LETA.js} +591 -466
  25. package/dist/chunk-EHB5LETA.js.map +1 -0
  26. package/dist/{chunk-CFUUVLYQ.cjs → chunk-GXKKWFZR.cjs} +33 -33
  27. package/dist/{chunk-CFUUVLYQ.cjs.map → chunk-GXKKWFZR.cjs.map} +1 -1
  28. package/dist/{chunk-IZ6F7LOE.js → chunk-IHOSMLBC.js} +3 -3
  29. package/dist/{chunk-DK3BHDUZ.js → chunk-INIITSC3.js} +3 -3
  30. package/dist/{chunk-ZHRIFUN3.cjs → chunk-L5TTJXLU.cjs} +28 -28
  31. package/dist/{chunk-ZHRIFUN3.cjs.map → chunk-L5TTJXLU.cjs.map} +1 -1
  32. package/dist/{chunk-7UW2BBFI.cjs → chunk-LGIBMKRM.cjs} +59 -59
  33. package/dist/{chunk-7UW2BBFI.cjs.map → chunk-LGIBMKRM.cjs.map} +1 -1
  34. package/dist/{chunk-IBQT4RTS.js → chunk-O3PPPILJ.js} +3 -3
  35. package/dist/{chunk-CWK4QHVF.js → chunk-OABFQXAI.js} +3 -3
  36. package/dist/{chunk-VEQWH5KH.cjs → chunk-OHLOPLOM.cjs} +30 -30
  37. package/dist/{chunk-VEQWH5KH.cjs.map → chunk-OHLOPLOM.cjs.map} +1 -1
  38. package/dist/{chunk-PHLBRU5P.js → chunk-P7FKI2UW.js} +3 -3
  39. package/dist/{chunk-YH3BUB4W.js → chunk-PTZKMV35.js} +5 -5
  40. package/dist/{chunk-E5HUHKHH.js → chunk-QGHMHW7R.js} +7 -7
  41. package/dist/{chunk-VJ4XBTR5.cjs → chunk-QH6PMRGF.cjs} +22 -22
  42. package/dist/{chunk-VJ4XBTR5.cjs.map → chunk-QH6PMRGF.cjs.map} +1 -1
  43. package/dist/{chunk-37NYF4W2.cjs → chunk-QRUW4UXB.cjs} +2 -1
  44. package/dist/chunk-QRUW4UXB.cjs.map +1 -0
  45. package/dist/{chunk-LJOELK7P.js → chunk-RG7XYHN7.js} +2 -2
  46. package/dist/{chunk-Y7SFGCF4.cjs → chunk-RRJ6EL63.cjs} +20 -20
  47. package/dist/{chunk-Y7SFGCF4.cjs.map → chunk-RRJ6EL63.cjs.map} +1 -1
  48. package/dist/{chunk-FJNX67W2.cjs → chunk-SEW23M5X.cjs} +5 -5
  49. package/dist/{chunk-FJNX67W2.cjs.map → chunk-SEW23M5X.cjs.map} +1 -1
  50. package/dist/{chunk-K4WYWTQV.cjs → chunk-TLMPNPYS.cjs} +6 -6
  51. package/dist/{chunk-K4WYWTQV.cjs.map → chunk-TLMPNPYS.cjs.map} +1 -1
  52. package/dist/{chunk-SHGMDYCR.js → chunk-VEAA5C5X.js} +2 -2
  53. package/dist/{chunk-5CC33IJ7.js → chunk-VXRWR56S.js} +6 -6
  54. package/dist/{chunk-NNYND3I4.js → chunk-XUHLWFEM.js} +3 -3
  55. package/dist/{chunk-QHL4GYYY.cjs → chunk-ZYFDPD73.cjs} +577 -452
  56. package/dist/chunk-ZYFDPD73.cjs.map +1 -0
  57. package/dist/components/case-studies/index.cjs +16 -16
  58. package/dist/components/case-studies/index.js +4 -4
  59. package/dist/components/chat/entity-cards/design-doc-card.d.ts +23 -0
  60. package/dist/components/chat/entity-cards/design-doc-card.d.ts.map +1 -0
  61. package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
  62. package/dist/components/chat/entity-cards/index.d.ts +1 -0
  63. package/dist/components/chat/entity-cards/index.d.ts.map +1 -1
  64. package/dist/components/chat/index.cjs +11 -3
  65. package/dist/components/chat/index.cjs.map +1 -1
  66. package/dist/components/chat/index.js +10 -2
  67. package/dist/components/contact/index.cjs +5 -5
  68. package/dist/components/contact/index.js +4 -4
  69. package/dist/components/docs/index.cjs +8 -8
  70. package/dist/components/docs/index.js +7 -7
  71. package/dist/components/embeds/index.cjs +5 -5
  72. package/dist/components/embeds/index.js +4 -4
  73. package/dist/components/faq/index.cjs +5 -5
  74. package/dist/components/faq/index.js +4 -4
  75. package/dist/components/features/index.cjs +4 -4
  76. package/dist/components/features/index.js +3 -3
  77. package/dist/components/help-center-pages/index.cjs +197 -89
  78. package/dist/components/help-center-pages/index.cjs.map +1 -1
  79. package/dist/components/help-center-pages/index.d.ts +1 -1
  80. package/dist/components/help-center-pages/index.d.ts.map +1 -1
  81. package/dist/components/help-center-pages/index.js +143 -35
  82. package/dist/components/help-center-pages/index.js.map +1 -1
  83. package/dist/components/help-center-pages/trust-center-page.d.ts +6 -0
  84. package/dist/components/help-center-pages/trust-center-page.d.ts.map +1 -1
  85. package/dist/components/help-center-pages/trust-center-sections.d.ts +17 -2
  86. package/dist/components/help-center-pages/trust-center-sections.d.ts.map +1 -1
  87. package/dist/components/index.cjs +62 -54
  88. package/dist/components/index.cjs.map +1 -1
  89. package/dist/components/index.js +23 -15
  90. package/dist/components/index.js.map +1 -1
  91. package/dist/components/meeting-scheduler/index.cjs +40 -40
  92. package/dist/components/meeting-scheduler/index.js +3 -3
  93. package/dist/components/navigation/index.cjs +7 -7
  94. package/dist/components/navigation/index.js +6 -6
  95. package/dist/components/onboarding-guides/index.cjs +7 -7
  96. package/dist/components/onboarding-guides/index.js +6 -6
  97. package/dist/components/related-content/index.cjs +3 -3
  98. package/dist/components/related-content/index.js +2 -2
  99. package/dist/components/tickets/index.cjs +8 -8
  100. package/dist/components/tickets/index.js +7 -7
  101. package/dist/components/ui/index.cjs +12 -4
  102. package/dist/components/ui/index.cjs.map +1 -1
  103. package/dist/components/ui/index.js +11 -3
  104. package/dist/index.cjs +27 -9
  105. package/dist/index.cjs.map +1 -1
  106. package/dist/index.js +25 -7
  107. package/dist/index.js.map +1 -1
  108. package/dist/utils/design-doc-readiness.d.ts +20 -0
  109. package/dist/utils/design-doc-readiness.d.ts.map +1 -0
  110. package/dist/utils/index.cjs +28 -3
  111. package/dist/utils/index.cjs.map +1 -1
  112. package/dist/utils/index.d.ts +1 -0
  113. package/dist/utils/index.d.ts.map +1 -1
  114. package/dist/utils/index.js +24 -4
  115. package/dist/utils/index.js.map +1 -1
  116. package/dist/utils/list-url.d.ts.map +1 -1
  117. package/dist/utils/source-icons.d.ts.map +1 -1
  118. package/package.json +1 -1
  119. package/src/components/chat/entity-cards/design-doc-card.tsx +114 -0
  120. package/src/components/chat/entity-cards/dispatch.tsx +62 -1
  121. package/src/components/chat/entity-cards/index.ts +7 -0
  122. package/src/components/help-center-pages/__tests__/trust-center-page.test.tsx +28 -1
  123. package/src/components/help-center-pages/index.ts +1 -1
  124. package/src/components/help-center-pages/trust-center-page.tsx +59 -16
  125. package/src/components/help-center-pages/trust-center-sections.tsx +159 -8
  126. package/src/utils/design-doc-readiness.ts +34 -0
  127. package/src/utils/index.ts +1 -0
  128. package/src/utils/list-url.ts +1 -0
  129. package/src/utils/source-icons.ts +3 -0
  130. package/dist/chunk-37NYF4W2.cjs.map +0 -1
  131. package/dist/chunk-IJPV7OMA.js.map +0 -1
  132. package/dist/chunk-QHL4GYYY.cjs.map +0 -1
  133. /package/dist/{chunk-6KJV6GAJ.js.map → chunk-5IP7FYUN.js.map} +0 -0
  134. /package/dist/{chunk-GF6WJ4MG.js.map → chunk-5Q6BE56A.js.map} +0 -0
  135. /package/dist/{chunk-GO63IT6K.js.map → chunk-6P335PRA.js.map} +0 -0
  136. /package/dist/{chunk-5V3LERVC.js.map → chunk-6X66J23J.js.map} +0 -0
  137. /package/dist/{chunk-FOH4MNOZ.js.map → chunk-C5CTLTKJ.js.map} +0 -0
  138. /package/dist/{chunk-IZ6F7LOE.js.map → chunk-IHOSMLBC.js.map} +0 -0
  139. /package/dist/{chunk-DK3BHDUZ.js.map → chunk-INIITSC3.js.map} +0 -0
  140. /package/dist/{chunk-IBQT4RTS.js.map → chunk-O3PPPILJ.js.map} +0 -0
  141. /package/dist/{chunk-CWK4QHVF.js.map → chunk-OABFQXAI.js.map} +0 -0
  142. /package/dist/{chunk-PHLBRU5P.js.map → chunk-P7FKI2UW.js.map} +0 -0
  143. /package/dist/{chunk-YH3BUB4W.js.map → chunk-PTZKMV35.js.map} +0 -0
  144. /package/dist/{chunk-E5HUHKHH.js.map → chunk-QGHMHW7R.js.map} +0 -0
  145. /package/dist/{chunk-LJOELK7P.js.map → chunk-RG7XYHN7.js.map} +0 -0
  146. /package/dist/{chunk-SHGMDYCR.js.map → chunk-VEAA5C5X.js.map} +0 -0
  147. /package/dist/{chunk-5CC33IJ7.js.map → chunk-VXRWR56S.js.map} +0 -0
  148. /package/dist/{chunk-NNYND3I4.js.map → chunk-XUHLWFEM.js.map} +0 -0
@@ -19,6 +19,7 @@ import { useDebounce } from '../../hooks/ui/use-debounce';
19
19
  import { useSelfFetch } from '../../hooks/use-self-fetch';
20
20
  import {
21
21
  TRUST_CENTER_SEARCH_DEBOUNCE_MS,
22
+ TRUST_CENTER_SECTIONS,
22
23
  normalizeTrustControlQuery,
23
24
  trustCenterControlsUrl,
24
25
  trustFrameworkBadge,
@@ -28,6 +29,7 @@ import {
28
29
  type TrustCenterControlsPage,
29
30
  type TrustCenterDocument,
30
31
  type TrustCenterFramework,
32
+ type TrustCenterSectionId,
31
33
  type TrustCenterSubprocessor,
32
34
  type TrustFrameworkMonitoringEntry,
33
35
  } from '../../types/trust-center';
@@ -43,8 +45,7 @@ import { ShieldCheckIcon } from '../icons-v2-generated/security/shield-check-ico
43
45
  import { CheckIcon } from '../icons-v2-generated/signs-and-symbols/check-icon';
44
46
  import { SECTION_HEADING_CLASS } from '../layout/page-heading';
45
47
  import { ListEmptyState } from '../list-empty-state';
46
- import { CardSkeletonGrid } from '../loading/card-skeleton';
47
- import { TextSkeleton } from '../loading/unified-skeleton';
48
+ import { TextSkeleton, UnifiedSkeleton } from '../loading/unified-skeleton';
48
49
  import { Button } from '../ui/button/button';
49
50
  import { ComplianceLogo } from '../ui/compliance-badge';
50
51
  import { EntityImage } from '../ui/entity-image';
@@ -62,8 +63,27 @@ const FRAMEWORK_ICONS: Record<TrustFrameworkMonitoringEntry['icon'], typeof Shie
62
63
  'file-shield': FileShieldIcon,
63
64
  };
64
65
 
65
- const STACK_CLASSES = 'flex flex-col gap-[var(--spacing-system-l)]';
66
66
  const BODY_TEXT = 'text-h6 text-ods-text-secondary';
67
+
68
+ /**
69
+ * The page body's layout, ONE copy for the loaded page and its skeleton, so the
70
+ * skeleton cannot drift from what replaces it: the status row, then the sections
71
+ * beside the section rail.
72
+ */
73
+ export const TRUST_STATUS_ROW_CLASS =
74
+ 'flex flex-col gap-[var(--spacing-system-xs)] md:flex-row md:items-center md:justify-between';
75
+ export const TRUST_BODY_GRID_CLASS =
76
+ 'grid grid-cols-1 gap-[var(--spacing-system-xl)] lg:grid-cols-[minmax(0,1fr)_12rem]';
77
+ export const TRUST_SECTIONS_COLUMN_CLASS = 'flex min-w-0 flex-col gap-[var(--spacing-system-xxl)]';
78
+
79
+ /** Each section's one-line lead under its heading (the page and its skeleton read the same copy). */
80
+ export const TRUST_SECTION_LEADS: Partial<Record<TrustCenterSectionId, string>> = {
81
+ compliance: 'Frameworks we are audited against, and what is next.',
82
+ controls: 'Security controls currently passing in continuous monitoring (via Vanta).',
83
+ documents: 'Public documents open directly. Gated documents are shared under NDA after one short request.',
84
+ subprocessors: 'Third parties that process customer data on our behalf.',
85
+ ai: 'How customer data is handled by the AI in our products.',
86
+ };
67
87
  /**
68
88
  * The right-hand action / status column: ONE width from tablet up, so every
69
89
  * panel's rows line up; on phones it hugs its content so the title keeps the room.
@@ -540,12 +560,143 @@ export function ContactSection({ onContact }: { onContact: () => void }) {
540
560
  );
541
561
  }
542
562
 
543
- /** Mirrors the page: a card row, then the controls grid. */
544
- export function TrustCenterSkeleton() {
563
+ // ---------------------------------------------------------------------------
564
+ // Skeleton
565
+ // ---------------------------------------------------------------------------
566
+
567
+ /** A section's heading and lead, exactly as `TrustSection` renders them (real text: it does not load). */
568
+ function SkeletonSection({ id, children }: { id: TrustCenterSectionId; children: ReactNode }) {
569
+ const label = TRUST_CENTER_SECTIONS.find(section => section.id === id)?.label ?? id;
570
+ const lead = TRUST_SECTION_LEADS[id];
545
571
  return (
546
- <div className={STACK_CLASSES}>
547
- <CardSkeletonGrid count={2} variant="category" />
548
- <CardSkeletonGrid count={4} variant="category" />
572
+ <section aria-hidden="true" className="flex flex-col gap-[var(--spacing-system-m)]">
573
+ <div className="flex min-w-0 flex-col gap-[var(--spacing-system-xxs)]">
574
+ <h2 className={SECTION_HEADING_CLASS}>{label}</h2>
575
+ {lead ? <p className={BODY_TEXT}>{lead}</p> : null}
576
+ </div>
577
+ {children}
578
+ </section>
579
+ );
580
+ }
581
+
582
+ /**
583
+ * Rows of the REAL `StackedRowsPanel` (its own row heights and borders) whose
584
+ * cells hold bars: a mark (a 40px compliance logo or a 24px glyph), a value over
585
+ * a label, and the right-hand badge or button in the page's `ACTION_COLUMN`.
586
+ */
587
+ function skeletonRows(count: number, shape: { mark: 'logo' | 'glyph'; action: 'badge' | 'button' }): PanelRow[] {
588
+ return Array.from({ length: count }, (_, index) => ({
589
+ id: `skeleton-${index}`,
590
+ columns: [
591
+ {
592
+ key: 'item',
593
+ content: (
594
+ <div className="flex w-full items-center gap-[var(--spacing-system-s)]">
595
+ <UnifiedSkeleton
596
+ className={shape.mark === 'logo' ? 'size-10 shrink-0 rounded-full' : 'size-6 shrink-0 rounded'}
597
+ />
598
+ <div className="flex min-w-0 flex-1 flex-col gap-[var(--spacing-system-xxs)]">
599
+ <UnifiedSkeleton variant="text" className="h-5 w-1/3 md:h-6" />
600
+ <TextSkeleton.Body className="w-1/2" />
601
+ </div>
602
+ </div>
603
+ ),
604
+ },
605
+ {
606
+ key: 'action',
607
+ width: ACTION_COLUMN,
608
+ align: 'right',
609
+ content:
610
+ shape.action === 'badge' ? (
611
+ <UnifiedSkeleton className="h-4 w-28 rounded" />
612
+ ) : (
613
+ <UnifiedSkeleton className="h-6 w-16 rounded md:h-8" />
614
+ ),
615
+ },
616
+ ],
617
+ }));
618
+ }
619
+
620
+ /** The Controls section while loading: its tab strip, its summary and search, and the list box's own skeleton. */
621
+ function ControlsSectionSkeleton() {
622
+ return (
623
+ <div className="flex flex-col gap-[var(--spacing-system-m)]">
624
+ <div className="flex items-center gap-[var(--spacing-system-xxs)] overflow-hidden">
625
+ {[0, 1, 2, 3].map(index => (
626
+ <div key={index} className="shrink-0 p-[var(--spacing-system-m)]">
627
+ <UnifiedSkeleton variant="text" className="h-5 w-36 md:h-6" />
628
+ </div>
629
+ ))}
630
+ </div>
631
+ <div className="flex flex-col gap-[var(--spacing-system-s)] md:flex-row md:items-center md:justify-between">
632
+ <TextSkeleton.Body className="w-48" />
633
+ <UnifiedSkeleton className="h-11 w-full rounded-[6px] md:h-12 md:w-80" />
634
+ </div>
635
+ <div className={CONTROLS_LIST_HEIGHT}>
636
+ <ControlsListSkeleton />
637
+ </div>
638
+ </div>
639
+ );
640
+ }
641
+
642
+ /** The section rail while loading: `StickySectionNav`'s line and its 40px items. */
643
+ function SectionRailSkeleton() {
644
+ return (
645
+ <div className="sticky" style={{ top: STICKY_HEADER_OFFSET_PX }}>
646
+ <div className="relative">
647
+ <div
648
+ className="absolute -left-0.5 top-0 w-px bg-ods-border"
649
+ style={{ height: TRUST_CENTER_SECTIONS.length * 40 }}
650
+ />
651
+ {TRUST_CENTER_SECTIONS.map(section => (
652
+ <div key={section.id} className="flex h-10 items-center px-3 py-2">
653
+ <TextSkeleton.Body className="w-24" />
654
+ </div>
655
+ ))}
656
+ </div>
549
657
  </div>
550
658
  );
551
659
  }
660
+
661
+ /**
662
+ * The page BODY while the trust center loads: built on the loaded body's own
663
+ * layout classes and the real section headings and panels, so nothing moves when
664
+ * the content replaces it. The status row, then Compliance, Controls and
665
+ * Documents (what fills the first screens), beside the section rail.
666
+ */
667
+ export function TrustCenterSkeleton() {
668
+ return (
669
+ <>
670
+ <div className={TRUST_STATUS_ROW_CLASS} aria-hidden="true">
671
+ <div className="flex items-center gap-1.5">
672
+ <UnifiedSkeleton className="size-3 rounded-full" />
673
+ <TextSkeleton.Body className="w-44" />
674
+ </div>
675
+ {/* `DataAttribution`'s own layout: two lines below `sm`, one row above it. */}
676
+ <div className="flex shrink-0 flex-col gap-[var(--spacing-system-xxs)] sm:flex-row sm:items-center sm:gap-[var(--spacing-system-sf)]">
677
+ <div className="flex items-center gap-[var(--spacing-system-xsf)]">
678
+ <UnifiedSkeleton className="size-4 rounded" />
679
+ <TextSkeleton.Body className="w-36" />
680
+ </div>
681
+ <TextSkeleton.Body className="w-44" />
682
+ </div>
683
+ </div>
684
+ <div className={TRUST_BODY_GRID_CLASS} aria-busy="true" aria-label="Loading the trust center">
685
+ <div className={TRUST_SECTIONS_COLUMN_CLASS}>
686
+ <SkeletonSection id="compliance">
687
+ <StackedRowsPanel rows={skeletonRows(3, { mark: 'logo', action: 'badge' })} />
688
+ </SkeletonSection>
689
+ <SkeletonSection id="controls">
690
+ <ControlsSectionSkeleton />
691
+ </SkeletonSection>
692
+ <SkeletonSection id="documents">
693
+ <StackedRowsPanel rows={skeletonRows(2, { mark: 'glyph', action: 'button' })} />
694
+ </SkeletonSection>
695
+ </div>
696
+ <aside className="hidden lg:block" aria-hidden="true">
697
+ <SectionRailSkeleton />
698
+ </aside>
699
+ </div>
700
+ </>
701
+ );
702
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * A design doc's readiness in words — THE wording every surface prints (the hub's design-docs screens, its doc
3
+ * page, the lib's design-doc card): "Ready to build" once every department section is signed off and no blocking
4
+ * comment is open, else "Not ready", and the sign-off caption beside it. Pure, zero-import beyond the type.
5
+ */
6
+ import type { DesignDocCompletion } from '../types/design-doc';
7
+
8
+ export const DESIGN_DOC_READINESS = ['ready', 'not_ready'] as const;
9
+ export type DesignDocReadiness = (typeof DESIGN_DOC_READINESS)[number];
10
+
11
+ /** Each readiness's label and badge colour (a `StatusBadge` scheme). */
12
+ export const DESIGN_DOC_READINESS_DISPLAY: Record<
13
+ DesignDocReadiness,
14
+ { label: string; scheme: 'success' | 'default' }
15
+ > = {
16
+ ready: { label: 'Ready to build', scheme: 'success' },
17
+ not_ready: { label: 'Not ready', scheme: 'default' },
18
+ };
19
+
20
+ /** Ready to build ⇔ `completion.isComplete` (every section signed off, no open blocking comment). */
21
+ export const designDocReadiness = (completion: Pick<DesignDocCompletion, 'isComplete'>): DesignDocReadiness =>
22
+ completion.isComplete ? 'ready' : 'not_ready';
23
+
24
+ /** "4/6 reviews signed off" */
25
+ export function formatCompletionLabel(completion: Pick<DesignDocCompletion, 'total' | 'completed'>): string {
26
+ return `${completion.completed}/${completion.total} reviews signed off`;
27
+ }
28
+
29
+ /** The SAME label plus readiness and the blocking veto (the dashboard row, the doc page, the card). */
30
+ export function formatCompletionLabelWithBlockers(completion: DesignDocCompletion): string {
31
+ const base = formatCompletionLabel(completion);
32
+ if (completion.isComplete) return `${DESIGN_DOC_READINESS_DISPLAY.ready.label} · ${base}`;
33
+ return completion.openBlocking > 0 ? `${base} · ${completion.openBlocking} blocking` : base;
34
+ }
@@ -57,6 +57,7 @@ export { SEO_DESCRIPTION_MAX_LENGTH } from './seo-description';
57
57
  // Brand silhouette SVG registry (CSS mask-image tinting) — centralized so
58
58
  // consumers (e.g. company-hub deck) don't ship their own copies of the marks.
59
59
  export * from './brand-marks';
60
+ export * from './design-doc-readiness';
60
61
  export * from './platform-config';
61
62
  export * from './os-platforms';
62
63
  export * from './access-code-client';
@@ -128,6 +128,7 @@ const BUILDERS: Record<string, (ids: string[], base: string) => string> = {
128
128
  code_symbol: (ids, b) => `${b}/api/code-graph/symbols?ids=${ids.join(',')}`,
129
129
  code_duplicate: (ids, b) => `${b}/api/code-graph/duplicates?ids=${ids.join(',')}`,
130
130
  code_impact: (ids, b) => `${b}/api/code-graph/impacts?ids=${ids.join(',')}`,
131
+ change_set: (ids, b) => `${b}/api/code-graph/change-sets?ids=${ids.join(',')}`,
131
132
  // Single-record live source: the route ignores `?ids=` and returns the ONE
132
133
  // `TrustCenterPublic` object (card id `TRUST_CENTER_CARD_ID`); see
133
134
  // `extractCardItems` for how the loader matches it back.
@@ -91,6 +91,7 @@ export const SOURCE_ICON_NAMES: Record<string, string> = {
91
91
  'code-symbols': 'box',
92
92
  'code-duplicates': 'search',
93
93
  'code-impact': 'activity',
94
+ 'change-sets': 'coding-branch',
94
95
  };
95
96
 
96
97
  /** Lookup an icon name by RagTableConfig.id. Returns undefined when
@@ -185,6 +186,7 @@ export const SOURCE_LABELS_BY_TABLE: Record<string, string> = {
185
186
  'code-symbols': 'Code Symbols',
186
187
  'code-duplicates': 'Duplicates',
187
188
  'code-impact': 'Change Impact',
189
+ 'change-sets': 'Change Sets',
188
190
  };
189
191
 
190
192
  /** Lookup a human-readable label by RagTableConfig.id. Falls back
@@ -281,6 +283,7 @@ export const DEFAULT_DOCUMENT_TYPE_TO_TABLE_ID: Record<string, string> = {
281
283
  code_symbol: 'code-symbols',
282
284
  code_duplicate: 'code-duplicates',
283
285
  code_impact: 'code-impact',
286
+ change_set: 'change-sets',
284
287
  };
285
288
 
286
289
  /**