react-auto-smart-table 1.0.0
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.
- package/README.md +130 -0
- package/dist/index.css +404 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +164 -0
- package/dist/index.d.ts +164 -0
- package/dist/index.js +1259 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1218 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +62 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
FilterPanel: () => FilterPanel,
|
|
34
|
+
InsightsPanel: () => InsightsPanel,
|
|
35
|
+
PluginRegistry: () => PluginRegistry,
|
|
36
|
+
SmartTable: () => SmartTable,
|
|
37
|
+
aggregateData: () => aggregateData,
|
|
38
|
+
aggregateDataByCount: () => aggregateDataByCount,
|
|
39
|
+
buildSchema: () => buildSchema,
|
|
40
|
+
computeColumnStats: () => computeColumnStats,
|
|
41
|
+
currencyPlugin: () => currencyPlugin,
|
|
42
|
+
detectChartFields: () => detectChartFields,
|
|
43
|
+
detectValueType: () => detectValueType,
|
|
44
|
+
generateInsights: () => generateInsights,
|
|
45
|
+
getCellRenderer: () => getCellRenderer,
|
|
46
|
+
globalPluginRegistry: () => globalPluginRegistry,
|
|
47
|
+
percentagePlugin: () => percentagePlugin,
|
|
48
|
+
sampleDataset: () => sampleDataset,
|
|
49
|
+
useFilters: () => useFilters,
|
|
50
|
+
usePagination: () => usePagination,
|
|
51
|
+
useSorting: () => useSorting
|
|
52
|
+
});
|
|
53
|
+
module.exports = __toCommonJS(index_exports);
|
|
54
|
+
|
|
55
|
+
// src/analyzer/detectTypes.ts
|
|
56
|
+
var EMAIL_REGEX = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
|
|
57
|
+
var URL_REGEX = /^(https?:\/\/)?([\w.-]+)\.([a-z]{2,6}\.?)(\/[\w.-]*)*\/?$/i;
|
|
58
|
+
var IMAGE_REGEX = /\.(jpeg|jpg|gif|png|webp|svg|bmp)(\?.*)?$/i;
|
|
59
|
+
var isEmail = (value) => EMAIL_REGEX.test(value);
|
|
60
|
+
var isImage = (value) => {
|
|
61
|
+
return URL_REGEX.test(value) && IMAGE_REGEX.test(value);
|
|
62
|
+
};
|
|
63
|
+
var isUrl = (value) => {
|
|
64
|
+
if (isImage(value)) return false;
|
|
65
|
+
return URL_REGEX.test(value);
|
|
66
|
+
};
|
|
67
|
+
var isDate = (value) => {
|
|
68
|
+
if (value instanceof Date) return !isNaN(value.getTime());
|
|
69
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
70
|
+
if (typeof value === "number") return false;
|
|
71
|
+
if (typeof value === "string" && value.trim() !== "" && !isNaN(Number(value))) return false;
|
|
72
|
+
const datePattern = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?Z?)?$|^\d{1,2}[-/]\d{1,2}[-/]\d{2,4}$/;
|
|
73
|
+
if (typeof value === "string" && !datePattern.test(value.trim())) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const d = new Date(value);
|
|
77
|
+
return !isNaN(d.getTime()) && String(d) !== "Invalid Date";
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
};
|
|
81
|
+
var isCurrency = (value) => {
|
|
82
|
+
return typeof value === "string" && /^[£$€¥]?\s*-?\d+(?:,\d{3})*(?:\.\d+)?\s*[£$€¥]?$/.test(value) && /[£$€¥]/.test(value);
|
|
83
|
+
};
|
|
84
|
+
var isPercentage = (value) => {
|
|
85
|
+
return typeof value === "string" && /^-?\d+(?:\.\d+)?%$/.test(value);
|
|
86
|
+
};
|
|
87
|
+
var isNumber = (value) => {
|
|
88
|
+
if (typeof value === "number") return true;
|
|
89
|
+
if (typeof value === "string") {
|
|
90
|
+
return value.trim() !== "" && !isNaN(Number(value));
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
};
|
|
94
|
+
var isBoolean = (value) => {
|
|
95
|
+
if (typeof value === "boolean") return true;
|
|
96
|
+
if (typeof value === "string") {
|
|
97
|
+
const lower = value.toLowerCase();
|
|
98
|
+
return lower === "true" || lower === "false" || lower === "yes" || lower === "no";
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
};
|
|
102
|
+
var detectValueType = (value) => {
|
|
103
|
+
if (value === null || value === void 0 || value === "") return "string";
|
|
104
|
+
const strVal = String(value);
|
|
105
|
+
if (isEmail(strVal)) return "email";
|
|
106
|
+
if (isUrl(strVal)) return "url";
|
|
107
|
+
if (isImage(strVal)) return "image";
|
|
108
|
+
if (isDate(value)) return "date";
|
|
109
|
+
if (isCurrency(strVal)) return "currency";
|
|
110
|
+
if (isPercentage(strVal)) return "percentage";
|
|
111
|
+
if (isNumber(value)) return "number";
|
|
112
|
+
if (isBoolean(value)) return "boolean";
|
|
113
|
+
return "string";
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// src/analyzer/sampleDataset.ts
|
|
117
|
+
function sampleDataset(dataset, maxSampleSize = 200) {
|
|
118
|
+
if (!dataset || !dataset.length) return [];
|
|
119
|
+
if (dataset.length <= maxSampleSize) return [...dataset];
|
|
120
|
+
const sample = [];
|
|
121
|
+
const indices = /* @__PURE__ */ new Set();
|
|
122
|
+
while (sample.length < maxSampleSize) {
|
|
123
|
+
const idx = Math.floor(Math.random() * dataset.length);
|
|
124
|
+
if (!indices.has(idx)) {
|
|
125
|
+
indices.add(idx);
|
|
126
|
+
sample.push(dataset[idx]);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return sample;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/analyzer/computeStats.ts
|
|
133
|
+
var computeColumnStats = (columnKey, dataset, type) => {
|
|
134
|
+
const stats = {};
|
|
135
|
+
const uniqueVals = /* @__PURE__ */ new Set();
|
|
136
|
+
let min = void 0;
|
|
137
|
+
let max = void 0;
|
|
138
|
+
for (const row of dataset) {
|
|
139
|
+
const val = row[columnKey];
|
|
140
|
+
if (val !== null && val !== void 0 && val !== "") {
|
|
141
|
+
uniqueVals.add(val);
|
|
142
|
+
if (type === "number" || type === "currency" || type === "percentage") {
|
|
143
|
+
let numVal = NaN;
|
|
144
|
+
if (type === "number") numVal = Number(val);
|
|
145
|
+
else if (type === "currency" || type === "percentage") {
|
|
146
|
+
const clean = String(val).replace(/[^0-9.-]/g, "");
|
|
147
|
+
numVal = Number(clean);
|
|
148
|
+
}
|
|
149
|
+
if (!isNaN(numVal)) {
|
|
150
|
+
if (min === void 0 || numVal < min) min = numVal;
|
|
151
|
+
if (max === void 0 || numVal > max) max = numVal;
|
|
152
|
+
}
|
|
153
|
+
} else if (type === "date") {
|
|
154
|
+
const d = new Date(val).getTime();
|
|
155
|
+
if (!isNaN(d)) {
|
|
156
|
+
if (min === void 0 || d < min) min = d;
|
|
157
|
+
if (max === void 0 || d > max) max = d;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
stats.uniqueValues = uniqueVals.size;
|
|
163
|
+
if (min !== void 0) stats.min = min;
|
|
164
|
+
if (max !== void 0) stats.max = max;
|
|
165
|
+
return stats;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// src/schema/buildSchema.ts
|
|
169
|
+
var buildSchema = (dataset, registry) => {
|
|
170
|
+
if (!dataset || dataset.length === 0) return {};
|
|
171
|
+
const sample = sampleDataset(dataset, 200);
|
|
172
|
+
const sampleSize = sample.length;
|
|
173
|
+
const keys = /* @__PURE__ */ new Set();
|
|
174
|
+
for (const row of sample) {
|
|
175
|
+
for (const key of Object.keys(row)) {
|
|
176
|
+
keys.add(key);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const schema = {};
|
|
180
|
+
for (const key of keys) {
|
|
181
|
+
const values = sample.map((row) => row[key]);
|
|
182
|
+
let detectedTypeStr = null;
|
|
183
|
+
if (registry) {
|
|
184
|
+
detectedTypeStr = registry.runDetectors(key, values);
|
|
185
|
+
}
|
|
186
|
+
if (detectedTypeStr) {
|
|
187
|
+
const isNull = values.some((v) => v === null || v === void 0);
|
|
188
|
+
schema[key] = {
|
|
189
|
+
key,
|
|
190
|
+
type: detectedTypeStr,
|
|
191
|
+
nullable: isNull
|
|
192
|
+
};
|
|
193
|
+
const stats2 = computeColumnStats(key, sample, detectedTypeStr);
|
|
194
|
+
schema[key].uniqueValues = stats2.uniqueValues;
|
|
195
|
+
if (stats2.min !== void 0) schema[key].min = stats2.min;
|
|
196
|
+
if (stats2.max !== void 0) schema[key].max = stats2.max;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
let nullCount = 0;
|
|
200
|
+
const typeCounts = {};
|
|
201
|
+
for (const row of sample) {
|
|
202
|
+
const val = row[key];
|
|
203
|
+
if (val === null || val === void 0 || val === "") {
|
|
204
|
+
nullCount++;
|
|
205
|
+
} else {
|
|
206
|
+
const detectedType = detectValueType(val);
|
|
207
|
+
typeCounts[detectedType] = (typeCounts[detectedType] || 0) + 1;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const validValuesCount = sampleSize - nullCount;
|
|
211
|
+
let dominantType = "string";
|
|
212
|
+
if (validValuesCount > 0) {
|
|
213
|
+
let maxCount = 0;
|
|
214
|
+
let topType = "string";
|
|
215
|
+
for (const [type, count] of Object.entries(typeCounts)) {
|
|
216
|
+
if (count > maxCount) {
|
|
217
|
+
maxCount = count;
|
|
218
|
+
topType = type;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const confidence = maxCount / validValuesCount;
|
|
222
|
+
dominantType = confidence > 0.7 ? topType : "string";
|
|
223
|
+
}
|
|
224
|
+
const stats = computeColumnStats(key, sample, dominantType);
|
|
225
|
+
const columnSchema = {
|
|
226
|
+
key,
|
|
227
|
+
type: dominantType,
|
|
228
|
+
nullable: nullCount > 0,
|
|
229
|
+
uniqueValues: stats.uniqueValues
|
|
230
|
+
};
|
|
231
|
+
if (stats.min !== void 0) columnSchema.min = stats.min;
|
|
232
|
+
if (stats.max !== void 0) columnSchema.max = stats.max;
|
|
233
|
+
schema[key] = columnSchema;
|
|
234
|
+
}
|
|
235
|
+
return schema;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// src/hooks/useSorting.ts
|
|
239
|
+
var import_react = require("react");
|
|
240
|
+
var useSorting = (data, schema, initialSortColumn = null, initialSortDirection = "asc") => {
|
|
241
|
+
const [sortColumn, setSortColumn] = (0, import_react.useState)(initialSortColumn);
|
|
242
|
+
const [sortDirection, setSortDirection] = (0, import_react.useState)(initialSortDirection);
|
|
243
|
+
const handleSort = (columnKey) => {
|
|
244
|
+
if (sortColumn === columnKey) {
|
|
245
|
+
if (sortDirection === "asc") {
|
|
246
|
+
setSortDirection("desc");
|
|
247
|
+
} else {
|
|
248
|
+
setSortColumn(null);
|
|
249
|
+
setSortDirection("asc");
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
setSortColumn(columnKey);
|
|
253
|
+
setSortDirection("asc");
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const sortedData = (0, import_react.useMemo)(() => {
|
|
257
|
+
if (!sortColumn || !data || data.length === 0) return data;
|
|
258
|
+
const columnType = schema[sortColumn]?.type || "string";
|
|
259
|
+
return [...data].sort((a, b) => {
|
|
260
|
+
let valA = a[sortColumn];
|
|
261
|
+
let valB = b[sortColumn];
|
|
262
|
+
if (valA === valB) return 0;
|
|
263
|
+
if (valA === null || valA === void 0) return sortDirection === "asc" ? 1 : -1;
|
|
264
|
+
if (valB === null || valB === void 0) return sortDirection === "asc" ? -1 : 1;
|
|
265
|
+
let comparison = 0;
|
|
266
|
+
switch (columnType) {
|
|
267
|
+
case "number":
|
|
268
|
+
case "currency":
|
|
269
|
+
case "percentage":
|
|
270
|
+
const numA = typeof valA === "number" ? valA : Number(String(valA).replace(/[^0-9.-]/g, ""));
|
|
271
|
+
const numB = typeof valB === "number" ? valB : Number(String(valB).replace(/[^0-9.-]/g, ""));
|
|
272
|
+
comparison = (isNaN(numA) ? 0 : numA) - (isNaN(numB) ? 0 : numB);
|
|
273
|
+
break;
|
|
274
|
+
case "date":
|
|
275
|
+
const dateA = new Date(valA).getTime();
|
|
276
|
+
const dateB = new Date(valB).getTime();
|
|
277
|
+
comparison = (isNaN(dateA) ? 0 : dateA) - (isNaN(dateB) ? 0 : dateB);
|
|
278
|
+
break;
|
|
279
|
+
case "boolean":
|
|
280
|
+
const boolA = valA === true || String(valA).toLowerCase() === "true" || String(valA).toLowerCase() === "yes" ? 1 : 0;
|
|
281
|
+
const boolB = valB === true || String(valB).toLowerCase() === "true" || String(valB).toLowerCase() === "yes" ? 1 : 0;
|
|
282
|
+
comparison = boolA - boolB;
|
|
283
|
+
break;
|
|
284
|
+
default:
|
|
285
|
+
comparison = String(valA).localeCompare(String(valB));
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
return sortDirection === "asc" ? comparison : -comparison;
|
|
289
|
+
});
|
|
290
|
+
}, [data, schema, sortColumn, sortDirection]);
|
|
291
|
+
return { sortedData, sortColumn, sortDirection, handleSort };
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// src/hooks/useFilters.ts
|
|
295
|
+
var import_react2 = require("react");
|
|
296
|
+
var useFilters = (data, schema) => {
|
|
297
|
+
const [filters, setFilters] = (0, import_react2.useState)({});
|
|
298
|
+
const setFilter = (columnKey, value) => {
|
|
299
|
+
setFilters((prev) => {
|
|
300
|
+
const next = { ...prev };
|
|
301
|
+
if (value === null || value === void 0 || value === "") {
|
|
302
|
+
delete next[columnKey];
|
|
303
|
+
} else {
|
|
304
|
+
next[columnKey] = value;
|
|
305
|
+
}
|
|
306
|
+
return next;
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
const clearFilters = () => setFilters({});
|
|
310
|
+
const filteredData = (0, import_react2.useMemo)(() => {
|
|
311
|
+
if (!filters || Object.keys(filters).length === 0 || !data || data.length === 0) {
|
|
312
|
+
return data;
|
|
313
|
+
}
|
|
314
|
+
return data.filter((row) => {
|
|
315
|
+
for (const [key, filterValue] of Object.entries(filters)) {
|
|
316
|
+
const rowValue = row[key];
|
|
317
|
+
const type = schema[key]?.type || "string";
|
|
318
|
+
if (rowValue === null || rowValue === void 0) {
|
|
319
|
+
if (filterValue !== null && filterValue !== "") return false;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
switch (type) {
|
|
323
|
+
case "string":
|
|
324
|
+
case "email":
|
|
325
|
+
case "url":
|
|
326
|
+
if (typeof filterValue === "string") {
|
|
327
|
+
if (!String(rowValue).toLowerCase().includes(filterValue.toLowerCase())) return false;
|
|
328
|
+
}
|
|
329
|
+
break;
|
|
330
|
+
case "number":
|
|
331
|
+
case "currency":
|
|
332
|
+
case "percentage":
|
|
333
|
+
if (typeof filterValue === "object" && filterValue !== null) {
|
|
334
|
+
const numVal = typeof rowValue === "number" ? rowValue : Number(String(rowValue).replace(/[^0-9.-]/g, ""));
|
|
335
|
+
if (isNaN(numVal)) return false;
|
|
336
|
+
const { min, max } = filterValue;
|
|
337
|
+
if (min !== void 0 && numVal < min) return false;
|
|
338
|
+
if (max !== void 0 && numVal > max) return false;
|
|
339
|
+
}
|
|
340
|
+
break;
|
|
341
|
+
case "date":
|
|
342
|
+
if (typeof filterValue === "object" && filterValue !== null) {
|
|
343
|
+
const dateVal = new Date(rowValue).getTime();
|
|
344
|
+
if (isNaN(dateVal)) return false;
|
|
345
|
+
const { start, end } = filterValue;
|
|
346
|
+
if (start && dateVal < new Date(start).getTime()) return false;
|
|
347
|
+
if (end && dateVal > new Date(end).getTime()) return false;
|
|
348
|
+
}
|
|
349
|
+
break;
|
|
350
|
+
case "boolean":
|
|
351
|
+
if (typeof filterValue === "boolean") {
|
|
352
|
+
const boolVal = rowValue === true || String(rowValue).toLowerCase() === "true" || String(rowValue).toLowerCase() === "yes";
|
|
353
|
+
if (boolVal !== filterValue) return false;
|
|
354
|
+
}
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return true;
|
|
359
|
+
});
|
|
360
|
+
}, [data, schema, filters]);
|
|
361
|
+
return { filteredData, filters, setFilter, clearFilters };
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// src/hooks/usePagination.ts
|
|
365
|
+
var import_react3 = require("react");
|
|
366
|
+
var usePagination = (data, initialPageSize = 10) => {
|
|
367
|
+
const [currentPage, setCurrentPage] = (0, import_react3.useState)(1);
|
|
368
|
+
const [pageSize, setPageSize] = (0, import_react3.useState)(initialPageSize);
|
|
369
|
+
const totalPages = Math.max(1, Math.ceil(data.length / pageSize));
|
|
370
|
+
const safeCurrentPage = Math.min(currentPage, totalPages);
|
|
371
|
+
const paginatedData = (0, import_react3.useMemo)(() => {
|
|
372
|
+
const startIndex = (safeCurrentPage - 1) * pageSize;
|
|
373
|
+
return data.slice(startIndex, startIndex + pageSize);
|
|
374
|
+
}, [data, safeCurrentPage, pageSize]);
|
|
375
|
+
return {
|
|
376
|
+
paginatedData,
|
|
377
|
+
currentPage: safeCurrentPage,
|
|
378
|
+
pageSize,
|
|
379
|
+
totalPages,
|
|
380
|
+
setPage: setCurrentPage,
|
|
381
|
+
setPageSize
|
|
382
|
+
};
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
// src/insights/aggregationEngine.ts
|
|
386
|
+
var aggregateData = (dataset, dimension, metrics) => {
|
|
387
|
+
if (!dataset || dataset.length === 0) return [];
|
|
388
|
+
if (!dimension) return [];
|
|
389
|
+
const groups = /* @__PURE__ */ new Map();
|
|
390
|
+
for (const row of dataset) {
|
|
391
|
+
let dimValue = row[dimension];
|
|
392
|
+
if (dimValue === null || dimValue === void 0) {
|
|
393
|
+
dimValue = "Unknown";
|
|
394
|
+
} else if (typeof dimValue === "boolean") {
|
|
395
|
+
dimValue = dimValue ? "Yes" : "No";
|
|
396
|
+
}
|
|
397
|
+
if (!groups.has(dimValue)) {
|
|
398
|
+
const initPoint = { [dimension]: dimValue };
|
|
399
|
+
for (const m of metrics) {
|
|
400
|
+
initPoint[m] = 0;
|
|
401
|
+
}
|
|
402
|
+
groups.set(dimValue, initPoint);
|
|
403
|
+
}
|
|
404
|
+
const group = groups.get(dimValue);
|
|
405
|
+
for (const m of metrics) {
|
|
406
|
+
const val = row[m];
|
|
407
|
+
let numVal = 0;
|
|
408
|
+
if (typeof val === "number") {
|
|
409
|
+
numVal = val;
|
|
410
|
+
} else if (typeof val === "string") {
|
|
411
|
+
numVal = Number(val.replace(/[^0-9.-]/g, ""));
|
|
412
|
+
}
|
|
413
|
+
if (!isNaN(numVal)) {
|
|
414
|
+
group[m] += numVal;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return Array.from(groups.values());
|
|
419
|
+
};
|
|
420
|
+
var aggregateDataByCount = (dataset, dimension) => {
|
|
421
|
+
if (!dataset || dataset.length === 0) return [];
|
|
422
|
+
const groups = /* @__PURE__ */ new Map();
|
|
423
|
+
for (const row of dataset) {
|
|
424
|
+
let dimValue = row[dimension];
|
|
425
|
+
if (dimValue === null || dimValue === void 0) {
|
|
426
|
+
dimValue = "Unknown";
|
|
427
|
+
} else if (typeof dimValue === "boolean") {
|
|
428
|
+
dimValue = dimValue ? "Yes" : "No";
|
|
429
|
+
}
|
|
430
|
+
groups.set(dimValue, (groups.get(dimValue) || 0) + 1);
|
|
431
|
+
}
|
|
432
|
+
return Array.from(groups.entries()).map(([dim, count]) => ({
|
|
433
|
+
[dimension]: dim,
|
|
434
|
+
count
|
|
435
|
+
}));
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// src/insights/chartDetector.ts
|
|
439
|
+
var detectChartFields = (schema) => {
|
|
440
|
+
const dimensions = [];
|
|
441
|
+
const metrics = [];
|
|
442
|
+
for (const [key, col] of Object.entries(schema)) {
|
|
443
|
+
const lowerKey = key.toLowerCase();
|
|
444
|
+
const isIdField = lowerKey === "id" || lowerKey === "_id" || lowerKey === "uuid" || lowerKey === "guid" || lowerKey.endsWith("id") || lowerKey.endsWith("_id") || lowerKey.endsWith("uuid");
|
|
445
|
+
if (isIdField) {
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
const role = determineFieldRole(col.type, col.uniqueValues);
|
|
449
|
+
if (role === "dimension") {
|
|
450
|
+
dimensions.push(key);
|
|
451
|
+
} else if (role === "metric") {
|
|
452
|
+
metrics.push(key);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return { dimensions, metrics };
|
|
456
|
+
};
|
|
457
|
+
var determineFieldRole = (type, uniqueValues) => {
|
|
458
|
+
switch (type) {
|
|
459
|
+
case "boolean":
|
|
460
|
+
return "dimension";
|
|
461
|
+
case "string":
|
|
462
|
+
if (uniqueValues !== void 0 && uniqueValues > 1 && uniqueValues <= 20) {
|
|
463
|
+
return "dimension";
|
|
464
|
+
}
|
|
465
|
+
return "discard";
|
|
466
|
+
case "date":
|
|
467
|
+
return "dimension";
|
|
468
|
+
case "number":
|
|
469
|
+
case "currency":
|
|
470
|
+
case "percentage":
|
|
471
|
+
return "metric";
|
|
472
|
+
default:
|
|
473
|
+
return "discard";
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// src/utils/stringUtils.ts
|
|
478
|
+
var ACRONYMS = ["ID", "URL", "API", "JSON", "UUID", "SKU", "IP", "VAT", "GST"];
|
|
479
|
+
var toTitleCase = (str) => {
|
|
480
|
+
if (!str) return "";
|
|
481
|
+
const spaced = str.replace(/([A-Z])/g, " $1").replace(/[_-]/g, " ").trim();
|
|
482
|
+
return spaced.split(" ").filter(Boolean).map((word) => {
|
|
483
|
+
const upper = word.toUpperCase();
|
|
484
|
+
if (ACRONYMS.includes(upper)) {
|
|
485
|
+
return upper;
|
|
486
|
+
}
|
|
487
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
488
|
+
}).join(" ");
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// src/insights/insightBuilder.ts
|
|
492
|
+
var generateInsights = (dataset, schema) => {
|
|
493
|
+
if (!dataset || dataset.length === 0 || !schema) return [];
|
|
494
|
+
const { dimensions, metrics } = detectChartFields(schema);
|
|
495
|
+
const widgets = [];
|
|
496
|
+
const maxCharts = 4;
|
|
497
|
+
if (dimensions.length > 0 && metrics.length > 0) {
|
|
498
|
+
for (const dim of dimensions) {
|
|
499
|
+
if (widgets.length >= maxCharts) break;
|
|
500
|
+
const dimInfo = schema[dim];
|
|
501
|
+
const targetMetrics = metrics.slice(0, 2);
|
|
502
|
+
const aggregated = aggregateData(dataset, dim, targetMetrics);
|
|
503
|
+
if (dimInfo.type === "date") {
|
|
504
|
+
aggregated.sort((a, b) => new Date(a[dim]).getTime() - new Date(b[dim]).getTime());
|
|
505
|
+
} else {
|
|
506
|
+
const primaryMetric = targetMetrics[0];
|
|
507
|
+
aggregated.sort((a, b) => (b[primaryMetric] || 0) - (a[primaryMetric] || 0));
|
|
508
|
+
}
|
|
509
|
+
const isLine = dimInfo.type === "date";
|
|
510
|
+
const isPie = !isLine && aggregated.length > 0 && aggregated.length <= 5 && targetMetrics.length === 1;
|
|
511
|
+
widgets.push({
|
|
512
|
+
id: `chart-${dim}-metrics`,
|
|
513
|
+
title: `${targetMetrics.map(toTitleCase).join(" & ")} by ${toTitleCase(dim)}`,
|
|
514
|
+
type: isPie ? "pie" : isLine ? "line" : "bar",
|
|
515
|
+
data: aggregated,
|
|
516
|
+
xAxisKey: dim,
|
|
517
|
+
yAxisKeys: targetMetrics
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
if (dimensions.length > 0 && widgets.length < maxCharts) {
|
|
522
|
+
for (const dim of dimensions) {
|
|
523
|
+
if (widgets.length >= maxCharts) break;
|
|
524
|
+
if (widgets.some((w) => w.id.includes(dim))) continue;
|
|
525
|
+
const dimInfo = schema[dim];
|
|
526
|
+
const aggregated = aggregateDataByCount(dataset, dim);
|
|
527
|
+
if (dimInfo.type === "date") {
|
|
528
|
+
aggregated.sort((a, b) => new Date(a[dim]).getTime() - new Date(b[dim]).getTime());
|
|
529
|
+
} else {
|
|
530
|
+
aggregated.sort((a, b) => b.count - a.count);
|
|
531
|
+
}
|
|
532
|
+
const isLine = dimInfo.type === "date";
|
|
533
|
+
widgets.push({
|
|
534
|
+
id: `chart-${dim}-count`,
|
|
535
|
+
title: `Distribution of ${toTitleCase(dim)}`,
|
|
536
|
+
type: isLine ? "line" : aggregated.length <= 6 ? "pie" : "bar",
|
|
537
|
+
data: aggregated,
|
|
538
|
+
xAxisKey: dim,
|
|
539
|
+
yAxisKeys: ["count"]
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return widgets;
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
// src/components/SmartTable.tsx
|
|
547
|
+
var import_react6 = __toESM(require("react"));
|
|
548
|
+
|
|
549
|
+
// src/components/TableHeader.tsx
|
|
550
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
551
|
+
var getColumnWidthClass = (key, type) => {
|
|
552
|
+
const lowerKey = key.toLowerCase();
|
|
553
|
+
if (lowerKey === "id" || lowerKey === "_id" || type === "boolean") return "rst-col-xs";
|
|
554
|
+
if (type === "date" || type === "number" || lowerKey.includes("status")) return "rst-col-sm";
|
|
555
|
+
if (lowerKey.includes("role") || lowerKey.includes("country") || lowerKey.includes("category")) return "rst-col-md";
|
|
556
|
+
if (lowerKey.includes("name") || lowerKey.includes("email") || lowerKey.includes("url") || lowerKey.includes("description")) return "rst-col-flex";
|
|
557
|
+
return "rst-col-md";
|
|
558
|
+
};
|
|
559
|
+
var TableHeader = ({
|
|
560
|
+
schema,
|
|
561
|
+
sortable = false,
|
|
562
|
+
sortColumn = null,
|
|
563
|
+
sortDirection = "asc",
|
|
564
|
+
onSort
|
|
565
|
+
}) => {
|
|
566
|
+
const columns = Object.keys(schema);
|
|
567
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { className: "rst-header", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { className: "rst-header-row", children: columns.map((colKey) => {
|
|
568
|
+
const colInfo = schema[colKey];
|
|
569
|
+
const widthClass = getColumnWidthClass(colKey, colInfo.type);
|
|
570
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
571
|
+
"th",
|
|
572
|
+
{
|
|
573
|
+
className: `rst-th rst-th-col-${colKey} rst-th-type-${colInfo.type} ${widthClass} ${sortable && onSort ? "rst-sortable" : ""} ${sortColumn === colKey ? "rst-sort-active" : ""}`,
|
|
574
|
+
onClick: () => {
|
|
575
|
+
if (sortable && onSort) onSort(colKey);
|
|
576
|
+
},
|
|
577
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rst-header-content", children: [
|
|
578
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: toTitleCase(colKey) }),
|
|
579
|
+
sortable && onSort && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rst-sort-icon", children: sortColumn === colKey ? sortDirection === "asc" ? "\u2191" : "\u2193" : "\u21C5" })
|
|
580
|
+
] })
|
|
581
|
+
},
|
|
582
|
+
colKey
|
|
583
|
+
);
|
|
584
|
+
}) }) });
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
// src/components/TableBody.tsx
|
|
588
|
+
var import_react4 = __toESM(require("react"));
|
|
589
|
+
|
|
590
|
+
// src/renderers/numberRenderer.tsx
|
|
591
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
592
|
+
var NumberRenderer = ({ value }) => {
|
|
593
|
+
if (value === null || value === void 0) return null;
|
|
594
|
+
const num = Number(value);
|
|
595
|
+
if (!isNaN(num)) {
|
|
596
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "rst-cell-number", children: new Intl.NumberFormat().format(num) });
|
|
597
|
+
}
|
|
598
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "rst-cell-number", children: String(value) });
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
// src/renderers/emailRenderer.tsx
|
|
602
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
603
|
+
var EmailRenderer = ({ value }) => {
|
|
604
|
+
if (!value) return null;
|
|
605
|
+
const email = String(value);
|
|
606
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("a", { href: `mailto:${email}`, className: "rst-cell-email", onClick: (e) => e.stopPropagation(), children: email });
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
// src/renderers/urlRenderer.tsx
|
|
610
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
611
|
+
var UrlRenderer = ({ value }) => {
|
|
612
|
+
if (!value) return null;
|
|
613
|
+
const url = String(value);
|
|
614
|
+
const href = /^https?:\/\//i.test(url) ? url : `https://${url}`;
|
|
615
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
616
|
+
"a",
|
|
617
|
+
{
|
|
618
|
+
href,
|
|
619
|
+
target: "_blank",
|
|
620
|
+
rel: "noopener noreferrer",
|
|
621
|
+
className: "rst-cell-url",
|
|
622
|
+
onClick: (e) => e.stopPropagation(),
|
|
623
|
+
children: url
|
|
624
|
+
}
|
|
625
|
+
);
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
// src/renderers/imageRenderer.tsx
|
|
629
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
630
|
+
var ImageRenderer = ({ value }) => {
|
|
631
|
+
if (!value) return null;
|
|
632
|
+
const src = String(value);
|
|
633
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
634
|
+
"img",
|
|
635
|
+
{
|
|
636
|
+
src,
|
|
637
|
+
alt: "cell content",
|
|
638
|
+
className: "rst-cell-image",
|
|
639
|
+
style: { maxWidth: "50px", maxHeight: "50px", objectFit: "contain", borderRadius: "4px" },
|
|
640
|
+
loading: "lazy"
|
|
641
|
+
}
|
|
642
|
+
);
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
// src/renderers/percentageRenderer.tsx
|
|
646
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
647
|
+
var PercentageRenderer = ({ value }) => {
|
|
648
|
+
if (value === null || value === void 0) return null;
|
|
649
|
+
const str = String(value);
|
|
650
|
+
if (str.includes("%")) {
|
|
651
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rst-cell-percentage", children: str });
|
|
652
|
+
}
|
|
653
|
+
const num = Number(value);
|
|
654
|
+
if (!isNaN(num)) {
|
|
655
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "rst-cell-percentage", children: [
|
|
656
|
+
(num * 100).toFixed(2),
|
|
657
|
+
"%"
|
|
658
|
+
] });
|
|
659
|
+
}
|
|
660
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rst-cell-percentage", children: str });
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
// src/renderers/dateRenderer.tsx
|
|
664
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
665
|
+
var DateRenderer = ({ value }) => {
|
|
666
|
+
if (!value) return null;
|
|
667
|
+
const d = new Date(value);
|
|
668
|
+
if (isNaN(d.getTime())) {
|
|
669
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rst-cell-date", children: String(value) });
|
|
670
|
+
}
|
|
671
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rst-cell-date", title: d.toISOString(), children: d.toLocaleDateString(void 0, {
|
|
672
|
+
year: "numeric",
|
|
673
|
+
month: "short",
|
|
674
|
+
day: "numeric"
|
|
675
|
+
}) });
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
// src/renderers/booleanRenderer.tsx
|
|
679
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
680
|
+
var BooleanRenderer = ({ value }) => {
|
|
681
|
+
if (value === null || value === void 0) return null;
|
|
682
|
+
let isTrue = false;
|
|
683
|
+
if (typeof value === "boolean") {
|
|
684
|
+
isTrue = value;
|
|
685
|
+
} else if (typeof value === "string") {
|
|
686
|
+
const lower = value.toLowerCase();
|
|
687
|
+
isTrue = lower === "true" || lower === "yes";
|
|
688
|
+
} else if (typeof value === "number") {
|
|
689
|
+
isTrue = value > 0;
|
|
690
|
+
}
|
|
691
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
692
|
+
"span",
|
|
693
|
+
{
|
|
694
|
+
className: `rst-cell-boolean rst-cell-boolean-${isTrue ? "true" : "false"}`,
|
|
695
|
+
style: {
|
|
696
|
+
display: "inline-block",
|
|
697
|
+
padding: "2px 8px",
|
|
698
|
+
borderRadius: "12px",
|
|
699
|
+
fontSize: "0.85em",
|
|
700
|
+
fontWeight: "bold",
|
|
701
|
+
backgroundColor: isTrue ? "#e6f4ea" : "#fce8e6",
|
|
702
|
+
color: isTrue ? "#137333" : "#c5221f"
|
|
703
|
+
},
|
|
704
|
+
children: isTrue ? "Yes" : "No"
|
|
705
|
+
}
|
|
706
|
+
);
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
// src/renderers/defaultRenderer.tsx
|
|
710
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
711
|
+
var DefaultRenderer = ({ value }) => {
|
|
712
|
+
if (value === null || value === void 0) return null;
|
|
713
|
+
if (typeof value === "object") {
|
|
714
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rst-cell-default", children: JSON.stringify(value) });
|
|
715
|
+
}
|
|
716
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rst-cell-default", children: String(value) });
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
// src/plugins/currencyPlugin.tsx
|
|
720
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
721
|
+
var currencyPlugin = {
|
|
722
|
+
detect: (columnKey, sample) => {
|
|
723
|
+
const lowerKey = columnKey.toLowerCase();
|
|
724
|
+
if (lowerKey.includes("amount") || lowerKey.includes("price") || lowerKey.includes("revenue")) {
|
|
725
|
+
return "currency";
|
|
726
|
+
}
|
|
727
|
+
return null;
|
|
728
|
+
},
|
|
729
|
+
render: ({ value }) => {
|
|
730
|
+
if (value === null || value === void 0) return null;
|
|
731
|
+
let num = NaN;
|
|
732
|
+
if (typeof value === "number") num = value;
|
|
733
|
+
else if (typeof value === "string") num = Number(value.replace(/[^0-9.-]/g, ""));
|
|
734
|
+
if (isNaN(num)) return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: String(value) });
|
|
735
|
+
const formatted = new Intl.NumberFormat("en-US", {
|
|
736
|
+
style: "currency",
|
|
737
|
+
currency: "USD"
|
|
738
|
+
}).format(num);
|
|
739
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { color: num < 0 ? "#d32f2f" : "#388e3c", fontWeight: "bold" }, children: formatted });
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
// src/plugins/percentagePlugin.tsx
|
|
744
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
745
|
+
var percentagePlugin = {
|
|
746
|
+
detect: (columnKey, sample) => {
|
|
747
|
+
const lowerKey = columnKey.toLowerCase();
|
|
748
|
+
if (lowerKey.includes("percent") || lowerKey.includes("rate")) {
|
|
749
|
+
return "percentage";
|
|
750
|
+
}
|
|
751
|
+
return null;
|
|
752
|
+
},
|
|
753
|
+
render: ({ value }) => {
|
|
754
|
+
if (value === null || value === void 0) return null;
|
|
755
|
+
let str = String(value);
|
|
756
|
+
let num = Number(value);
|
|
757
|
+
if (!str.includes("%") && !isNaN(num)) {
|
|
758
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { style: {
|
|
759
|
+
backgroundColor: "#e3f2fd",
|
|
760
|
+
padding: "2px 6px",
|
|
761
|
+
borderRadius: "4px",
|
|
762
|
+
color: "#1565c0",
|
|
763
|
+
fontSize: "0.9em",
|
|
764
|
+
fontWeight: 600
|
|
765
|
+
}, children: [
|
|
766
|
+
(num * 100).toFixed(1),
|
|
767
|
+
"%"
|
|
768
|
+
] });
|
|
769
|
+
}
|
|
770
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: {
|
|
771
|
+
backgroundColor: "#e3f2fd",
|
|
772
|
+
padding: "2px 6px",
|
|
773
|
+
borderRadius: "4px",
|
|
774
|
+
color: "#1565c0",
|
|
775
|
+
fontSize: "0.9em",
|
|
776
|
+
fontWeight: 600
|
|
777
|
+
}, children: str });
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
// src/renderers/cellRendererFactory.ts
|
|
782
|
+
var getCellRenderer = (type, registry) => {
|
|
783
|
+
if (registry) {
|
|
784
|
+
for (const plugin of registry.getPlugins()) {
|
|
785
|
+
if (type === "currency" && plugin.render && (plugin === currencyPlugin || plugin.detect?.("amount", []) === "currency")) {
|
|
786
|
+
return plugin.render;
|
|
787
|
+
}
|
|
788
|
+
if (type === "percentage" && plugin.render && (plugin === percentagePlugin || plugin.detect?.("percentage", []) === "percentage")) {
|
|
789
|
+
return plugin.render;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
switch (type) {
|
|
794
|
+
case "number":
|
|
795
|
+
case "currency":
|
|
796
|
+
return NumberRenderer;
|
|
797
|
+
case "email":
|
|
798
|
+
return EmailRenderer;
|
|
799
|
+
case "url":
|
|
800
|
+
return UrlRenderer;
|
|
801
|
+
case "image":
|
|
802
|
+
return ImageRenderer;
|
|
803
|
+
case "percentage":
|
|
804
|
+
return PercentageRenderer;
|
|
805
|
+
case "date":
|
|
806
|
+
return DateRenderer;
|
|
807
|
+
case "boolean":
|
|
808
|
+
return BooleanRenderer;
|
|
809
|
+
case "string":
|
|
810
|
+
default:
|
|
811
|
+
return DefaultRenderer;
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
// src/components/TableBody.tsx
|
|
816
|
+
var import_react_virtual = require("react-virtual");
|
|
817
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
818
|
+
var TableBody = ({ data, schema, registry }) => {
|
|
819
|
+
const columns = Object.keys(schema);
|
|
820
|
+
const parentRef = (0, import_react4.useRef)(null);
|
|
821
|
+
const rowVirtualizer = (0, import_react_virtual.useVirtual)({
|
|
822
|
+
size: data.length,
|
|
823
|
+
parentRef,
|
|
824
|
+
estimateSize: import_react4.default.useCallback(() => 45, []),
|
|
825
|
+
overscan: 5
|
|
826
|
+
});
|
|
827
|
+
const isVirtualized = data.length > 20;
|
|
828
|
+
if (!isVirtualized) {
|
|
829
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tbody", { className: "rst-body", children: data.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tr", { className: "rst-row", children: columns.map((colKey) => {
|
|
830
|
+
const colInfo = schema[colKey];
|
|
831
|
+
const Renderer = getCellRenderer(colInfo.type, registry);
|
|
832
|
+
const widthClass = getColumnWidthClass(colKey, colInfo.type);
|
|
833
|
+
const val = row[colKey];
|
|
834
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
835
|
+
"td",
|
|
836
|
+
{
|
|
837
|
+
className: `rst-td rst-td-col-${colKey} rst-td-type-${colInfo.type} ${widthClass}`,
|
|
838
|
+
children: colInfo.type === "boolean" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rst-badge ${val ? "rst-badge-yes" : "rst-badge-no"}`, children: val ? "Yes" : "No" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Renderer, { value: val })
|
|
839
|
+
},
|
|
840
|
+
colKey
|
|
841
|
+
);
|
|
842
|
+
}) }, index)) });
|
|
843
|
+
}
|
|
844
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
845
|
+
"tbody",
|
|
846
|
+
{
|
|
847
|
+
className: "rst-body",
|
|
848
|
+
ref: parentRef,
|
|
849
|
+
style: {
|
|
850
|
+
height: `${rowVirtualizer.totalSize}px`,
|
|
851
|
+
width: "100%",
|
|
852
|
+
position: "relative"
|
|
853
|
+
},
|
|
854
|
+
children: rowVirtualizer.virtualItems.map((virtualRow) => {
|
|
855
|
+
const row = data[virtualRow.index];
|
|
856
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
857
|
+
"tr",
|
|
858
|
+
{
|
|
859
|
+
className: "rst-row",
|
|
860
|
+
style: {
|
|
861
|
+
position: "absolute",
|
|
862
|
+
top: 0,
|
|
863
|
+
left: 0,
|
|
864
|
+
width: "100%",
|
|
865
|
+
height: `${virtualRow.size}px`,
|
|
866
|
+
transform: `translateY(${virtualRow.start}px)`
|
|
867
|
+
},
|
|
868
|
+
children: columns.map((colKey) => {
|
|
869
|
+
const val = row[colKey];
|
|
870
|
+
const colInfo = schema[colKey];
|
|
871
|
+
const Renderer = getCellRenderer(colInfo.type, registry);
|
|
872
|
+
const widthClass = getColumnWidthClass(colKey, colInfo.type);
|
|
873
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
874
|
+
"td",
|
|
875
|
+
{
|
|
876
|
+
className: `rst-td rst-td-col-${colKey} rst-td-type-${colInfo.type} ${widthClass}`,
|
|
877
|
+
children: colInfo.type === "boolean" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rst-badge ${val ? "rst-badge-yes" : "rst-badge-no"}`, children: val ? "Yes" : "No" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Renderer, { value: val })
|
|
878
|
+
},
|
|
879
|
+
colKey
|
|
880
|
+
);
|
|
881
|
+
})
|
|
882
|
+
},
|
|
883
|
+
virtualRow.index
|
|
884
|
+
);
|
|
885
|
+
})
|
|
886
|
+
}
|
|
887
|
+
);
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
// src/components/FilterPanel.tsx
|
|
891
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
892
|
+
var FilterPanel = ({ schema, filters, onFilterChange, onClear }) => {
|
|
893
|
+
const handleStringChange = (key, e) => {
|
|
894
|
+
onFilterChange(key, e.target.value || null);
|
|
895
|
+
};
|
|
896
|
+
const handleNumberChange = (key, field, value) => {
|
|
897
|
+
const current = filters[key] || {};
|
|
898
|
+
const num = parseFloat(value);
|
|
899
|
+
const next = { ...current };
|
|
900
|
+
if (isNaN(num)) {
|
|
901
|
+
delete next[field];
|
|
902
|
+
} else {
|
|
903
|
+
next[field] = num;
|
|
904
|
+
}
|
|
905
|
+
if (Object.keys(next).length === 0) {
|
|
906
|
+
onFilterChange(key, null);
|
|
907
|
+
} else {
|
|
908
|
+
onFilterChange(key, next);
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
const handleDateChange = (key, field, value) => {
|
|
912
|
+
const current = filters[key] || {};
|
|
913
|
+
const next = { ...current };
|
|
914
|
+
if (!value) {
|
|
915
|
+
delete next[field];
|
|
916
|
+
} else {
|
|
917
|
+
next[field] = value;
|
|
918
|
+
}
|
|
919
|
+
if (Object.keys(next).length === 0) {
|
|
920
|
+
onFilterChange(key, null);
|
|
921
|
+
} else {
|
|
922
|
+
onFilterChange(key, next);
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
const handleBooleanToggle = (key) => {
|
|
926
|
+
const current = filters[key];
|
|
927
|
+
if (current === true) {
|
|
928
|
+
onFilterChange(key, false);
|
|
929
|
+
} else if (current === false) {
|
|
930
|
+
onFilterChange(key, null);
|
|
931
|
+
} else {
|
|
932
|
+
onFilterChange(key, true);
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rst-filter-panel", children: [
|
|
936
|
+
Object.entries(schema).map(([key, colInfo]) => {
|
|
937
|
+
const type = colInfo.type;
|
|
938
|
+
const currentFilter = filters[key];
|
|
939
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `rst-filter-group rst-filter-type-${type}`, children: [
|
|
940
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { children: [
|
|
941
|
+
toTitleCase(key),
|
|
942
|
+
" Filter"
|
|
943
|
+
] }),
|
|
944
|
+
(type === "string" || type === "email" || type === "url") && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
945
|
+
"input",
|
|
946
|
+
{
|
|
947
|
+
type: "text",
|
|
948
|
+
placeholder: `Search ${toTitleCase(key)}...`,
|
|
949
|
+
value: currentFilter || "",
|
|
950
|
+
onChange: (e) => handleStringChange(key, e)
|
|
951
|
+
}
|
|
952
|
+
),
|
|
953
|
+
(type === "number" || type === "currency" || type === "percentage") && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rst-filter-range", children: [
|
|
954
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
955
|
+
"input",
|
|
956
|
+
{
|
|
957
|
+
type: "number",
|
|
958
|
+
placeholder: "Min",
|
|
959
|
+
value: currentFilter?.min ?? "",
|
|
960
|
+
onChange: (e) => handleNumberChange(key, "min", e.target.value)
|
|
961
|
+
}
|
|
962
|
+
),
|
|
963
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
964
|
+
"input",
|
|
965
|
+
{
|
|
966
|
+
type: "number",
|
|
967
|
+
placeholder: "Max",
|
|
968
|
+
value: currentFilter?.max ?? "",
|
|
969
|
+
onChange: (e) => handleNumberChange(key, "max", e.target.value)
|
|
970
|
+
}
|
|
971
|
+
)
|
|
972
|
+
] }),
|
|
973
|
+
type === "date" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rst-filter-range", children: [
|
|
974
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
975
|
+
"input",
|
|
976
|
+
{
|
|
977
|
+
type: "date",
|
|
978
|
+
title: "Start Date",
|
|
979
|
+
value: currentFilter?.start || "",
|
|
980
|
+
onChange: (e) => handleDateChange(key, "start", e.target.value)
|
|
981
|
+
}
|
|
982
|
+
),
|
|
983
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
984
|
+
"input",
|
|
985
|
+
{
|
|
986
|
+
type: "date",
|
|
987
|
+
title: "End Date",
|
|
988
|
+
value: currentFilter?.end || "",
|
|
989
|
+
onChange: (e) => handleDateChange(key, "end", e.target.value)
|
|
990
|
+
}
|
|
991
|
+
)
|
|
992
|
+
] }),
|
|
993
|
+
type === "boolean" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
994
|
+
"button",
|
|
995
|
+
{
|
|
996
|
+
className: "rst-filter-toggle",
|
|
997
|
+
onClick: () => handleBooleanToggle(key),
|
|
998
|
+
style: {
|
|
999
|
+
backgroundColor: currentFilter === true ? "#e6f4ea" : currentFilter === false ? "#fce8e6" : "#fff"
|
|
1000
|
+
},
|
|
1001
|
+
children: currentFilter === true ? "Only Yes" : currentFilter === false ? "Only No" : "All"
|
|
1002
|
+
}
|
|
1003
|
+
)
|
|
1004
|
+
] }, key);
|
|
1005
|
+
}),
|
|
1006
|
+
Object.keys(filters).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1007
|
+
"button",
|
|
1008
|
+
{
|
|
1009
|
+
className: "rst-filter-clear-btn",
|
|
1010
|
+
onClick: onClear,
|
|
1011
|
+
children: "Clear All Filters"
|
|
1012
|
+
}
|
|
1013
|
+
)
|
|
1014
|
+
] });
|
|
1015
|
+
};
|
|
1016
|
+
|
|
1017
|
+
// src/components/InsightsPanel.tsx
|
|
1018
|
+
var import_react5 = require("react");
|
|
1019
|
+
var import_recharts = require("recharts");
|
|
1020
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1021
|
+
var COLORS = ["#8884d8", "#82ca9d", "#ffc658", "#ff8042", "#a4de6c", "#d0ed57"];
|
|
1022
|
+
var InsightsPanel = ({ data, schema }) => {
|
|
1023
|
+
const insights = (0, import_react5.useMemo)(() => {
|
|
1024
|
+
return generateInsights(data, schema);
|
|
1025
|
+
}, [data, schema]);
|
|
1026
|
+
if (!insights || insights.length === 0) {
|
|
1027
|
+
return null;
|
|
1028
|
+
}
|
|
1029
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "rst-insights-panel", children: [
|
|
1030
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { children: "AI Generated Insights" }),
|
|
1031
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "rst-insights-grid", children: insights.map((widget) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "rst-insight-card", children: [
|
|
1032
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h4", { className: "rst-insight-title", children: widget.title }),
|
|
1033
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "rst-insight-chart-wrapper", style: { height: "240px" }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.ResponsiveContainer, { width: "100%", height: "100%", children: widget.type === "bar" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_recharts.BarChart, { data: widget.data, margin: { top: 5, right: 10, left: 0, bottom: 5 }, children: [
|
|
1034
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }),
|
|
1035
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.XAxis, { dataKey: widget.xAxisKey, tick: { fontSize: 11, fill: "#64748b" }, axisLine: { stroke: "#e2e8f0" }, tickLine: false }),
|
|
1036
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.YAxis, { tick: { fontSize: 11, fill: "#64748b" }, axisLine: { stroke: "#e2e8f0" }, tickLine: false }),
|
|
1037
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1038
|
+
import_recharts.Tooltip,
|
|
1039
|
+
{
|
|
1040
|
+
contentStyle: { borderRadius: "8px", border: "none", boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1)" },
|
|
1041
|
+
cursor: { fill: "#f1f5f9" }
|
|
1042
|
+
}
|
|
1043
|
+
),
|
|
1044
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.Legend, { wrapperStyle: { fontSize: "11px", paddingTop: "10px" }, iconType: "circle" }),
|
|
1045
|
+
widget.yAxisKeys.map((yKey, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.Bar, { dataKey: yKey, fill: COLORS[index % COLORS.length], radius: [4, 4, 0, 0], barSize: 32 }, yKey))
|
|
1046
|
+
] }) : widget.type === "line" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_recharts.LineChart, { data: widget.data, margin: { top: 5, right: 10, left: 0, bottom: 5 }, children: [
|
|
1047
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }),
|
|
1048
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.XAxis, { dataKey: widget.xAxisKey, tick: { fontSize: 11, fill: "#64748b" }, axisLine: { stroke: "#e2e8f0" }, tickLine: false }),
|
|
1049
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.YAxis, { tick: { fontSize: 11, fill: "#64748b" }, axisLine: { stroke: "#e2e8f0" }, tickLine: false }),
|
|
1050
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.Tooltip, { contentStyle: { borderRadius: "8px", border: "none", boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1)" } }),
|
|
1051
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.Legend, { wrapperStyle: { fontSize: "11px", paddingTop: "10px" }, iconType: "circle" }),
|
|
1052
|
+
widget.yAxisKeys.map((yKey, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.Line, { type: "monotone", dataKey: yKey, stroke: COLORS[index % COLORS.length], strokeWidth: 2.5, dot: { r: 4, strokeWidth: 2, fill: "#fff" }, activeDot: { r: 6, strokeWidth: 0 } }, yKey))
|
|
1053
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_recharts.PieChart, { children: [
|
|
1054
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.Tooltip, { contentStyle: { borderRadius: "8px", border: "none", boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1)" } }),
|
|
1055
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.Legend, { wrapperStyle: { fontSize: "11px" }, iconType: "circle" }),
|
|
1056
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1057
|
+
import_recharts.Pie,
|
|
1058
|
+
{
|
|
1059
|
+
data: widget.data,
|
|
1060
|
+
cx: "50%",
|
|
1061
|
+
cy: "45%",
|
|
1062
|
+
innerRadius: 60,
|
|
1063
|
+
outerRadius: 80,
|
|
1064
|
+
fill: "#8884d8",
|
|
1065
|
+
paddingAngle: 5,
|
|
1066
|
+
dataKey: widget.yAxisKeys[0],
|
|
1067
|
+
nameKey: widget.xAxisKey,
|
|
1068
|
+
label: false,
|
|
1069
|
+
children: widget.data.map((entry, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_recharts.Cell, { fill: COLORS[index % COLORS.length] }, `cell-${index}`))
|
|
1070
|
+
}
|
|
1071
|
+
)
|
|
1072
|
+
] }) }) })
|
|
1073
|
+
] }, widget.id)) })
|
|
1074
|
+
] });
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
// src/plugins/pluginRegistry.ts
|
|
1078
|
+
var PluginRegistry = class {
|
|
1079
|
+
constructor() {
|
|
1080
|
+
this.plugins = [];
|
|
1081
|
+
}
|
|
1082
|
+
register(pluginOrPlugins) {
|
|
1083
|
+
if (Array.isArray(pluginOrPlugins)) {
|
|
1084
|
+
this.plugins.push(...pluginOrPlugins);
|
|
1085
|
+
} else {
|
|
1086
|
+
this.plugins.push(pluginOrPlugins);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
clear() {
|
|
1090
|
+
this.plugins = [];
|
|
1091
|
+
}
|
|
1092
|
+
runDetectors(columnKey, sample) {
|
|
1093
|
+
for (const plugin of this.plugins) {
|
|
1094
|
+
if (plugin.detect) {
|
|
1095
|
+
const detectedType = plugin.detect(columnKey, sample);
|
|
1096
|
+
if (detectedType) {
|
|
1097
|
+
return detectedType;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
return null;
|
|
1102
|
+
}
|
|
1103
|
+
getRenderers() {
|
|
1104
|
+
const renderers = {};
|
|
1105
|
+
for (const plugin of this.plugins) {
|
|
1106
|
+
}
|
|
1107
|
+
return renderers;
|
|
1108
|
+
}
|
|
1109
|
+
getPlugins() {
|
|
1110
|
+
return this.plugins;
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
var globalPluginRegistry = new PluginRegistry();
|
|
1114
|
+
|
|
1115
|
+
// src/components/SmartTable.tsx
|
|
1116
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1117
|
+
var SmartTable = ({
|
|
1118
|
+
data,
|
|
1119
|
+
sortable = false,
|
|
1120
|
+
filterable = false,
|
|
1121
|
+
pagination = false,
|
|
1122
|
+
insights = false,
|
|
1123
|
+
plugins = []
|
|
1124
|
+
}) => {
|
|
1125
|
+
const registry = (0, import_react6.useMemo)(() => {
|
|
1126
|
+
const reg = new PluginRegistry();
|
|
1127
|
+
if (plugins && plugins.length > 0) {
|
|
1128
|
+
reg.register(plugins);
|
|
1129
|
+
}
|
|
1130
|
+
return reg;
|
|
1131
|
+
}, [plugins]);
|
|
1132
|
+
const schema = (0, import_react6.useMemo)(() => {
|
|
1133
|
+
return buildSchema(data, registry);
|
|
1134
|
+
}, [data, registry]);
|
|
1135
|
+
const { filteredData, filters, setFilter, clearFilters } = useFilters(data, schema);
|
|
1136
|
+
const dataToFilter = filterable ? filteredData : data;
|
|
1137
|
+
const { sortedData, sortColumn, sortDirection, handleSort } = useSorting(dataToFilter, schema);
|
|
1138
|
+
const dataToRenderOrPaginate = sortable ? sortedData : dataToFilter;
|
|
1139
|
+
const { paginatedData, currentPage, totalPages, setPage } = usePagination(dataToRenderOrPaginate, 10);
|
|
1140
|
+
const finalData = pagination ? paginatedData : dataToRenderOrPaginate;
|
|
1141
|
+
const [showFilters, setShowFilters] = import_react6.default.useState(true);
|
|
1142
|
+
if (!data || data.length === 0) {
|
|
1143
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rst-container", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rst-empty", children: "No data to display." }) });
|
|
1144
|
+
}
|
|
1145
|
+
const summaryStats = /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rst-summary-strip", children: [
|
|
1146
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { children: [
|
|
1147
|
+
data.length,
|
|
1148
|
+
" Records"
|
|
1149
|
+
] }),
|
|
1150
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rst-dot", children: "\u2022" }),
|
|
1151
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { children: [
|
|
1152
|
+
Object.keys(schema).length,
|
|
1153
|
+
" Columns"
|
|
1154
|
+
] }),
|
|
1155
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rst-dot", children: "\u2022" }),
|
|
1156
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { children: [
|
|
1157
|
+
"Types: ",
|
|
1158
|
+
Array.from(new Set(Object.values(schema).map((c) => c.type))).join(", ")
|
|
1159
|
+
] })
|
|
1160
|
+
] });
|
|
1161
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rst-container", children: [
|
|
1162
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("header", { className: "rst-main-header", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-end" }, children: [
|
|
1163
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
1164
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h2", { children: "Smart Table" }),
|
|
1165
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { children: "Auto-generated insights, filters, and records" })
|
|
1166
|
+
] }),
|
|
1167
|
+
filterable && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
1168
|
+
"button",
|
|
1169
|
+
{
|
|
1170
|
+
className: "rst-toggle-filters-btn",
|
|
1171
|
+
onClick: () => setShowFilters(!showFilters),
|
|
1172
|
+
children: [
|
|
1173
|
+
"Filters ",
|
|
1174
|
+
showFilters ? "\u25B2" : "\u25BC"
|
|
1175
|
+
]
|
|
1176
|
+
}
|
|
1177
|
+
)
|
|
1178
|
+
] }) }),
|
|
1179
|
+
insights && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(InsightsPanel, { data: dataToRenderOrPaginate, schema }),
|
|
1180
|
+
summaryStats,
|
|
1181
|
+
filterable && showFilters && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("section", { className: "rst-card", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1182
|
+
FilterPanel,
|
|
1183
|
+
{
|
|
1184
|
+
schema,
|
|
1185
|
+
filters,
|
|
1186
|
+
onFilterChange: setFilter,
|
|
1187
|
+
onClear: clearFilters
|
|
1188
|
+
}
|
|
1189
|
+
) }),
|
|
1190
|
+
data.length > 0 && finalData.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rst-empty-results-card", children: [
|
|
1191
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rst-empty-icon", children: "\u{1F50D}" }),
|
|
1192
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h3", { children: "No results found" }),
|
|
1193
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { children: "Try adjusting your filters to find what you're looking for." }),
|
|
1194
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { className: "btn btn-primary", onClick: clearFilters, children: "Clear All Filters" })
|
|
1195
|
+
] }),
|
|
1196
|
+
data.length > 0 && finalData.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("section", { className: "rst-card", children: [
|
|
1197
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rst-table-container", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("table", { className: "rst-table", children: [
|
|
1198
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1199
|
+
TableHeader,
|
|
1200
|
+
{
|
|
1201
|
+
schema,
|
|
1202
|
+
sortable,
|
|
1203
|
+
sortColumn,
|
|
1204
|
+
sortDirection,
|
|
1205
|
+
onSort: handleSort
|
|
1206
|
+
}
|
|
1207
|
+
),
|
|
1208
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TableBody, { data: finalData, schema, registry })
|
|
1209
|
+
] }) }),
|
|
1210
|
+
pagination && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rst-pagination", children: [
|
|
1211
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1212
|
+
"button",
|
|
1213
|
+
{
|
|
1214
|
+
disabled: currentPage === 1,
|
|
1215
|
+
onClick: () => setPage(currentPage - 1),
|
|
1216
|
+
children: "Previous"
|
|
1217
|
+
}
|
|
1218
|
+
),
|
|
1219
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { children: [
|
|
1220
|
+
"Page ",
|
|
1221
|
+
currentPage,
|
|
1222
|
+
" of ",
|
|
1223
|
+
totalPages
|
|
1224
|
+
] }),
|
|
1225
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1226
|
+
"button",
|
|
1227
|
+
{
|
|
1228
|
+
disabled: currentPage === totalPages,
|
|
1229
|
+
onClick: () => setPage(currentPage + 1),
|
|
1230
|
+
children: "Next"
|
|
1231
|
+
}
|
|
1232
|
+
)
|
|
1233
|
+
] })
|
|
1234
|
+
] })
|
|
1235
|
+
] });
|
|
1236
|
+
};
|
|
1237
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1238
|
+
0 && (module.exports = {
|
|
1239
|
+
FilterPanel,
|
|
1240
|
+
InsightsPanel,
|
|
1241
|
+
PluginRegistry,
|
|
1242
|
+
SmartTable,
|
|
1243
|
+
aggregateData,
|
|
1244
|
+
aggregateDataByCount,
|
|
1245
|
+
buildSchema,
|
|
1246
|
+
computeColumnStats,
|
|
1247
|
+
currencyPlugin,
|
|
1248
|
+
detectChartFields,
|
|
1249
|
+
detectValueType,
|
|
1250
|
+
generateInsights,
|
|
1251
|
+
getCellRenderer,
|
|
1252
|
+
globalPluginRegistry,
|
|
1253
|
+
percentagePlugin,
|
|
1254
|
+
sampleDataset,
|
|
1255
|
+
useFilters,
|
|
1256
|
+
usePagination,
|
|
1257
|
+
useSorting
|
|
1258
|
+
});
|
|
1259
|
+
//# sourceMappingURL=index.js.map
|