@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,750 @@
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 allRows: 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
+ // -- Filter helpers ----------------------------------------------------------
389
+
390
+ function getSelectedFromFilterModel(filterModel: GridFilterModel, fieldName: string): string[] {
391
+ const item = filterModel.items.find((i) => i.field === fieldName);
392
+ if (!item || item.value === undefined) return [];
393
+ if (item.operator === 'isAnyOf') {
394
+ return Array.isArray(item.value) ? item.value : [item.value];
395
+ }
396
+ return [];
397
+ }
398
+
399
+ function updateFilterModel(filterModel: GridFilterModel, fieldName: string, values: string[]): GridFilterModel {
400
+ const items = filterModel.items.filter((i) => i.field !== fieldName);
401
+ if (values.length > 0) {
402
+ items.push({
403
+ field: fieldName,
404
+ id: Math.floor(Math.random() * 100000),
405
+ operator: 'isAnyOf',
406
+ value: values,
407
+ });
408
+ }
409
+ return { ...filterModel, items };
410
+ }
411
+
412
+ function getActiveBooleanSelection(filterModel: GridFilterModel): boolean | null {
413
+ const item = filterModel.items.find((i) => i.field === 'active');
414
+ if (!item || item.value === undefined) return null;
415
+ if (item.operator === 'is') return item.value === 'true';
416
+ return null;
417
+ }
418
+
419
+ function updateBooleanFilter(filterModel: GridFilterModel, fieldName: string, value: boolean | null): GridFilterModel {
420
+ const items = filterModel.items.filter((i) => i.field !== fieldName);
421
+ if (value !== null) {
422
+ items.push({
423
+ field: fieldName,
424
+ id: Math.floor(Math.random() * 100000),
425
+ operator: 'is',
426
+ value: String(value),
427
+ });
428
+ }
429
+ return { ...filterModel, items };
430
+ }
431
+
432
+ // -- Cross-filtering: apply filterModel to rows client-side -----------------
433
+
434
+ /**
435
+ * Applies the current filterModel to the full row set.
436
+ * Supports: isAnyOf (array match), is (exact match), isNot (exclude match),
437
+ * and boolean 'is' for the active field.
438
+ * Filters for OTHER fields are applied to compute counts for each card,
439
+ * excluding the card's own field — this prevents a card from filtering itself.
440
+ */
441
+ function applyFilters(rows: Row[], filterModel: GridFilterModel, excludeField?: string): Row[] {
442
+ return rows.filter((row) => {
443
+ for (const item of filterModel.items) {
444
+ if (item.field === excludeField) continue;
445
+ if (!item.value || (Array.isArray(item.value) && item.value.length === 0)) continue;
446
+
447
+ const rowValue = row[item.field as keyof Row];
448
+
449
+ switch (item.operator) {
450
+ case 'isAnyOf': {
451
+ const filterValues = Array.isArray(item.value) ? item.value : [item.value];
452
+ if (!filterValues.includes(String(rowValue))) return false;
453
+ break;
454
+ }
455
+ case 'is': {
456
+ if (item.field === 'active') {
457
+ const boolVal = item.value === 'true';
458
+ if (rowValue !== boolVal) return false;
459
+ } else {
460
+ if (String(rowValue) !== String(item.value)) return false;
461
+ }
462
+ break;
463
+ }
464
+ case 'isNot': {
465
+ if (String(rowValue) === String(item.value)) return false;
466
+ break;
467
+ }
468
+ default:
469
+ break;
470
+ }
471
+ }
472
+ return true;
473
+ });
474
+ }
475
+
476
+ /**
477
+ * Compute aggregate counts from a subset of rows.
478
+ */
479
+ function computeCounts(rows: Row[]) {
480
+ const status: Record<string, number> = {};
481
+ const category: Record<string, number> = {};
482
+ let activeCount = 0;
483
+ let inactiveCount = 0;
484
+
485
+ for (const row of rows) {
486
+ status[row.status] = (status[row.status] || 0) + 1;
487
+ category[row.category] = (category[row.category] || 0) + 1;
488
+ if (row.active) activeCount++;
489
+ else inactiveCount++;
490
+ }
491
+
492
+ return { status, category, activeCount, inactiveCount };
493
+ }
494
+
495
+ // -- Columns ----------------------------------------------------------------
496
+
497
+ const columns: GridColDef[] = [
498
+ {
499
+ field: 'id',
500
+ headerName: 'ID',
501
+ width: 100,
502
+ type: 'rsString' as string,
503
+ renderCell: ({ value }: GridRenderCellParams) => <TextCell style={{ fontFamily: 'monospace' }}>{value}</TextCell>,
504
+ },
505
+ {
506
+ field: 'label',
507
+ headerName: 'Label',
508
+ flex: 2,
509
+ type: 'rsString' as string,
510
+ renderCell: ({ value }: GridRenderCellParams) => <TextCell>{value}</TextCell>,
511
+ },
512
+ {
513
+ field: 'active',
514
+ headerName: 'Active',
515
+ width: 80,
516
+ type: 'boolean',
517
+ renderCell: ({ value }: GridRenderCellParams) =>
518
+ value ? (
519
+ <Icon icon={mdiCheck} color="success" size="small" />
520
+ ) : (
521
+ <Icon icon={mdiClose} color="grey" size="small" />
522
+ ),
523
+ },
524
+ {
525
+ field: 'status',
526
+ headerName: 'Status',
527
+ flex: 1,
528
+ type: 'rsSingleSelect' as string,
529
+ valueOptions: [
530
+ { value: 'active', label: 'Active' },
531
+ { value: 'warning', label: 'Warning' },
532
+ { value: 'critical', label: 'Critical' },
533
+ { value: 'expired', label: 'Expired' },
534
+ { value: 'revoked', label: 'Revoked' },
535
+ ],
536
+ renderCell: ({ value }: GridRenderCellParams) => <Pill color={statusColor(value)}>{value}</Pill>,
537
+ } as GridColDef,
538
+ {
539
+ field: 'category',
540
+ headerName: 'Category',
541
+ width: 110,
542
+ type: 'rsSingleSelect' as string,
543
+ valueOptions: [
544
+ { value: 'primary', label: 'Primary' },
545
+ { value: 'secondary', label: 'Secondary' },
546
+ { value: 'tertiary', label: 'Tertiary' },
547
+ ],
548
+ renderCell: ({ value }: GridRenderCellParams) => {
549
+ const cfg = categoryIcon(value);
550
+ return <Icon icon={cfg.icon} color={cfg.color} size="small" />;
551
+ },
552
+ } as GridColDef,
553
+ {
554
+ field: 'score',
555
+ headerName: 'Score',
556
+ width: 80,
557
+ type: 'rsNumber' as string,
558
+ },
559
+ {
560
+ field: 'expiry',
561
+ headerName: 'Expiry',
562
+ width: 180,
563
+ type: 'date',
564
+ valueGetter: (value: unknown) => new Date(value as string),
565
+ renderCell: ({ value }: GridRenderCellParams) => {
566
+ if (!value) return <TextCell>—</TextCell>;
567
+ const d = value as Date;
568
+ const isPast = d < new Date();
569
+ return <TextCell style={isPast ? { color: 'var(--redsift-color-red-n)' } : undefined}>{formatDate(d)}</TextCell>;
570
+ },
571
+ },
572
+ {
573
+ field: 'tags',
574
+ headerName: 'Tags',
575
+ flex: 2,
576
+ type: 'rsMultipleSelect' as string,
577
+ valueOptions: TAG_OPTIONS,
578
+ sortable: false,
579
+ renderCell: ({ value }: GridRenderCellParams) => {
580
+ const arr = value as string[];
581
+ if (!arr || arr.length === 0) return <TextCell>—</TextCell>;
582
+ return (
583
+ <Flexbox gap="4px" flexWrap="wrap" alignItems="center">
584
+ {arr.map((tag: string) => (
585
+ <Pill key={tag} color="blue">
586
+ {tag}
587
+ </Pill>
588
+ ))}
589
+ </Flexbox>
590
+ );
591
+ },
592
+ } as GridColDef,
593
+ {
594
+ field: 'actions',
595
+ headerName: '',
596
+ type: 'actions',
597
+ width: 56,
598
+ hideable: false,
599
+ sortable: false,
600
+ filterable: false,
601
+ renderCell: ({ row }: GridRenderCellParams) => (
602
+ <IconButtonLink
603
+ href="/patterns/crossfiltered-datagrid-page"
604
+ icon={mdiArrowRight}
605
+ color="info"
606
+ aria-label={`View ${row.label}`}
607
+ />
608
+ ),
609
+ },
610
+ ];
611
+
612
+ // -- Main component ---------------------------------------------------------
613
+
614
+ export default () => {
615
+ const [filterModel, setFilterModel] = useState<GridFilterModel>({ items: [] });
616
+ const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
617
+
618
+ // Cross-filtered counts: each card counts rows filtered by ALL OTHER fields
619
+ // (excluding the card's own field). This prevents selecting "active" status
620
+ // from zeroing out "warning", "critical" etc. in the Status card.
621
+ const statusCounts = useMemo(() => computeCounts(applyFilters(allRows, filterModel, 'status')), [filterModel]);
622
+ const categoryCounts = useMemo(() => computeCounts(applyFilters(allRows, filterModel, 'category')), [filterModel]);
623
+ const activeCounts = useMemo(() => computeCounts(applyFilters(allRows, filterModel, 'active')), [filterModel]);
624
+
625
+ // Rows displayed in the DataGrid — all filters applied
626
+ const filteredRows = useMemo(() => applyFilters(allRows, filterModel), [filterModel]);
627
+
628
+ // Derive DataCard selections from filterModel
629
+ const statusSelection = getSelectedFromFilterModel(filterModel, 'status');
630
+ const categorySelection = getSelectedFromFilterModel(filterModel, 'category');
631
+ // DataCard change handlers
632
+ const handleStatusChange = useCallback(
633
+ (values: string[]) => setFilterModel((prev) => updateFilterModel(prev, 'status', values)),
634
+ []
635
+ );
636
+
637
+ const handleCategoryChange = useCallback(
638
+ (values: string[]) => setFilterModel((prev) => updateFilterModel(prev, 'category', values)),
639
+ []
640
+ );
641
+
642
+ const handleActiveToggle = useCallback((value: boolean) => {
643
+ setFilterModel((prev) => {
644
+ const current = getActiveBooleanSelection(prev);
645
+ if (current === value) return updateBooleanFilter(prev, 'active', null);
646
+ return updateBooleanFilter(prev, 'active', value);
647
+ });
648
+ }, []);
649
+
650
+ return (
651
+ <div style={{ width: '100%' }}>
652
+ <Flexbox flexDirection="column" gap="16px">
653
+ {/* DataCard row — cross-filtered summary cards */}
654
+ <Flexbox gap="12px" flexWrap="wrap">
655
+ {/* Status card — counts exclude status filters, include category + active filters */}
656
+ <DataCard color="info" isLoading={false}>
657
+ <DataCard.Header icon={mdiCheckCircleOutline} title="Status" />
658
+ <DataCard.Listbox
659
+ selectionMode="multiple"
660
+ values={statusSelection}
661
+ onChange={handleStatusChange}
662
+ aria-label="Filter by status"
663
+ >
664
+ {STATUS_VALUES.map((s) => (
665
+ <DrilldownItem
666
+ key={s}
667
+ value={statusCounts.status[s] || 0}
668
+ legend={s}
669
+ hasFilterIcon
670
+ labelDecorator={(l) => l.charAt(0).toUpperCase() + l.slice(1)}
671
+ />
672
+ ))}
673
+ </DataCard.Listbox>
674
+ </DataCard>
675
+
676
+ {/* Category card — counts exclude category filters, include status + active filters */}
677
+ <DataCard color="warning" isLoading={false}>
678
+ <DataCard.Header icon={mdiShapeOutline} title="Category" />
679
+ <DataCard.Listbox
680
+ selectionMode="multiple"
681
+ values={categorySelection}
682
+ onChange={handleCategoryChange}
683
+ aria-label="Filter by category"
684
+ >
685
+ {CATEGORY_VALUES.map((c) => (
686
+ <DrilldownItem
687
+ key={c}
688
+ value={categoryCounts.category[c] || 0}
689
+ legend={c}
690
+ hasFilterIcon
691
+ labelDecorator={(l) => l.charAt(0).toUpperCase() + l.slice(1)}
692
+ />
693
+ ))}
694
+ </DataCard.Listbox>
695
+ </DataCard>
696
+
697
+ {/* Active card — counts exclude active filters, include status + category filters */}
698
+ <DataCard color="success" isLoading={false}>
699
+ <DataCard.Header icon={mdiToggleSwitch} title="Active" />
700
+ <DataCard.Body>
701
+ <DrilldownItem
702
+ value={activeCounts.activeCount}
703
+ legend="Active"
704
+ hasFilterIcon
705
+ onClick={() => handleActiveToggle(true)}
706
+ size="medium"
707
+ />
708
+ <DrilldownItem
709
+ value={activeCounts.inactiveCount}
710
+ legend="Inactive"
711
+ hasFilterIcon
712
+ onClick={() => handleActiveToggle(false)}
713
+ size="medium"
714
+ />
715
+ </DataCard.Body>
716
+ </DataCard>
717
+ </Flexbox>
718
+
719
+ {/* Bulk actions bar */}
720
+ {selectionModel.length > 0 && (
721
+ <Flexbox gap="8px" alignItems="center" style={{ padding: '8px 0' }}>
722
+ <Pill color="info">{selectionModel.length} selected</Pill>
723
+ <Button variant="secondary" onClick={() => console.log('Selected:', selectionModel)}>
724
+ Log Selection
725
+ </Button>
726
+ <Button variant="secondary" color="error" onClick={() => console.log('Delete:', selectionModel)}>
727
+ Delete
728
+ </Button>
729
+ </Flexbox>
730
+ )}
731
+
732
+ {/* DataGrid with controlled filterModel — uses pre-filtered rows */}
733
+ <DataGrid
734
+ autoHeight
735
+ pagination
736
+ paginationModel={{ page: 0, pageSize: 10 }}
737
+ pageSizeOptions={[10, 25, 50]}
738
+ rows={filteredRows}
739
+ columns={columns}
740
+ checkboxSelection
741
+ rowSelectionModel={selectionModel}
742
+ onRowSelectionModelChange={setSelectionModel}
743
+ filterModel={filterModel}
744
+ onFilterModelChange={setFilterModel}
745
+ slots={{ toolbar: CustomToolbar }}
746
+ />
747
+ </Flexbox>
748
+ </div>
749
+ );
750
+ };