@redsift/ds-mcp-server 12.3.1-muiv6-alpha.2

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 (258) hide show
  1. package/README.md +203 -0
  2. package/consumer-instructions/.cursorrules +80 -0
  3. package/consumer-instructions/.windsurfrules +80 -0
  4. package/consumer-instructions/CLAUDE.md +87 -0
  5. package/consumer-instructions/redsift-design-system.instructions.md +87 -0
  6. package/data/demos/patterns/crossfiltered-datagrid-page/example.tsx +750 -0
  7. package/data/demos/patterns/crossfiltered-datagrid-page/with-empty-state.tsx +111 -0
  8. package/data/demos/patterns/crossfiltered-datagrid-page/with-error.tsx +122 -0
  9. package/data/demos/patterns/crossfiltered-datagrid-page/with-loading.tsx +88 -0
  10. package/data/demos/patterns/datagrid-page/example.tsx +521 -0
  11. package/data/demos/patterns/datagrid-page/with-empty-state.tsx +80 -0
  12. package/data/demos/patterns/datagrid-page/with-error.tsx +96 -0
  13. package/data/demos/patterns/datagrid-page/with-loading.tsx +57 -0
  14. package/data/demos/patterns/drilldown-datagrid-page/example.tsx +715 -0
  15. package/data/demos/patterns/drilldown-datagrid-page/with-empty-state.tsx +113 -0
  16. package/data/demos/patterns/drilldown-datagrid-page/with-error.tsx +125 -0
  17. package/data/demos/patterns/drilldown-datagrid-page/with-loading.tsx +90 -0
  18. package/data/demos/patterns/server-datagrid-page/example.tsx +757 -0
  19. package/data/demos/patterns/server-datagrid-page/with-empty-state.tsx +107 -0
  20. package/data/demos/patterns/server-datagrid-page/with-error.tsx +107 -0
  21. package/data/demos/patterns/server-datagrid-page/with-loading.tsx +63 -0
  22. package/data/docs/components/charts/Arc.json +179 -0
  23. package/data/docs/components/charts/Arcs.json +104 -0
  24. package/data/docs/components/charts/Axis.json +269 -0
  25. package/data/docs/components/charts/Bar.json +236 -0
  26. package/data/docs/components/charts/BarChart.json +852 -0
  27. package/data/docs/components/charts/BarChartBars.json +18 -0
  28. package/data/docs/components/charts/BarChartGroupedTooltip.json +9 -0
  29. package/data/docs/components/charts/BarChartOverlay.json +34 -0
  30. package/data/docs/components/charts/BarChartSection.json +94 -0
  31. package/data/docs/components/charts/BaseBarChart.json +852 -0
  32. package/data/docs/components/charts/ChartContainerTitle.json +574 -0
  33. package/data/docs/components/charts/DataPoint.json +160 -0
  34. package/data/docs/components/charts/Dot.json +191 -0
  35. package/data/docs/components/charts/EmptyBarChart.json +852 -0
  36. package/data/docs/components/charts/EmptyLineChart.json +753 -0
  37. package/data/docs/components/charts/EmptyPieChart.json +826 -0
  38. package/data/docs/components/charts/EmptyScatterPlot.json +802 -0
  39. package/data/docs/components/charts/Legend.json +510 -0
  40. package/data/docs/components/charts/LegendItem.json +128 -0
  41. package/data/docs/components/charts/Line.json +69 -0
  42. package/data/docs/components/charts/LineChart.json +753 -0
  43. package/data/docs/components/charts/LoadingBarChart.json +852 -0
  44. package/data/docs/components/charts/LoadingLineChart.json +753 -0
  45. package/data/docs/components/charts/LoadingPieChart.json +826 -0
  46. package/data/docs/components/charts/LoadingScatterPlot.json +802 -0
  47. package/data/docs/components/charts/PieChart.json +826 -0
  48. package/data/docs/components/charts/RenderedLineChart.json +753 -0
  49. package/data/docs/components/charts/RenderedLinearBarChart.json +823 -0
  50. package/data/docs/components/charts/RenderedOrdinalBarChart.json +852 -0
  51. package/data/docs/components/charts/RenderedPieChart.json +826 -0
  52. package/data/docs/components/charts/RenderedScatterPlot.json +802 -0
  53. package/data/docs/components/charts/ScatterPlot.json +802 -0
  54. package/data/docs/components/charts/getAxisType.json +9 -0
  55. package/data/docs/components/charts/getComponentPosition.json +9 -0
  56. package/data/docs/components/dashboard/ChartEmptyState.json +42 -0
  57. package/data/docs/components/dashboard/Dashboard.json +26 -0
  58. package/data/docs/components/dashboard/DataCard.json +300 -0
  59. package/data/docs/components/dashboard/DataCardBody.json +431 -0
  60. package/data/docs/components/dashboard/DataCardHeader.json +304 -0
  61. package/data/docs/components/dashboard/DataCardListbox.json +529 -0
  62. package/data/docs/components/dashboard/DataRow.json +539 -0
  63. package/data/docs/components/dashboard/DrilldownItem.json +342 -0
  64. package/data/docs/components/dashboard/FilterableBarChart.json +83 -0
  65. package/data/docs/components/dashboard/FilterableDataGrid.json +83 -0
  66. package/data/docs/components/dashboard/FilterablePieChart.json +83 -0
  67. package/data/docs/components/dashboard/FilterableScatterPlot.json +83 -0
  68. package/data/docs/components/dashboard/PdfDocument.json +58 -0
  69. package/data/docs/components/dashboard/PdfExportButton.json +458 -0
  70. package/data/docs/components/dashboard/TimeSeriesBarChart.json +172 -0
  71. package/data/docs/components/dashboard/WithFilters.json +83 -0
  72. package/data/docs/components/design-system/ActiveDescendantListbox.json +521 -0
  73. package/data/docs/components/design-system/Alert.json +349 -0
  74. package/data/docs/components/design-system/AppBar.json +64 -0
  75. package/data/docs/components/design-system/AppContainer.json +67 -0
  76. package/data/docs/components/design-system/AppContent.json +566 -0
  77. package/data/docs/components/design-system/AppSidePanel.json +87 -0
  78. package/data/docs/components/design-system/Badge.json +293 -0
  79. package/data/docs/components/design-system/BaseBreadcrumbs.json +298 -0
  80. package/data/docs/components/design-system/BaseGrid.json +543 -0
  81. package/data/docs/components/design-system/BaseSkeleton.json +338 -0
  82. package/data/docs/components/design-system/BreadcrumbItem.json +231 -0
  83. package/data/docs/components/design-system/Breadcrumbs.json +298 -0
  84. package/data/docs/components/design-system/Button.json +402 -0
  85. package/data/docs/components/design-system/ButtonGroup.json +415 -0
  86. package/data/docs/components/design-system/ButtonLink.json +568 -0
  87. package/data/docs/components/design-system/Card.json +328 -0
  88. package/data/docs/components/design-system/CardActions.json +431 -0
  89. package/data/docs/components/design-system/CardBody.json +431 -0
  90. package/data/docs/components/design-system/CardHeader.json +428 -0
  91. package/data/docs/components/design-system/Checkbox.json +426 -0
  92. package/data/docs/components/design-system/CheckboxGroup.json +382 -0
  93. package/data/docs/components/design-system/ConditionalWrapper.json +40 -0
  94. package/data/docs/components/design-system/DetailedCard.json +401 -0
  95. package/data/docs/components/design-system/DetailedCardCollapsibleSectionItems.json +29 -0
  96. package/data/docs/components/design-system/DetailedCardHeader.json +37 -0
  97. package/data/docs/components/design-system/DetailedCardSection.json +90 -0
  98. package/data/docs/components/design-system/DetailedCardSectionItem.json +109 -0
  99. package/data/docs/components/design-system/Flexbox.json +523 -0
  100. package/data/docs/components/design-system/FocusWithinGroup.json +9 -0
  101. package/data/docs/components/design-system/Grid.json +543 -0
  102. package/data/docs/components/design-system/GridItem.json +388 -0
  103. package/data/docs/components/design-system/Heading.json +390 -0
  104. package/data/docs/components/design-system/Icon.json +325 -0
  105. package/data/docs/components/design-system/IconButton.json +371 -0
  106. package/data/docs/components/design-system/IconButtonLink.json +588 -0
  107. package/data/docs/components/design-system/Item.json +554 -0
  108. package/data/docs/components/design-system/Link.json +552 -0
  109. package/data/docs/components/design-system/LinkButton.json +397 -0
  110. package/data/docs/components/design-system/Listbox.json +529 -0
  111. package/data/docs/components/design-system/Number.json +773 -0
  112. package/data/docs/components/design-system/NumberField.json +594 -0
  113. package/data/docs/components/design-system/Pill.json +378 -0
  114. package/data/docs/components/design-system/ProgressBar.json +121 -0
  115. package/data/docs/components/design-system/RadarSvgLinearGradient.json +9 -0
  116. package/data/docs/components/design-system/Radio.json +415 -0
  117. package/data/docs/components/design-system/RadioGroup.json +382 -0
  118. package/data/docs/components/design-system/RenderedListboxItem.json +18 -0
  119. package/data/docs/components/design-system/RovingTabindexListbox.json +521 -0
  120. package/data/docs/components/design-system/Shield.json +360 -0
  121. package/data/docs/components/design-system/SideNavigationMenu.json +144 -0
  122. package/data/docs/components/design-system/SideNavigationMenuBar.json +89 -0
  123. package/data/docs/components/design-system/SideNavigationMenuItem.json +319 -0
  124. package/data/docs/components/design-system/Skeleton.json +338 -0
  125. package/data/docs/components/design-system/SkeletonCircle.json +338 -0
  126. package/data/docs/components/design-system/SkeletonText.json +371 -0
  127. package/data/docs/components/design-system/Spinner.json +291 -0
  128. package/data/docs/components/design-system/Switch.json +415 -0
  129. package/data/docs/components/design-system/SwitchGroup.json +382 -0
  130. package/data/docs/components/design-system/Text.json +418 -0
  131. package/data/docs/components/design-system/TextArea.json +501 -0
  132. package/data/docs/components/design-system/TextField.json +539 -0
  133. package/data/docs/components/design-system/sizeToDimension.json +9 -0
  134. package/data/docs/components/pickers/BaseCombobox.json +320 -0
  135. package/data/docs/components/pickers/BaseComboboxContent.json +453 -0
  136. package/data/docs/components/pickers/BaseMenuButton.json +240 -0
  137. package/data/docs/components/pickers/BaseMenuButtonContent.json +442 -0
  138. package/data/docs/components/pickers/BaseSelect.json +258 -0
  139. package/data/docs/components/pickers/Combobox.json +320 -0
  140. package/data/docs/components/pickers/ComboboxContent.json +453 -0
  141. package/data/docs/components/pickers/ComboboxContentFooter.json +431 -0
  142. package/data/docs/components/pickers/ComboboxContentHeader.json +431 -0
  143. package/data/docs/components/pickers/ComboboxContentListbox.json +541 -0
  144. package/data/docs/components/pickers/ComboboxTrigger.json +336 -0
  145. package/data/docs/components/pickers/Item.json +554 -0
  146. package/data/docs/components/pickers/MenuButton.json +240 -0
  147. package/data/docs/components/pickers/MenuButtonContent.json +442 -0
  148. package/data/docs/components/pickers/MenuButtonContentFooter.json +431 -0
  149. package/data/docs/components/pickers/MenuButtonContentHeader.json +431 -0
  150. package/data/docs/components/pickers/MenuButtonContentMenu.json +523 -0
  151. package/data/docs/components/pickers/MenuButtonTrigger.json +287 -0
  152. package/data/docs/components/pickers/Select.json +258 -0
  153. package/data/docs/components/pickers/SelectContent.json +442 -0
  154. package/data/docs/components/pickers/SelectTrigger.json +298 -0
  155. package/data/docs/components/popovers/BaseDialog.json +114 -0
  156. package/data/docs/components/popovers/BaseDialogContent.json +21 -0
  157. package/data/docs/components/popovers/BasePopover.json +171 -0
  158. package/data/docs/components/popovers/BaseToggletip.json +184 -0
  159. package/data/docs/components/popovers/BaseTooltip.json +121 -0
  160. package/data/docs/components/popovers/Button.json +402 -0
  161. package/data/docs/components/popovers/ButtonLink.json +568 -0
  162. package/data/docs/components/popovers/Dialog.json +114 -0
  163. package/data/docs/components/popovers/DialogContent.json +21 -0
  164. package/data/docs/components/popovers/DialogContentActions.json +442 -0
  165. package/data/docs/components/popovers/DialogContentBody.json +442 -0
  166. package/data/docs/components/popovers/DialogContentHeader.json +76 -0
  167. package/data/docs/components/popovers/DialogTrigger.json +276 -0
  168. package/data/docs/components/popovers/IconButton.json +371 -0
  169. package/data/docs/components/popovers/IconButtonLink.json +588 -0
  170. package/data/docs/components/popovers/Link.json +552 -0
  171. package/data/docs/components/popovers/LinkButton.json +397 -0
  172. package/data/docs/components/popovers/Popover.json +171 -0
  173. package/data/docs/components/popovers/PopoverContent.json +442 -0
  174. package/data/docs/components/popovers/PopoverTrigger.json +276 -0
  175. package/data/docs/components/popovers/Toast.json +145 -0
  176. package/data/docs/components/popovers/ToastContainer.json +122 -0
  177. package/data/docs/components/popovers/Toggletip.json +184 -0
  178. package/data/docs/components/popovers/ToggletipContent.json +402 -0
  179. package/data/docs/components/popovers/ToggletipTrigger.json +276 -0
  180. package/data/docs/components/popovers/Tooltip.json +121 -0
  181. package/data/docs/components/popovers/TooltipContent.json +402 -0
  182. package/data/docs/components/popovers/TooltipTrigger.json +276 -0
  183. package/data/docs/components/products/Dialog.json +106 -0
  184. package/data/docs/components/products/MenuButton.json +232 -0
  185. package/data/docs/components/products/PulsedRadarImage.json +9 -0
  186. package/data/docs/components/products/RadarButton.json +402 -0
  187. package/data/docs/components/products/RadarItem.json +554 -0
  188. package/data/docs/components/table/ControlledPagination.json +9 -0
  189. package/data/docs/components/table/DataGrid.json +93 -0
  190. package/data/docs/components/table/GridToolbarFilterSemanticField.json +69 -0
  191. package/data/docs/components/table/ServerSideControlledPagination.json +9 -0
  192. package/data/docs/components/table/StatefulDataGrid.json +117 -0
  193. package/data/docs/components/table/TextCell.json +118 -0
  194. package/data/docs/components/table/Toolbar.json +145 -0
  195. package/data/docs/components/table/ToolbarWrapper.json +9 -0
  196. package/data/docs/components-index.json +1206 -0
  197. package/data/docs/components.json +55694 -0
  198. package/data/docs/llms-full.txt +8012 -0
  199. package/data/docs/llms.txt +234 -0
  200. package/data/docs/patterns-catalog.md +359 -0
  201. package/data/docs/patterns.json +712 -0
  202. package/data/metadata.json +4 -0
  203. package/data/patterns/crossfiltered-datagrid-page.mdx +386 -0
  204. package/data/patterns/datagrid-page.mdx +214 -0
  205. package/data/patterns/drilldown-datagrid-page.mdx +291 -0
  206. package/data/patterns/server-datagrid-page.mdx +301 -0
  207. package/data/tokens/properties/components/dark-components.json +1108 -0
  208. package/data/tokens/properties/components/light-components.json +1108 -0
  209. package/data/tokens/properties/core/border-radius.json +3 -0
  210. package/data/tokens/properties/core/color.json +280 -0
  211. package/data/tokens/properties/core/layout.json +14 -0
  212. package/data/tokens/properties/core/typography.json +199 -0
  213. package/data/tokens/redsift-design-tokens.css +1391 -0
  214. package/dist/data-store.d.ts +47 -0
  215. package/dist/data-store.d.ts.map +1 -0
  216. package/dist/data-store.js +152 -0
  217. package/dist/data-store.js.map +1 -0
  218. package/dist/index.d.ts +16 -0
  219. package/dist/index.d.ts.map +1 -0
  220. package/dist/index.js +50 -0
  221. package/dist/index.js.map +1 -0
  222. package/dist/init.d.ts +14 -0
  223. package/dist/init.d.ts.map +1 -0
  224. package/dist/init.js +137 -0
  225. package/dist/init.js.map +1 -0
  226. package/dist/paths.d.ts +30 -0
  227. package/dist/paths.d.ts.map +1 -0
  228. package/dist/paths.js +53 -0
  229. package/dist/paths.js.map +1 -0
  230. package/dist/pattern-store.d.ts +41 -0
  231. package/dist/pattern-store.d.ts.map +1 -0
  232. package/dist/pattern-store.js +177 -0
  233. package/dist/pattern-store.js.map +1 -0
  234. package/dist/prompts.d.ts +14 -0
  235. package/dist/prompts.d.ts.map +1 -0
  236. package/dist/prompts.js +762 -0
  237. package/dist/prompts.js.map +1 -0
  238. package/dist/resources.d.ts +14 -0
  239. package/dist/resources.d.ts.map +1 -0
  240. package/dist/resources.js +482 -0
  241. package/dist/resources.js.map +1 -0
  242. package/dist/scaffold.d.ts +31 -0
  243. package/dist/scaffold.d.ts.map +1 -0
  244. package/dist/scaffold.js +239 -0
  245. package/dist/scaffold.js.map +1 -0
  246. package/dist/token-store.d.ts +70 -0
  247. package/dist/token-store.d.ts.map +1 -0
  248. package/dist/token-store.js +196 -0
  249. package/dist/token-store.js.map +1 -0
  250. package/dist/tools.d.ts +15 -0
  251. package/dist/tools.d.ts.map +1 -0
  252. package/dist/tools.js +491 -0
  253. package/dist/tools.js.map +1 -0
  254. package/dist/types.d.ts +108 -0
  255. package/dist/types.d.ts.map +1 -0
  256. package/dist/types.js +17 -0
  257. package/dist/types.js.map +1 -0
  258. package/package.json +39 -0
@@ -0,0 +1,715 @@
1
+ import React, { useCallback, useMemo, useState } from 'react';
2
+ import { DataGrid, TextCell } from '@redsift/table';
3
+ import { Button, Flexbox, Icon, IconButtonLink, Pill } from '@redsift/design-system';
4
+ import { DataCard, DrilldownItem } from '@redsift/dashboard';
5
+ import {
6
+ mdiArrowRight,
7
+ mdiCheck,
8
+ mdiClose,
9
+ mdiShieldCheck,
10
+ mdiLinkVariant,
11
+ mdiLeaf,
12
+ mdiCheckCircleOutline,
13
+ mdiShapeOutline,
14
+ mdiToggleSwitch,
15
+ } from '@redsift/icons';
16
+ import { GridColDef, GridFilterModel, GridRenderCellParams, GridRowSelectionModel } from '@mui/x-data-grid-pro';
17
+ import {
18
+ GridToolbarContainer,
19
+ GridToolbarColumnsButton,
20
+ GridToolbarDensitySelector,
21
+ GridToolbarFilterButton,
22
+ GridToolbarExport,
23
+ GridToolbarQuickFilter,
24
+ } from '@mui/x-data-grid-pro';
25
+
26
+ // -- Toolbar ----------------------------------------------------------------
27
+
28
+ const CustomToolbar = () => (
29
+ <GridToolbarContainer>
30
+ <Flexbox gap="0">
31
+ <GridToolbarFilterButton />
32
+ <GridToolbarColumnsButton />
33
+ <GridToolbarDensitySelector />
34
+ <GridToolbarExport />
35
+ <Flexbox marginLeft="8px">
36
+ <GridToolbarQuickFilter />
37
+ </Flexbox>
38
+ </Flexbox>
39
+ </GridToolbarContainer>
40
+ );
41
+
42
+ // -- Tag options for the rsMultipleSelect column ----------------------------
43
+
44
+ const TAG_OPTIONS = [
45
+ { value: 'production', label: 'Production' },
46
+ { value: 'staging', label: 'Staging' },
47
+ { value: 'legacy', label: 'Legacy' },
48
+ { value: 'infrastructure', label: 'Infrastructure' },
49
+ { value: 'cdn', label: 'CDN' },
50
+ { value: 'api', label: 'API' },
51
+ { value: 'auth', label: 'Auth' },
52
+ { value: 'ca', label: 'CA' },
53
+ { value: 'email', label: 'Email' },
54
+ { value: 'ecommerce', label: 'Ecommerce' },
55
+ { value: 'wildcard', label: 'Wildcard' },
56
+ { value: 'monitoring', label: 'Monitoring' },
57
+ { value: 'vpn', label: 'VPN' },
58
+ { value: 'billing', label: 'Billing' },
59
+ { value: 'ci', label: 'CI' },
60
+ { value: 'cms', label: 'CMS' },
61
+ ];
62
+
63
+ // -- Row type ---------------------------------------------------------------
64
+
65
+ type Row = {
66
+ id: string;
67
+ label: string;
68
+ active: boolean;
69
+ status: 'active' | 'warning' | 'critical' | 'expired' | 'revoked';
70
+ category: 'primary' | 'secondary' | 'tertiary';
71
+ score: number;
72
+ expiry: string;
73
+ tags: string[];
74
+ };
75
+
76
+ // -- 27 rows ----------------------------------------------------------------
77
+
78
+ const rows: Row[] = [
79
+ {
80
+ id: 'a3f8c1d2',
81
+ label: 'cdn.acme.com',
82
+ active: true,
83
+ status: 'active',
84
+ category: 'primary',
85
+ score: 92,
86
+ expiry: '2027-06-15T00:00:00Z',
87
+ tags: ['production', 'cdn'],
88
+ },
89
+ {
90
+ id: 'b7e2d4f6',
91
+ label: 'api.globex.io',
92
+ active: true,
93
+ status: 'warning',
94
+ category: 'primary',
95
+ score: 74,
96
+ expiry: '2026-05-01T12:00:00Z',
97
+ tags: ['production', 'api'],
98
+ },
99
+ {
100
+ id: 'c1a9b5e3',
101
+ label: '*.staging.acme.com',
102
+ active: true,
103
+ status: 'critical',
104
+ category: 'secondary',
105
+ score: 45,
106
+ expiry: '2026-03-28T23:59:59Z',
107
+ tags: ['staging'],
108
+ },
109
+ {
110
+ id: 'd4f0c8a7',
111
+ label: 'mail.initech.dev',
112
+ active: false,
113
+ status: 'expired',
114
+ category: 'tertiary',
115
+ score: 12,
116
+ expiry: '2025-11-30T08:45:00Z',
117
+ tags: ['legacy', 'email'],
118
+ },
119
+ {
120
+ id: 'e6b3d1f9',
121
+ label: 'Acme Root CA',
122
+ active: true,
123
+ status: 'active',
124
+ category: 'primary',
125
+ score: 100,
126
+ expiry: '2035-06-01T00:00:00Z',
127
+ tags: ['infrastructure'],
128
+ },
129
+ {
130
+ id: 'f2c7a5e8',
131
+ label: 'Issuing CA G2',
132
+ active: true,
133
+ status: 'active',
134
+ category: 'secondary',
135
+ score: 98,
136
+ expiry: '2031-12-31T23:59:59Z',
137
+ tags: ['infrastructure', 'ca'],
138
+ },
139
+ {
140
+ id: 'a8d4f6b1',
141
+ label: 'auth.globex.io',
142
+ active: false,
143
+ status: 'revoked',
144
+ category: 'primary',
145
+ score: 0,
146
+ expiry: '2026-09-10T17:30:00Z',
147
+ tags: ['production', 'auth'],
148
+ },
149
+ {
150
+ id: 'b5e9c3a2',
151
+ label: 'shop.initech.dev',
152
+ active: true,
153
+ status: 'warning',
154
+ category: 'tertiary',
155
+ score: 63,
156
+ expiry: '2026-04-20T00:00:00Z',
157
+ tags: ['ecommerce'],
158
+ },
159
+ {
160
+ id: 'c0f7d2b4',
161
+ label: 'Sectigo DV CA',
162
+ active: true,
163
+ status: 'active',
164
+ category: 'secondary',
165
+ score: 95,
166
+ expiry: '2030-01-01T00:00:00Z',
167
+ tags: ['infrastructure', 'ca'],
168
+ },
169
+ {
170
+ id: 'd9a1e5c6',
171
+ label: 'vault.initech.dev',
172
+ active: false,
173
+ status: 'expired',
174
+ category: 'primary',
175
+ score: 8,
176
+ expiry: '2024-07-22T14:00:00Z',
177
+ tags: ['legacy'],
178
+ },
179
+ {
180
+ id: 'e3b8f0d7',
181
+ label: '*.acme.com',
182
+ active: true,
183
+ status: 'active',
184
+ category: 'primary',
185
+ score: 88,
186
+ expiry: '2027-08-05T12:00:00Z',
187
+ tags: ['production', 'wildcard'],
188
+ },
189
+ {
190
+ id: 'f6c2a4e1',
191
+ label: 'portal.globex.io',
192
+ active: true,
193
+ status: 'critical',
194
+ category: 'tertiary',
195
+ score: 51,
196
+ expiry: '2026-03-26T10:00:00Z',
197
+ tags: [],
198
+ },
199
+ {
200
+ id: 'a1d7f3b9',
201
+ label: 'logs.initech.dev',
202
+ active: true,
203
+ status: 'active',
204
+ category: 'secondary',
205
+ score: 81,
206
+ expiry: '2028-02-14T00:00:00Z',
207
+ tags: ['production', 'monitoring'],
208
+ },
209
+ {
210
+ id: 'b4e8c6a3',
211
+ label: 'cms.acme.com',
212
+ active: false,
213
+ status: 'expired',
214
+ category: 'tertiary',
215
+ score: 5,
216
+ expiry: '2025-06-10T00:00:00Z',
217
+ tags: ['legacy', 'cms'],
218
+ },
219
+ {
220
+ id: 'c7f2d0b5',
221
+ label: 'DigiCert EV CA',
222
+ active: true,
223
+ status: 'active',
224
+ category: 'secondary',
225
+ score: 99,
226
+ expiry: '2033-09-15T00:00:00Z',
227
+ tags: ['infrastructure', 'ca'],
228
+ },
229
+ {
230
+ id: 'd0a3e9f1',
231
+ label: 'vpn.globex.io',
232
+ active: true,
233
+ status: 'warning',
234
+ category: 'primary',
235
+ score: 68,
236
+ expiry: '2026-04-12T00:00:00Z',
237
+ tags: ['production', 'vpn'],
238
+ },
239
+ {
240
+ id: 'e5b1c4d8',
241
+ label: '*.initech.dev',
242
+ active: true,
243
+ status: 'active',
244
+ category: 'primary',
245
+ score: 85,
246
+ expiry: '2027-11-20T00:00:00Z',
247
+ tags: ['production', 'wildcard'],
248
+ },
249
+ {
250
+ id: 'f8c6a2e7',
251
+ label: 'docs.acme.com',
252
+ active: true,
253
+ status: 'active',
254
+ category: 'tertiary',
255
+ score: 90,
256
+ expiry: '2028-07-01T00:00:00Z',
257
+ tags: ['production'],
258
+ },
259
+ {
260
+ id: 'a2d5f9b0',
261
+ label: 'billing.globex.io',
262
+ active: false,
263
+ status: 'revoked',
264
+ category: 'primary',
265
+ score: 0,
266
+ expiry: '2026-01-15T00:00:00Z',
267
+ tags: ['production', 'billing'],
268
+ },
269
+ {
270
+ id: 'b6e0c3a1',
271
+ label: 'staging.initech.dev',
272
+ active: true,
273
+ status: 'critical',
274
+ category: 'secondary',
275
+ score: 42,
276
+ expiry: '2026-03-27T00:00:00Z',
277
+ tags: ['staging', 'ci'],
278
+ },
279
+ {
280
+ id: 'c9f4d7b2',
281
+ label: "Let's Encrypt R3",
282
+ active: true,
283
+ status: 'active',
284
+ category: 'secondary',
285
+ score: 97,
286
+ expiry: '2030-09-30T00:00:00Z',
287
+ tags: ['infrastructure', 'ca'],
288
+ },
289
+ {
290
+ id: 'd3a8e1f5',
291
+ label: 'analytics.acme.com',
292
+ active: true,
293
+ status: 'warning',
294
+ category: 'tertiary',
295
+ score: 59,
296
+ expiry: '2026-04-05T00:00:00Z',
297
+ tags: ['production', 'monitoring'],
298
+ },
299
+ {
300
+ id: 'e7b2c5d9',
301
+ label: 'sso.globex.io',
302
+ active: true,
303
+ status: 'active',
304
+ category: 'primary',
305
+ score: 91,
306
+ expiry: '2027-12-01T00:00:00Z',
307
+ tags: ['production', 'auth'],
308
+ },
309
+ {
310
+ id: 'f0c9a6e4',
311
+ label: 'wiki.initech.dev',
312
+ active: false,
313
+ status: 'expired',
314
+ category: 'tertiary',
315
+ score: 3,
316
+ expiry: '2024-12-31T00:00:00Z',
317
+ tags: ['legacy'],
318
+ },
319
+ {
320
+ id: 'a4d1f8b6',
321
+ label: 'proxy.acme.com',
322
+ active: true,
323
+ status: 'active',
324
+ category: 'primary',
325
+ score: 83,
326
+ expiry: '2028-03-22T00:00:00Z',
327
+ tags: ['production', 'cdn'],
328
+ },
329
+ {
330
+ id: 'b9e5c0a8',
331
+ label: 'dev.globex.io',
332
+ active: true,
333
+ status: 'critical',
334
+ category: 'secondary',
335
+ score: 38,
336
+ expiry: '2026-03-25T00:00:00Z',
337
+ tags: ['staging'],
338
+ },
339
+ {
340
+ id: 'c2f6d3b7',
341
+ label: 'GTS Root R1',
342
+ active: true,
343
+ status: 'active',
344
+ category: 'primary',
345
+ score: 100,
346
+ expiry: '2036-06-22T00:00:00Z',
347
+ tags: ['infrastructure'],
348
+ },
349
+ ];
350
+
351
+ // -- Helpers ----------------------------------------------------------------
352
+
353
+ const statusColor = (v: string) => {
354
+ switch (v) {
355
+ case 'active':
356
+ return 'success';
357
+ case 'warning':
358
+ return 'warning';
359
+ case 'critical':
360
+ return 'error';
361
+ case 'expired':
362
+ return 'grey';
363
+ case 'revoked':
364
+ return 'error-dark';
365
+ default:
366
+ return 'grey';
367
+ }
368
+ };
369
+
370
+ const categoryIcon = (v: string) => {
371
+ switch (v) {
372
+ case 'primary':
373
+ return { icon: mdiShieldCheck, color: 'success' };
374
+ case 'secondary':
375
+ return { icon: mdiLinkVariant, color: 'info' };
376
+ case 'tertiary':
377
+ return { icon: mdiLeaf, color: 'grey' };
378
+ default:
379
+ return { icon: mdiLeaf, color: 'grey' };
380
+ }
381
+ };
382
+
383
+ const formatDate = (d: Date) => (isNaN(d.getTime()) ? '—' : d.toISOString().replace('T', ' ').slice(0, 19));
384
+
385
+ const STATUS_VALUES = ['active', 'warning', 'critical', 'expired', 'revoked'] as const;
386
+ const CATEGORY_VALUES = ['primary', 'secondary', 'tertiary'] as const;
387
+
388
+ // -- Aggregate counts (always from the full dataset — one-way) ---------------
389
+
390
+ function useAggregateCounts(allRows: Row[]) {
391
+ return useMemo(() => {
392
+ const status: Record<string, number> = {};
393
+ const category: Record<string, number> = {};
394
+ let activeCount = 0;
395
+ let inactiveCount = 0;
396
+
397
+ for (const row of allRows) {
398
+ status[row.status] = (status[row.status] || 0) + 1;
399
+ category[row.category] = (category[row.category] || 0) + 1;
400
+ if (row.active) activeCount++;
401
+ else inactiveCount++;
402
+ }
403
+
404
+ return { status, category, activeCount, inactiveCount };
405
+ }, [allRows]);
406
+ }
407
+
408
+ // -- Filter helpers ----------------------------------------------------------
409
+
410
+ /**
411
+ * Read the selected values for a field from the filterModel.
412
+ * Returns [] for `is` / `isNot` operators — the DataCard clears in that case.
413
+ * Only `isAnyOf` is considered synced.
414
+ */
415
+ function getSelectedFromFilterModel(filterModel: GridFilterModel, fieldName: string): string[] {
416
+ const item = filterModel.items.find((i) => i.field === fieldName);
417
+ if (!item || item.value === undefined) return [];
418
+ if (item.operator === 'isAnyOf') {
419
+ return Array.isArray(item.value) ? item.value : [item.value];
420
+ }
421
+ // For `is`, `isNot`, or any other operator → clear the card selection
422
+ return [];
423
+ }
424
+
425
+ /**
426
+ * Returns a handler that adds/removes an `isAnyOf` filter for `fieldName`.
427
+ * Always creates a fresh `isAnyOf` item — never preserves an incompatible operator.
428
+ * When `values` is empty, removes the filter item entirely.
429
+ */
430
+ function updateFilterModel(filterModel: GridFilterModel, fieldName: string, values: string[]): GridFilterModel {
431
+ const items = filterModel.items.filter((i) => i.field !== fieldName);
432
+ if (values.length > 0) {
433
+ items.push({
434
+ field: fieldName,
435
+ id: Math.floor(Math.random() * 100000),
436
+ operator: 'isAnyOf',
437
+ value: values,
438
+ });
439
+ }
440
+ return { ...filterModel, items };
441
+ }
442
+
443
+ /**
444
+ * Returns whether the active boolean filter is set.
445
+ * Returns null when no compatible boolean filter exists (is/isNot with non-isAnyOf → null).
446
+ */
447
+ function getActiveBooleanSelection(filterModel: GridFilterModel): boolean | null {
448
+ const item = filterModel.items.find((i) => i.field === 'active');
449
+ if (!item || item.value === undefined) return null;
450
+ if (item.operator === 'is') return item.value === 'true';
451
+ return null;
452
+ }
453
+
454
+ function updateBooleanFilter(filterModel: GridFilterModel, fieldName: string, value: boolean | null): GridFilterModel {
455
+ const items = filterModel.items.filter((i) => i.field !== fieldName);
456
+ if (value !== null) {
457
+ items.push({
458
+ field: fieldName,
459
+ id: Math.floor(Math.random() * 100000),
460
+ operator: 'is',
461
+ value: String(value),
462
+ });
463
+ }
464
+ return { ...filterModel, items };
465
+ }
466
+
467
+ // -- Columns ----------------------------------------------------------------
468
+
469
+ const columns: GridColDef[] = [
470
+ {
471
+ field: 'id',
472
+ headerName: 'ID',
473
+ width: 100,
474
+ type: 'rsString' as string,
475
+ renderCell: ({ value }: GridRenderCellParams) => <TextCell style={{ fontFamily: 'monospace' }}>{value}</TextCell>,
476
+ },
477
+ {
478
+ field: 'label',
479
+ headerName: 'Label',
480
+ flex: 2,
481
+ type: 'rsString' as string,
482
+ renderCell: ({ value }: GridRenderCellParams) => <TextCell>{value}</TextCell>,
483
+ },
484
+ {
485
+ field: 'active',
486
+ headerName: 'Active',
487
+ width: 80,
488
+ type: 'boolean',
489
+ renderCell: ({ value }: GridRenderCellParams) =>
490
+ value ? (
491
+ <Icon icon={mdiCheck} color="success" size="small" />
492
+ ) : (
493
+ <Icon icon={mdiClose} color="grey" size="small" />
494
+ ),
495
+ },
496
+ {
497
+ field: 'status',
498
+ headerName: 'Status',
499
+ flex: 1,
500
+ type: 'rsSingleSelect' as string,
501
+ valueOptions: [
502
+ { value: 'active', label: 'Active' },
503
+ { value: 'warning', label: 'Warning' },
504
+ { value: 'critical', label: 'Critical' },
505
+ { value: 'expired', label: 'Expired' },
506
+ { value: 'revoked', label: 'Revoked' },
507
+ ],
508
+ renderCell: ({ value }: GridRenderCellParams) => <Pill color={statusColor(value)}>{value}</Pill>,
509
+ } as GridColDef,
510
+ {
511
+ field: 'category',
512
+ headerName: 'Category',
513
+ width: 110,
514
+ type: 'rsSingleSelect' as string,
515
+ valueOptions: [
516
+ { value: 'primary', label: 'Primary' },
517
+ { value: 'secondary', label: 'Secondary' },
518
+ { value: 'tertiary', label: 'Tertiary' },
519
+ ],
520
+ renderCell: ({ value }: GridRenderCellParams) => {
521
+ const cfg = categoryIcon(value);
522
+ return <Icon icon={cfg.icon} color={cfg.color} size="small" />;
523
+ },
524
+ } as GridColDef,
525
+ {
526
+ field: 'score',
527
+ headerName: 'Score',
528
+ width: 80,
529
+ type: 'rsNumber' as string,
530
+ },
531
+ {
532
+ field: 'expiry',
533
+ headerName: 'Expiry',
534
+ width: 180,
535
+ type: 'date',
536
+ valueGetter: (value: unknown) => new Date(value as string),
537
+ renderCell: ({ value }: GridRenderCellParams) => {
538
+ if (!value) return <TextCell>—</TextCell>;
539
+ const d = value as Date;
540
+ const isPast = d < new Date();
541
+ return <TextCell style={isPast ? { color: 'var(--redsift-color-red-n)' } : undefined}>{formatDate(d)}</TextCell>;
542
+ },
543
+ },
544
+ {
545
+ field: 'tags',
546
+ headerName: 'Tags',
547
+ flex: 2,
548
+ type: 'rsMultipleSelect' as string,
549
+ valueOptions: TAG_OPTIONS,
550
+ sortable: false,
551
+ renderCell: ({ value }: GridRenderCellParams) => {
552
+ const arr = value as string[];
553
+ if (!arr || arr.length === 0) return <TextCell>—</TextCell>;
554
+ return (
555
+ <Flexbox gap="4px" flexWrap="wrap" alignItems="center">
556
+ {arr.map((tag: string) => (
557
+ <Pill key={tag} color="blue">
558
+ {tag}
559
+ </Pill>
560
+ ))}
561
+ </Flexbox>
562
+ );
563
+ },
564
+ } as GridColDef,
565
+ {
566
+ field: 'actions',
567
+ headerName: '',
568
+ type: 'actions',
569
+ width: 56,
570
+ hideable: false,
571
+ sortable: false,
572
+ filterable: false,
573
+ renderCell: ({ row }: GridRenderCellParams) => (
574
+ <IconButtonLink
575
+ href="/patterns/drilldown-datagrid-page"
576
+ icon={mdiArrowRight}
577
+ color="info"
578
+ aria-label={`View ${row.label}`}
579
+ />
580
+ ),
581
+ },
582
+ ];
583
+
584
+ // -- Main component ---------------------------------------------------------
585
+
586
+ export default () => {
587
+ const [filterModel, setFilterModel] = useState<GridFilterModel>({ items: [] });
588
+ const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
589
+
590
+ const counts = useAggregateCounts(rows);
591
+
592
+ // Derive DataCard selections from filterModel
593
+ const statusSelection = getSelectedFromFilterModel(filterModel, 'status');
594
+ const categorySelection = getSelectedFromFilterModel(filterModel, 'category');
595
+ // DataCard change handlers — update filterModel
596
+ const handleStatusChange = useCallback(
597
+ (values: string[]) => setFilterModel((prev) => updateFilterModel(prev, 'status', values)),
598
+ []
599
+ );
600
+
601
+ const handleCategoryChange = useCallback(
602
+ (values: string[]) => setFilterModel((prev) => updateFilterModel(prev, 'category', values)),
603
+ []
604
+ );
605
+
606
+ const handleActiveToggle = useCallback((value: boolean) => {
607
+ setFilterModel((prev) => {
608
+ const current = getActiveBooleanSelection(prev);
609
+ // Toggle: if already set to this value, clear; otherwise set
610
+ if (current === value) return updateBooleanFilter(prev, 'active', null);
611
+ return updateBooleanFilter(prev, 'active', value);
612
+ });
613
+ }, []);
614
+
615
+ return (
616
+ <div style={{ width: '100%' }}>
617
+ <Flexbox flexDirection="column" gap="16px">
618
+ {/* DataCard row — summary cards with drilldown filters */}
619
+ <Flexbox gap="12px" flexWrap="wrap">
620
+ {/* Status card */}
621
+ <DataCard color="info" isLoading={false}>
622
+ <DataCard.Header icon={mdiCheckCircleOutline} title="Status" />
623
+ <DataCard.Listbox
624
+ selectionMode="multiple"
625
+ values={statusSelection}
626
+ onChange={handleStatusChange}
627
+ aria-label="Filter by status"
628
+ >
629
+ {STATUS_VALUES.map((s) => (
630
+ <DrilldownItem
631
+ key={s}
632
+ value={counts.status[s] || 0}
633
+ legend={s}
634
+ hasFilterIcon
635
+ labelDecorator={(l) => l.charAt(0).toUpperCase() + l.slice(1)}
636
+ />
637
+ ))}
638
+ </DataCard.Listbox>
639
+ </DataCard>
640
+
641
+ {/* Category card */}
642
+ <DataCard color="warning" isLoading={false}>
643
+ <DataCard.Header icon={mdiShapeOutline} title="Category" />
644
+ <DataCard.Listbox
645
+ selectionMode="multiple"
646
+ values={categorySelection}
647
+ onChange={handleCategoryChange}
648
+ aria-label="Filter by category"
649
+ >
650
+ {CATEGORY_VALUES.map((c) => (
651
+ <DrilldownItem
652
+ key={c}
653
+ value={counts.category[c] || 0}
654
+ legend={c}
655
+ hasFilterIcon
656
+ labelDecorator={(l) => l.charAt(0).toUpperCase() + l.slice(1)}
657
+ />
658
+ ))}
659
+ </DataCard.Listbox>
660
+ </DataCard>
661
+
662
+ {/* Active card (boolean toggle) */}
663
+ <DataCard color="success" isLoading={false}>
664
+ <DataCard.Header icon={mdiToggleSwitch} title="Active" />
665
+ <DataCard.Body>
666
+ <DrilldownItem
667
+ value={counts.activeCount}
668
+ legend="Active"
669
+ hasFilterIcon
670
+ onClick={() => handleActiveToggle(true)}
671
+ size="medium"
672
+ />
673
+ <DrilldownItem
674
+ value={counts.inactiveCount}
675
+ legend="Inactive"
676
+ hasFilterIcon
677
+ onClick={() => handleActiveToggle(false)}
678
+ size="medium"
679
+ />
680
+ </DataCard.Body>
681
+ </DataCard>
682
+ </Flexbox>
683
+
684
+ {/* Bulk actions bar */}
685
+ {selectionModel.length > 0 && (
686
+ <Flexbox gap="8px" alignItems="center" style={{ padding: '8px 0' }}>
687
+ <Pill color="info">{selectionModel.length} selected</Pill>
688
+ <Button variant="secondary" onClick={() => console.log('Selected:', selectionModel)}>
689
+ Log Selection
690
+ </Button>
691
+ <Button variant="secondary" color="error" onClick={() => console.log('Delete:', selectionModel)}>
692
+ Delete
693
+ </Button>
694
+ </Flexbox>
695
+ )}
696
+
697
+ {/* DataGrid with controlled filterModel */}
698
+ <DataGrid
699
+ autoHeight
700
+ pagination
701
+ paginationModel={{ page: 0, pageSize: 10 }}
702
+ pageSizeOptions={[10, 25, 50]}
703
+ rows={rows}
704
+ columns={columns}
705
+ checkboxSelection
706
+ rowSelectionModel={selectionModel}
707
+ onRowSelectionModelChange={setSelectionModel}
708
+ filterModel={filterModel}
709
+ onFilterModelChange={setFilterModel}
710
+ slots={{ toolbar: CustomToolbar }}
711
+ />
712
+ </Flexbox>
713
+ </div>
714
+ );
715
+ };