@peak-ai/canvas 1.4.21 → 1.4.22

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 (213) hide show
  1. package/.babelrc +14 -0
  2. package/.eslintcache +1 -0
  3. package/.eslintignore +5 -0
  4. package/.eslintrc.js +29 -0
  5. package/{GrapesjsCanvas.js → dist/GrapesjsCanvas.js} +3 -2
  6. package/dist/GrapesjsCanvas.js.map +1 -0
  7. package/dist/package.json +62 -0
  8. package/{plugins → dist/plugins}/helpers/render-components.js +1 -1
  9. package/dist/plugins/helpers/render-components.js.map +1 -0
  10. package/dist/shadcn/components/ui/error-wrapper.js +2 -0
  11. package/dist/shadcn/components/ui/error-wrapper.js.map +1 -0
  12. package/package.json +45 -7
  13. package/scripts/build.ts +120 -0
  14. package/src/GrapesjsCanvas.tsx +494 -0
  15. package/src/constants/index.ts +25 -0
  16. package/src/declaration.d.ts +1 -0
  17. package/src/helpers/compiled-table.css +2429 -0
  18. package/src/helpers/css.ts +2667 -0
  19. package/src/helpers/date-picker.ts +807 -0
  20. package/src/helpers/filter-placeholder.ts +18 -0
  21. package/src/helpers/index.ts +13 -0
  22. package/src/helpers/merge-json.ts +106 -0
  23. package/src/index.styles.ts +58 -0
  24. package/src/index.ts +9 -0
  25. package/src/plugins/grapejs-plugin.tsx +196 -0
  26. package/src/plugins/helpers/custom-modal.tsx +123 -0
  27. package/src/plugins/helpers/data-table.tsx +300 -0
  28. package/src/plugins/helpers/extra.tsx +164 -0
  29. package/src/plugins/helpers/query-cache-context.tsx +154 -0
  30. package/src/plugins/helpers/query-cache-singleton.ts +176 -0
  31. package/src/plugins/helpers/query-cache-utils.ts +226 -0
  32. package/src/plugins/helpers/query-details-modal.tsx +400 -0
  33. package/src/plugins/helpers/query-heading-formatter.ts +24 -0
  34. package/src/plugins/helpers/query-loading-modal.tsx +94 -0
  35. package/src/plugins/helpers/render-components.tsx +1450 -0
  36. package/src/plugins/helpers/styled-info-button.tsx +504 -0
  37. package/src/public/canvas.css +42 -0
  38. package/src/public/components-css/table/table-output.css +2436 -0
  39. package/src/public/components-css/table/table.css +30 -0
  40. package/src/public/output.css +2465 -0
  41. package/src/public/table.css +135 -0
  42. package/src/shadcn/components/icons/AiAvatarIcon.tsx +47 -0
  43. package/src/shadcn/components/icons/Co_driver Expanding button copy.svg +21 -0
  44. package/src/shadcn/components/icons/ai-avatar.svg +7 -0
  45. package/src/shadcn/components/icons/thinking.gif +0 -0
  46. package/src/shadcn/components/ui/button.tsx +132 -0
  47. package/src/shadcn/components/ui/card.tsx +92 -0
  48. package/src/shadcn/components/ui/chart.tsx +324 -0
  49. package/src/shadcn/components/ui/checkbox.tsx +27 -0
  50. package/src/shadcn/components/ui/component-wrapper.tsx +61 -0
  51. package/src/shadcn/components/ui/date-filter.tsx +816 -0
  52. package/src/shadcn/components/ui/error-container.tsx +125 -0
  53. package/src/shadcn/components/ui/error-wrapper.tsx +99 -0
  54. package/src/shadcn/components/ui/filter.tsx +368 -0
  55. package/src/shadcn/components/ui/hover-card.tsx +36 -0
  56. package/src/shadcn/components/ui/input.tsx +20 -0
  57. package/src/shadcn/components/ui/label.tsx +24 -0
  58. package/src/shadcn/components/ui/pagination.tsx +213 -0
  59. package/src/shadcn/components/ui/scroll-area.tsx +59 -0
  60. package/src/shadcn/components/ui/search.tsx +150 -0
  61. package/src/shadcn/components/ui/separator.tsx +26 -0
  62. package/src/shadcn/components/ui/skeleton.tsx +69 -0
  63. package/src/shadcn/components/ui/table.tsx +196 -0
  64. package/src/shadcn/components/ui/tabs.tsx +55 -0
  65. package/src/shadcn/components/ui/textarea.tsx +18 -0
  66. package/src/shadcn/components/ui/tooltip.tsx +87 -0
  67. package/src/shadcn/utils.ts +6 -0
  68. package/src/types/grapesjs-tailwind.d.ts +61 -0
  69. package/src/types/images.d.ts +1 -0
  70. package/tailwind.config.js +5 -0
  71. package/tooling/tailwind-compiler/index.js +99 -0
  72. package/tooling/tailwind-compiler/package.json +11 -0
  73. package/tooling/tailwind-compiler/yarn.lock +123 -0
  74. package/tsconfig.build.json +15 -0
  75. package/tsconfig.json +8 -0
  76. package/GrapesjsCanvas.js.map +0 -1
  77. package/plugins/helpers/render-components.js.map +0 -1
  78. package/shadcn/components/ui/error-wrapper.js +0 -2
  79. package/shadcn/components/ui/error-wrapper.js.map +0 -1
  80. /package/{GrapesjsCanvas.d.ts → dist/GrapesjsCanvas.d.ts} +0 -0
  81. /package/{constants → dist/constants}/index.d.ts +0 -0
  82. /package/{constants → dist/constants}/index.js +0 -0
  83. /package/{constants → dist/constants}/index.js.map +0 -0
  84. /package/{declaration.d.js → dist/declaration.d.js} +0 -0
  85. /package/{declaration.d.js.map → dist/declaration.d.js.map} +0 -0
  86. /package/{helpers → dist/helpers}/compiled-table.css +0 -0
  87. /package/{helpers → dist/helpers}/css.d.ts +0 -0
  88. /package/{helpers → dist/helpers}/css.js +0 -0
  89. /package/{helpers → dist/helpers}/css.js.map +0 -0
  90. /package/{helpers → dist/helpers}/date-picker.d.ts +0 -0
  91. /package/{helpers → dist/helpers}/date-picker.js +0 -0
  92. /package/{helpers → dist/helpers}/date-picker.js.map +0 -0
  93. /package/{helpers → dist/helpers}/filter-placeholder.d.ts +0 -0
  94. /package/{helpers → dist/helpers}/filter-placeholder.js +0 -0
  95. /package/{helpers → dist/helpers}/filter-placeholder.js.map +0 -0
  96. /package/{helpers → dist/helpers}/index.d.ts +0 -0
  97. /package/{helpers → dist/helpers}/index.js +0 -0
  98. /package/{helpers → dist/helpers}/index.js.map +0 -0
  99. /package/{helpers → dist/helpers}/merge-json.d.ts +0 -0
  100. /package/{helpers → dist/helpers}/merge-json.js +0 -0
  101. /package/{helpers → dist/helpers}/merge-json.js.map +0 -0
  102. /package/{index.d.ts → dist/index.d.ts} +0 -0
  103. /package/{index.js → dist/index.js} +0 -0
  104. /package/{index.js.map → dist/index.js.map} +0 -0
  105. /package/{index.styles.d.ts → dist/index.styles.d.ts} +0 -0
  106. /package/{index.styles.js → dist/index.styles.js} +0 -0
  107. /package/{index.styles.js.map → dist/index.styles.js.map} +0 -0
  108. /package/{plugins → dist/plugins}/grapejs-plugin.d.ts +0 -0
  109. /package/{plugins → dist/plugins}/grapejs-plugin.js +0 -0
  110. /package/{plugins → dist/plugins}/grapejs-plugin.js.map +0 -0
  111. /package/{plugins → dist/plugins}/helpers/custom-modal.d.ts +0 -0
  112. /package/{plugins → dist/plugins}/helpers/custom-modal.js +0 -0
  113. /package/{plugins → dist/plugins}/helpers/custom-modal.js.map +0 -0
  114. /package/{plugins → dist/plugins}/helpers/data-table.d.ts +0 -0
  115. /package/{plugins → dist/plugins}/helpers/data-table.js +0 -0
  116. /package/{plugins → dist/plugins}/helpers/data-table.js.map +0 -0
  117. /package/{plugins → dist/plugins}/helpers/extra.d.ts +0 -0
  118. /package/{plugins → dist/plugins}/helpers/extra.js +0 -0
  119. /package/{plugins → dist/plugins}/helpers/extra.js.map +0 -0
  120. /package/{plugins → dist/plugins}/helpers/query-cache-context.d.ts +0 -0
  121. /package/{plugins → dist/plugins}/helpers/query-cache-context.js +0 -0
  122. /package/{plugins → dist/plugins}/helpers/query-cache-context.js.map +0 -0
  123. /package/{plugins → dist/plugins}/helpers/query-cache-singleton.d.ts +0 -0
  124. /package/{plugins → dist/plugins}/helpers/query-cache-singleton.js +0 -0
  125. /package/{plugins → dist/plugins}/helpers/query-cache-singleton.js.map +0 -0
  126. /package/{plugins → dist/plugins}/helpers/query-cache-utils.d.ts +0 -0
  127. /package/{plugins → dist/plugins}/helpers/query-cache-utils.js +0 -0
  128. /package/{plugins → dist/plugins}/helpers/query-cache-utils.js.map +0 -0
  129. /package/{plugins → dist/plugins}/helpers/query-details-modal.d.ts +0 -0
  130. /package/{plugins → dist/plugins}/helpers/query-details-modal.js +0 -0
  131. /package/{plugins → dist/plugins}/helpers/query-details-modal.js.map +0 -0
  132. /package/{plugins → dist/plugins}/helpers/query-heading-formatter.d.ts +0 -0
  133. /package/{plugins → dist/plugins}/helpers/query-heading-formatter.js +0 -0
  134. /package/{plugins → dist/plugins}/helpers/query-heading-formatter.js.map +0 -0
  135. /package/{plugins → dist/plugins}/helpers/query-loading-modal.d.ts +0 -0
  136. /package/{plugins → dist/plugins}/helpers/query-loading-modal.js +0 -0
  137. /package/{plugins → dist/plugins}/helpers/query-loading-modal.js.map +0 -0
  138. /package/{plugins → dist/plugins}/helpers/render-components.d.ts +0 -0
  139. /package/{plugins → dist/plugins}/helpers/styled-info-button.d.ts +0 -0
  140. /package/{plugins → dist/plugins}/helpers/styled-info-button.js +0 -0
  141. /package/{plugins → dist/plugins}/helpers/styled-info-button.js.map +0 -0
  142. /package/{shadcn → dist/shadcn}/components/icons/AiAvatarIcon.d.ts +0 -0
  143. /package/{shadcn → dist/shadcn}/components/icons/AiAvatarIcon.js +0 -0
  144. /package/{shadcn → dist/shadcn}/components/icons/AiAvatarIcon.js.map +0 -0
  145. /package/{shadcn → dist/shadcn}/components/icons/thinking.gif +0 -0
  146. /package/{shadcn → dist/shadcn}/components/ui/button.d.ts +0 -0
  147. /package/{shadcn → dist/shadcn}/components/ui/button.js +0 -0
  148. /package/{shadcn → dist/shadcn}/components/ui/button.js.map +0 -0
  149. /package/{shadcn → dist/shadcn}/components/ui/card.d.ts +0 -0
  150. /package/{shadcn → dist/shadcn}/components/ui/card.js +0 -0
  151. /package/{shadcn → dist/shadcn}/components/ui/card.js.map +0 -0
  152. /package/{shadcn → dist/shadcn}/components/ui/chart.d.ts +0 -0
  153. /package/{shadcn → dist/shadcn}/components/ui/chart.js +0 -0
  154. /package/{shadcn → dist/shadcn}/components/ui/chart.js.map +0 -0
  155. /package/{shadcn → dist/shadcn}/components/ui/checkbox.d.ts +0 -0
  156. /package/{shadcn → dist/shadcn}/components/ui/checkbox.js +0 -0
  157. /package/{shadcn → dist/shadcn}/components/ui/checkbox.js.map +0 -0
  158. /package/{shadcn → dist/shadcn}/components/ui/component-wrapper.d.ts +0 -0
  159. /package/{shadcn → dist/shadcn}/components/ui/component-wrapper.js +0 -0
  160. /package/{shadcn → dist/shadcn}/components/ui/component-wrapper.js.map +0 -0
  161. /package/{shadcn → dist/shadcn}/components/ui/date-filter.d.ts +0 -0
  162. /package/{shadcn → dist/shadcn}/components/ui/date-filter.js +0 -0
  163. /package/{shadcn → dist/shadcn}/components/ui/date-filter.js.map +0 -0
  164. /package/{shadcn → dist/shadcn}/components/ui/error-container.d.ts +0 -0
  165. /package/{shadcn → dist/shadcn}/components/ui/error-container.js +0 -0
  166. /package/{shadcn → dist/shadcn}/components/ui/error-container.js.map +0 -0
  167. /package/{shadcn → dist/shadcn}/components/ui/error-wrapper.d.ts +0 -0
  168. /package/{shadcn → dist/shadcn}/components/ui/filter.d.ts +0 -0
  169. /package/{shadcn → dist/shadcn}/components/ui/filter.js +0 -0
  170. /package/{shadcn → dist/shadcn}/components/ui/filter.js.map +0 -0
  171. /package/{shadcn → dist/shadcn}/components/ui/hover-card.d.ts +0 -0
  172. /package/{shadcn → dist/shadcn}/components/ui/hover-card.js +0 -0
  173. /package/{shadcn → dist/shadcn}/components/ui/hover-card.js.map +0 -0
  174. /package/{shadcn → dist/shadcn}/components/ui/input.d.ts +0 -0
  175. /package/{shadcn → dist/shadcn}/components/ui/input.js +0 -0
  176. /package/{shadcn → dist/shadcn}/components/ui/input.js.map +0 -0
  177. /package/{shadcn → dist/shadcn}/components/ui/label.d.ts +0 -0
  178. /package/{shadcn → dist/shadcn}/components/ui/label.js +0 -0
  179. /package/{shadcn → dist/shadcn}/components/ui/label.js.map +0 -0
  180. /package/{shadcn → dist/shadcn}/components/ui/pagination.d.ts +0 -0
  181. /package/{shadcn → dist/shadcn}/components/ui/pagination.js +0 -0
  182. /package/{shadcn → dist/shadcn}/components/ui/pagination.js.map +0 -0
  183. /package/{shadcn → dist/shadcn}/components/ui/scroll-area.d.ts +0 -0
  184. /package/{shadcn → dist/shadcn}/components/ui/scroll-area.js +0 -0
  185. /package/{shadcn → dist/shadcn}/components/ui/scroll-area.js.map +0 -0
  186. /package/{shadcn → dist/shadcn}/components/ui/search.d.ts +0 -0
  187. /package/{shadcn → dist/shadcn}/components/ui/search.js +0 -0
  188. /package/{shadcn → dist/shadcn}/components/ui/search.js.map +0 -0
  189. /package/{shadcn → dist/shadcn}/components/ui/separator.d.ts +0 -0
  190. /package/{shadcn → dist/shadcn}/components/ui/separator.js +0 -0
  191. /package/{shadcn → dist/shadcn}/components/ui/separator.js.map +0 -0
  192. /package/{shadcn → dist/shadcn}/components/ui/skeleton.d.ts +0 -0
  193. /package/{shadcn → dist/shadcn}/components/ui/skeleton.js +0 -0
  194. /package/{shadcn → dist/shadcn}/components/ui/skeleton.js.map +0 -0
  195. /package/{shadcn → dist/shadcn}/components/ui/table.d.ts +0 -0
  196. /package/{shadcn → dist/shadcn}/components/ui/table.js +0 -0
  197. /package/{shadcn → dist/shadcn}/components/ui/table.js.map +0 -0
  198. /package/{shadcn → dist/shadcn}/components/ui/tabs.d.ts +0 -0
  199. /package/{shadcn → dist/shadcn}/components/ui/tabs.js +0 -0
  200. /package/{shadcn → dist/shadcn}/components/ui/tabs.js.map +0 -0
  201. /package/{shadcn → dist/shadcn}/components/ui/textarea.d.ts +0 -0
  202. /package/{shadcn → dist/shadcn}/components/ui/textarea.js +0 -0
  203. /package/{shadcn → dist/shadcn}/components/ui/textarea.js.map +0 -0
  204. /package/{shadcn → dist/shadcn}/components/ui/tooltip.d.ts +0 -0
  205. /package/{shadcn → dist/shadcn}/components/ui/tooltip.js +0 -0
  206. /package/{shadcn → dist/shadcn}/components/ui/tooltip.js.map +0 -0
  207. /package/{shadcn → dist/shadcn}/utils.d.ts +0 -0
  208. /package/{shadcn → dist/shadcn}/utils.js +0 -0
  209. /package/{shadcn → dist/shadcn}/utils.js.map +0 -0
  210. /package/{types → dist/types}/grapesjs-tailwind.d.js +0 -0
  211. /package/{types → dist/types}/grapesjs-tailwind.d.js.map +0 -0
  212. /package/{types → dist/types}/images.d.js +0 -0
  213. /package/{types → dist/types}/images.d.js.map +0 -0
@@ -0,0 +1,300 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { useEffect, useState, Fragment } from 'react';
3
+ import {
4
+ Table,
5
+ TableHeader,
6
+ TableRow,
7
+ TableHead,
8
+ TableBody,
9
+ TableCell,
10
+ TableHeadSortable,
11
+ SortDirection,
12
+ } from '../../shadcn/components/ui/table';
13
+ import { TableLoader } from '../../shadcn/components/ui/skeleton';
14
+ import { ScrollArea, ScrollBar } from '../../shadcn/components/ui/scroll-area';
15
+ import {
16
+ Pagination,
17
+ PaginationContent,
18
+ renderPaginationArrows,
19
+ } from '../../shadcn/components/ui/pagination';
20
+ import { TableButton } from '../../shadcn/components/ui/button';
21
+ import { cn } from '../../shadcn/utils';
22
+ import { renderNoDataFallback } from './extra'
23
+
24
+ type RowAction = {
25
+ id: string;
26
+ text: string;
27
+ variant?: string;
28
+ tooltipContent?: string;
29
+ className?: string;
30
+ };
31
+
32
+ export type DataTableProps = {
33
+ data: Record<string, any>[];
34
+ headerMapper?: Record<string, string>;
35
+ className?: string;
36
+ isEditable?: boolean;
37
+ actions?: RowAction[];
38
+ pagination?: {
39
+ values?: {
40
+ pageNumber: number;
41
+ pageSize: number;
42
+ };
43
+ initialPage?: number;
44
+ initialPageSize?: number;
45
+ totalCount: number;
46
+ pageSizeOptions: number[];
47
+ };
48
+ isLoading?: boolean;
49
+ onRowAction?: (rowIndex: number, actionId: string) => void;
50
+ onPageChange?: (pageNumber: number, pageSize: number) => void;
51
+ onSort?: (column: string, direction: SortDirection) => void;
52
+ sortConfig?: Record<string, SortDirection>;
53
+ otherProps?: Record<string, any>;
54
+ height?: string | number;
55
+ };
56
+
57
+ export function DataTable({
58
+ data = [],
59
+ headerMapper = {},
60
+ className = '',
61
+ actions = [],
62
+ isEditable = false,
63
+ pagination,
64
+ isLoading = false,
65
+ onRowAction,
66
+ onPageChange,
67
+ onSort,
68
+ sortConfig,
69
+ otherProps = {},
70
+ height,
71
+ }: DataTableProps) {
72
+
73
+ if (!data || data.length === 0) {
74
+ return (
75
+ <Fragment>
76
+ <div className="flex flex-col overflow-hidden">
77
+ <div
78
+ data-slot="table-container"
79
+ className="aspect-auto w-full"
80
+ style={{ height: 'auto', minHeight: '180px' }}
81
+ >
82
+ {isLoading ? <TableLoader /> : renderNoDataFallback()}
83
+ </div>
84
+ </div>
85
+ </Fragment>
86
+ );
87
+ }
88
+
89
+ const [activeSortColumn, setActiveSortColumn] = useState<string | null>(null);
90
+ const [sortDirections, setSortDirections] = useState<Record<string, SortDirection>>({});
91
+
92
+ const [pageNumber, setPageNumber] = useState(
93
+ Number(pagination?.values?.pageNumber ?? pagination?.initialPage ?? 1),
94
+ );
95
+ const [pageSize, setPageSize] = useState(
96
+ Number(pagination?.values?.pageSize ?? pagination?.initialPageSize ?? 10),
97
+ );
98
+ const [totalPages, setTotalPages] = useState(
99
+ Math.ceil((pagination?.totalCount || 0) / pageSize) || 1,
100
+ );
101
+
102
+ useEffect(() => {
103
+ setPageNumber(Number(pagination?.values?.pageNumber ?? pagination?.initialPage ?? 1));
104
+ setPageSize(Number(pagination?.values?.pageSize ?? pagination?.initialPageSize ?? 25));
105
+ }, [pagination]);
106
+
107
+ useEffect(() => {
108
+ setTotalPages(Math.ceil((pagination?.totalCount || 0) / pageSize) || 1);
109
+ }, [pagination?.totalCount, pageSize]);
110
+
111
+ useEffect(() => {
112
+ if (sortConfig) {
113
+ setSortDirections(sortConfig);
114
+ const first = Object.keys(sortConfig)[0];
115
+
116
+ if (first) {
117
+ setActiveSortColumn(first);
118
+ }
119
+ }
120
+ }, [sortConfig]);
121
+
122
+ useEffect(() => {
123
+ if (sortConfig?.sortColumn || sortConfig?.sortDirection) {
124
+ sortConfig.sortColumn = null;
125
+ sortConfig.sortDirection = null;
126
+ }
127
+
128
+ setActiveSortColumn(null);
129
+ setSortDirections({});
130
+ }, []);
131
+
132
+ const originalKeys = Object.keys(data[0] || {}).filter((key) => key in headerMapper);
133
+ const headers = originalKeys.map((key) => headerMapper[key]);
134
+
135
+ if (actions.length > 0) {
136
+ headers.push('');
137
+ }
138
+
139
+ const rows = data.map((datum: any) => originalKeys.map((key) => datum[key]));
140
+
141
+ async function handlePageChange(newPage = pageNumber, newSize = pageSize) {
142
+ setPageNumber(newPage);
143
+ setPageSize(newSize);
144
+ onPageChange?.(newPage, newSize);
145
+ }
146
+
147
+ function handleSortClick(column: string, direction: SortDirection) {
148
+ setActiveSortColumn(direction ? column : null);
149
+ setSortDirections((prev) => ({
150
+ ...prev,
151
+ [column]: direction,
152
+ }));
153
+ onSort?.(column, direction);
154
+ }
155
+
156
+ function renderActions(rowIndex: number, rowActions: RowAction[]) {
157
+ if (!actions.length) {
158
+ return null;
159
+ }
160
+
161
+ return (
162
+ <TableCell className="sticky right-0 bg-white">
163
+ {rowActions.map((action) => (
164
+ <TableButton
165
+ key={action.id}
166
+ className={action.className}
167
+ isEditable={isEditable}
168
+ onClick={() => onRowAction?.(rowIndex, action.id)}
169
+ variant={action.variant}
170
+ text={action.text}
171
+ tooltipContent={action.tooltipContent}
172
+ disabled={isLoading}
173
+ />
174
+ ))}
175
+ </TableCell>
176
+ );
177
+ }
178
+
179
+ const defaultClasses = `aspect-auto w-full`;
180
+ const mergedClasses = className ? cn(defaultClasses, className) : defaultClasses;
181
+
182
+ function renderTableComponent() {
183
+ if (isLoading || (!data || data.length === 0)) {
184
+ return <TableLoader />;
185
+ }
186
+
187
+ return (
188
+ <Table
189
+ {...otherProps}
190
+ className={`caption-bottom text-sm relative ${mergedClasses}`}
191
+ data-slot="table"
192
+ >
193
+ <TableHeader data-slot="table-header" className="sticky top-0 z-2">
194
+ <TableRow>
195
+ {headers.map((head: string, idx: number) => {
196
+ const isActionColumn = actions.length > 0 && idx === headers.length - 1;
197
+ const originalKey = originalKeys[idx];
198
+
199
+ if (isActionColumn) {
200
+ return (
201
+ <TableHead
202
+ key={idx}
203
+ data-slot={`table-header-${idx}`}
204
+ contentEditable={isEditable}
205
+ className="sticky right-0 bg-slate-100"
206
+ >
207
+ {head}
208
+ </TableHead>
209
+ );
210
+ }
211
+
212
+ return (
213
+ <TableHeadSortable
214
+ key={idx}
215
+ data-slot={`table-header-${idx}`}
216
+ sortDirection={sortDirections[originalKey] || 'asc'}
217
+ isActive={activeSortColumn === originalKey}
218
+ onSort={(direction) => handleSortClick(originalKey, direction)}
219
+ sortable={!isLoading}
220
+ isEditable={isEditable}
221
+ >
222
+ {head}
223
+ </TableHeadSortable>
224
+ );
225
+ })}
226
+ </TableRow>
227
+ </TableHeader>
228
+ <TableBody data-slot="table-body">
229
+ {rows.map((row: any[], rIdx: number) => (
230
+ <TableRow
231
+ key={rIdx}
232
+ data-slot={`row-${rIdx}`}
233
+ className="hover:bg-muted/50 border-b transition-colors"
234
+ >
235
+ {row.map((cell: string, cIdx: number) => (
236
+ <TableCell
237
+ key={cIdx}
238
+ data-slot={`cell-${rIdx}-${cIdx}`}
239
+ className="p-2 align-middle whitespace-nowrap"
240
+ contentEditable={false}
241
+ >
242
+ {cell === null ? '-' : String(cell)}
243
+ </TableCell>
244
+ ))}
245
+ {renderActions(rIdx, actions)}
246
+ </TableRow>
247
+ ))}
248
+ </TableBody>
249
+ </Table>
250
+ );
251
+ }
252
+
253
+ return (
254
+ <Fragment>
255
+ <div className="flex flex-col h-full overflow-hidden">
256
+ <ScrollArea
257
+ data-slot="table-container"
258
+ className={mergedClasses}
259
+ {...(height !== undefined && { style: { height } })}
260
+ >
261
+ {renderTableComponent()}
262
+ <ScrollBar orientation="horizontal" />
263
+ </ScrollArea>
264
+ </div>
265
+ {pagination && pagination.totalCount > 0 && (
266
+ <div className="flex-shrink-0 flex items-center justify-between mt-2 w-full overflow-x-auto border-t pt-2">
267
+ <div className="flex items-center gap-2 flex-shrink-0">
268
+ <label htmlFor="page-size-select" className="text-sm whitespace-nowrap">
269
+ Rows per page:
270
+ </label>
271
+ <select
272
+ id="page-size-select"
273
+ value={pageSize}
274
+ onChange={(e) => handlePageChange(1, Number(e.target.value))}
275
+ className="border rounded px-2 py-1 text-sm cursor-pointer"
276
+ disabled={isLoading}
277
+ >
278
+ {pagination.pageSizeOptions.map((size: number) => (
279
+ <option key={size} value={size}>
280
+ {size}
281
+ </option>
282
+ ))}
283
+ </select>
284
+ </div>
285
+ <div className="flex items-center gap-1 flex-shrink-0">
286
+ <span className="text-xs text-muted-foreground whitespace-nowrap ml-1.5">
287
+ {(pageNumber - 1) * pageSize + 1}–
288
+ {Math.min(pageNumber * pageSize, pagination.totalCount)} of {pagination.totalCount}
289
+ </span>
290
+ <Pagination>
291
+ <PaginationContent className="gap-0.2 flex-shrink-0">
292
+ {renderPaginationArrows(pageNumber, totalPages, isLoading, handlePageChange)}
293
+ </PaginationContent>
294
+ </Pagination>
295
+ </div>
296
+ </div>
297
+ )}
298
+ </Fragment>
299
+ );
300
+ }
@@ -0,0 +1,164 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { JSX } from 'react';
3
+
4
+ import { cn } from '../../shadcn/utils';
5
+ import { Checkbox } from '../../shadcn/components/ui/checkbox';
6
+ import { CircleAlert } from 'lucide-react';
7
+ import {
8
+ HoverCard,
9
+ HoverCardTrigger,
10
+ HoverCardContent,
11
+ } from '../../shadcn/components/ui/hover-card';
12
+ import { Input } from '../../shadcn/components/ui/input';
13
+ import { Label } from '../../shadcn/components/ui/label';
14
+ import { Separator } from '../../shadcn/components/ui/separator';
15
+ import { Textarea } from '../../shadcn/components/ui/textarea';
16
+ import { Tooltip, TooltipTrigger, TooltipContent } from '../../shadcn/components/ui/tooltip';
17
+
18
+ export function renderNoDataFallback() {
19
+ return (
20
+ <div className="w-full py-8 flex flex-col items-center justify-center px-6 text-center" style={{ minHeight: '180px' }}>
21
+ <CircleAlert className="w-12 h-12 text-gray-400 mb-2" />
22
+ <h3 className="text-lg font-medium text-gray-900">No data available</h3>
23
+ <p className="mt-2 text-sm text-gray-500 max-w-md">
24
+ Check your filters or try a different time range.
25
+ </p>
26
+ </div>
27
+ );
28
+ }
29
+
30
+ export function renderCheckbox(props: any): JSX.Element {
31
+ const { label = '', defaultChecked = false, className, isEditable = false, ...rest } = props;
32
+ const defaultClasses = 'flex items-center';
33
+ const merged = className ? cn(defaultClasses, className) : defaultClasses;
34
+
35
+ return (
36
+ <div className={merged} {...rest}>
37
+ <Checkbox defaultChecked={defaultChecked} />
38
+ {label && (
39
+ <Label data-slot="label" contentEditable={isEditable} className="ml-2">
40
+ {label}
41
+ </Label>
42
+ )}
43
+ </div>
44
+ );
45
+ }
46
+
47
+ export function renderHoverCard(props: any): JSX.Element {
48
+ const {
49
+ triggerContent = 'Hover over me',
50
+ cardContent = 'Additional information',
51
+ className,
52
+ isEditable = false,
53
+ ...rest
54
+ } = props;
55
+ const defaultClasses = 'w-[320px] max-w-[320px] shadow rounded-lg';
56
+ const merged = className ? cn(defaultClasses, className) : defaultClasses;
57
+
58
+ return (
59
+ <HoverCard className={merged} {...rest}>
60
+ <HoverCardTrigger asChild>
61
+ <span data-slot="triggerContent" contentEditable={isEditable}>
62
+ {triggerContent}
63
+ </span>
64
+ </HoverCardTrigger>
65
+ <HoverCardContent>
66
+ <div data-slot="cardContent" contentEditable={isEditable}>
67
+ {cardContent}
68
+ </div>
69
+ </HoverCardContent>
70
+ </HoverCard>
71
+ );
72
+ }
73
+
74
+ export function renderInput(props: any): JSX.Element {
75
+ const {
76
+ label = '',
77
+ placeholder = '',
78
+ value = '',
79
+ className,
80
+ isEditable = false,
81
+ ...rest
82
+ } = props;
83
+ const defaultClasses = 'w-[320px] p-2 border border-gray-300 rounded-md';
84
+ const merged = className ? cn(defaultClasses, className) : defaultClasses;
85
+
86
+ return (
87
+ <div className={`flex flex-col ${merged}`} {...rest}>
88
+ {label && (
89
+ <Label data-slot="label" contentEditable={isEditable} className="mb-1">
90
+ {label}
91
+ </Label>
92
+ )}
93
+ <Input placeholder={placeholder} value={value} />
94
+ </div>
95
+ );
96
+ }
97
+
98
+ export function renderLabel(props: any): JSX.Element {
99
+ const { text = 'Label', className, isEditable = false, ...rest } = props;
100
+ const defaultClasses = 'text-base font-semibold mb-2';
101
+ const merged = className ? cn(defaultClasses, className) : defaultClasses;
102
+
103
+ return (
104
+ <Label data-slot="text" contentEditable={isEditable} className={merged} {...rest}>
105
+ {text}
106
+ </Label>
107
+ );
108
+ }
109
+
110
+ export function renderSeparator(props: any): JSX.Element {
111
+ const { className, ...rest } = props;
112
+ const defaultClasses = 'my-4 border-t-2';
113
+ const merged = className ? cn(defaultClasses, className) : defaultClasses;
114
+
115
+ return <Separator className={merged} {...rest} />;
116
+ }
117
+
118
+ export function renderTextarea(props: any): JSX.Element {
119
+ const {
120
+ label = '',
121
+ placeholder = '',
122
+ value = '',
123
+ className,
124
+ isEditable = false,
125
+ ...rest
126
+ } = props;
127
+ const defaultClasses = 'w-[320px] p-2 border border-gray-300 rounded-md';
128
+ const merged = className ? cn(defaultClasses, className) : defaultClasses;
129
+
130
+ return (
131
+ <div className={`flex flex-col ${merged}`} {...rest}>
132
+ {label && (
133
+ <Label data-slot="label" contentEditable={isEditable} className="mb-1">
134
+ {label}
135
+ </Label>
136
+ )}
137
+ <Textarea placeholder={placeholder} value={value} />
138
+ </div>
139
+ );
140
+ }
141
+
142
+ export function renderTooltip(props: any): JSX.Element {
143
+ const {
144
+ triggerContent = 'Hover here',
145
+ tooltipContent = 'Tooltip information',
146
+ className,
147
+ isEditable = false,
148
+ ...rest
149
+ } = props;
150
+ const merged = className || '';
151
+
152
+ return (
153
+ <Tooltip className={merged} {...rest}>
154
+ <TooltipTrigger asChild>
155
+ <span data-slot="triggerContent" contentEditable={isEditable}>
156
+ {triggerContent}
157
+ </span>
158
+ </TooltipTrigger>
159
+ <TooltipContent data-slot="tooltipContent" contentEditable={isEditable}>
160
+ {tooltipContent}
161
+ </TooltipContent>
162
+ </Tooltip>
163
+ );
164
+ }
@@ -0,0 +1,154 @@
1
+ import React, { createContext, useContext, useCallback, useRef, useMemo } from 'react';
2
+ import {
3
+ QueryCacheEntry,
4
+ InteractionType,
5
+ InteractionState,
6
+ } from './query-cache-utils';
7
+ import { getGlobalQueryCache, configureGlobalQueryCache } from './query-cache-singleton';
8
+
9
+ type QueryCacheContextValue = {
10
+ getCachedQuery: (
11
+ componentId: string,
12
+ interactionType: InteractionType,
13
+ state: InteractionState
14
+ ) => QueryCacheEntry | null;
15
+
16
+ setCachedQuery: (
17
+ componentId: string,
18
+ interactionType: InteractionType,
19
+ state: InteractionState,
20
+ entry: QueryCacheEntry
21
+ ) => void;
22
+
23
+ invalidateComponent: (componentId: string) => void;
24
+
25
+ invalidateAll: () => void;
26
+
27
+ isCached: (
28
+ componentId: string,
29
+ interactionType: InteractionType,
30
+ state: InteractionState
31
+ ) => boolean;
32
+ };
33
+
34
+ const QueryCacheContext = createContext<QueryCacheContextValue | null>(null);
35
+
36
+ type QueryCacheProviderProps = {
37
+ children: React.ReactNode;
38
+ maxCacheSize?: number;
39
+ maxCacheAge?: number;
40
+ };
41
+
42
+ export function QueryCacheProvider({
43
+ children,
44
+ maxCacheSize = 100,
45
+ maxCacheAge = 5 * 60 * 1000, // 5 minutes
46
+ }: QueryCacheProviderProps) {
47
+ const isConfigured = useRef(false);
48
+
49
+ if (!isConfigured.current) {
50
+ configureGlobalQueryCache(maxCacheSize, maxCacheAge);
51
+ isConfigured.current = true;
52
+ }
53
+
54
+ const cacheManager = useRef(getGlobalQueryCache()).current;
55
+
56
+ const getCachedQuery = useCallback(
57
+ (
58
+ componentId: string,
59
+ interactionType: InteractionType,
60
+ state: InteractionState
61
+ ): QueryCacheEntry | null => {
62
+ return cacheManager.getCachedQuery(componentId, interactionType, state);
63
+ },
64
+ [cacheManager]
65
+ );
66
+
67
+ const setCachedQuery = useCallback(
68
+ (
69
+ componentId: string,
70
+ interactionType: InteractionType,
71
+ state: InteractionState,
72
+ entry: QueryCacheEntry
73
+ ) => {
74
+ cacheManager.setCachedQuery(componentId, interactionType, state, entry);
75
+ },
76
+ [cacheManager]
77
+ );
78
+
79
+ const invalidateComponent = useCallback(
80
+ (componentId: string) => {
81
+ cacheManager.invalidateComponent(componentId);
82
+ },
83
+ [cacheManager]
84
+ );
85
+
86
+ const invalidateAll = useCallback(() => {
87
+ cacheManager.invalidateAll();
88
+ }, [cacheManager]);
89
+
90
+ const isCached = useCallback(
91
+ (
92
+ componentId: string,
93
+ interactionType: InteractionType,
94
+ state: InteractionState
95
+ ): boolean => {
96
+ return cacheManager.isCached(componentId, interactionType, state);
97
+ },
98
+ [cacheManager]
99
+ );
100
+
101
+ const value = useMemo(
102
+ () => ({
103
+ getCachedQuery,
104
+ setCachedQuery,
105
+ invalidateComponent,
106
+ invalidateAll,
107
+ isCached,
108
+ }),
109
+ [getCachedQuery, setCachedQuery, invalidateComponent, invalidateAll, isCached]
110
+ );
111
+
112
+ return (
113
+ <QueryCacheContext.Provider value={value}>
114
+ {children}
115
+ </QueryCacheContext.Provider>
116
+ );
117
+ }
118
+
119
+ export function useQueryCache(): QueryCacheContextValue {
120
+ const context = useContext(QueryCacheContext);
121
+
122
+ if (!context) {
123
+ throw new Error('useQueryCache must be used within a QueryCacheProvider');
124
+ }
125
+
126
+ return context;
127
+ }
128
+
129
+ export function useInvalidateCacheOnChange(
130
+ componentId: string | undefined,
131
+ dependencies: unknown[]
132
+ ): void {
133
+ const { invalidateComponent } = useQueryCache();
134
+ const prevDepsRef = useRef<unknown[]>([]);
135
+
136
+ React.useEffect(() => {
137
+ if (prevDepsRef.current.length === 0) {
138
+ prevDepsRef.current = dependencies;
139
+
140
+ return;
141
+ }
142
+
143
+ const hasChanged = dependencies.some(
144
+ (dep, index) => dep !== prevDepsRef.current[index]
145
+ );
146
+
147
+ if (hasChanged && componentId) {
148
+ invalidateComponent(componentId);
149
+ }
150
+
151
+ prevDepsRef.current = dependencies;
152
+ }, [componentId, invalidateComponent, dependencies]);
153
+ }
154
+