@pattern-stack/frontend-patterns 0.2.0-alpha.8 → 0.2.0-alpha.9

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 (29) hide show
  1. package/dist/atoms/components/core/Badge/Badge.d.ts +1 -1
  2. package/dist/atoms/components/data/DataTable/DataTable.d.ts +2 -2
  3. package/dist/atoms/components/data/DataTable/DataTable.d.ts.map +1 -1
  4. package/dist/atoms/components/data/DataTable/DataTable.types.d.ts +40 -2
  5. package/dist/atoms/components/data/DataTable/DataTable.types.d.ts.map +1 -1
  6. package/dist/atoms/hooks/index.d.ts +1 -0
  7. package/dist/atoms/hooks/index.d.ts.map +1 -1
  8. package/dist/atoms/hooks/useAdaptiveTable.d.ts +49 -0
  9. package/dist/atoms/hooks/useAdaptiveTable.d.ts.map +1 -0
  10. package/dist/atoms/hooks/useApi.d.ts +1 -1
  11. package/dist/atoms/hooks/useApi.d.ts.map +1 -1
  12. package/dist/atoms/hooks/useResponsiveTable.d.ts +44 -24
  13. package/dist/atoms/hooks/useResponsiveTable.d.ts.map +1 -1
  14. package/dist/atoms/shared/config/table-config.d.ts +79 -0
  15. package/dist/atoms/shared/config/table-config.d.ts.map +1 -0
  16. package/dist/atoms/shared/index.d.ts +1 -0
  17. package/dist/atoms/shared/index.d.ts.map +1 -1
  18. package/dist/atoms/utils/entity-card-mapping.d.ts +1 -1
  19. package/dist/atoms/utils/entity-card-mapping.d.ts.map +1 -1
  20. package/dist/atoms/utils/index.d.ts +1 -0
  21. package/dist/atoms/utils/index.d.ts.map +1 -1
  22. package/dist/index.es.js +917 -179
  23. package/dist/index.es.js.map +1 -1
  24. package/dist/index.js +915 -177
  25. package/dist/index.js.map +1 -1
  26. package/dist/molecules/layout/navigation-context.d.ts.map +1 -1
  27. package/dist/templates/factory.d.ts +11 -0
  28. package/dist/templates/factory.d.ts.map +1 -1
  29. package/package.json +5 -3
package/dist/index.js CHANGED
@@ -179,6 +179,141 @@ const RESPONSIVE_CHART_HEIGHTS = {
179
179
  secondary: DASHBOARD_CHART_HEIGHTS.medium
180
180
  }
181
181
  };
182
+ const COLUMN_WIDTHS = {
183
+ // Text
184
+ text: 180,
185
+ password: 120,
186
+ // Numbers
187
+ number: 80,
188
+ money: 100,
189
+ percent: 70,
190
+ // Dates
191
+ date: 100,
192
+ datetime: 140,
193
+ // Contact/Links
194
+ email: 180,
195
+ phone: 120,
196
+ url: 160,
197
+ // Visual indicators
198
+ status: 90,
199
+ badge: 90,
200
+ boolean: 60,
201
+ rating: 80,
202
+ color: 60,
203
+ // Entity references
204
+ entity: 140,
205
+ user: 140,
206
+ // Special
207
+ image: 60,
208
+ file: 100,
209
+ json: 200
210
+ };
211
+ const COMPACT_COLUMN_WIDTHS = {
212
+ text: 120,
213
+ // With truncation
214
+ money: 60,
215
+ // $2.5k format
216
+ date: 55,
217
+ // "Dec 5" or "Tu"
218
+ datetime: 65,
219
+ // "5m ago"
220
+ email: 100,
221
+ // Truncated
222
+ phone: 80,
223
+ url: 80,
224
+ entity: 100,
225
+ user: 80,
226
+ json: 80
227
+ };
228
+ const COMPACT_FORMATTERS = {
229
+ money: "compactCurrency",
230
+ // $2,456.99 → $2.5k
231
+ date: "shortDate",
232
+ // 2024-01-15 → Dec 5
233
+ datetime: "relative",
234
+ // 2024-01-15T10:30 → 5m ago
235
+ text: "truncate",
236
+ // Long customer name → Long cust...
237
+ email: "truncate",
238
+ url: "domain",
239
+ // https://example.com/path → example.com
240
+ number: "compact"
241
+ // 1234567 → 1.2M
242
+ };
243
+ const COMPACT_LABELS = {
244
+ // Common field names → short versions
245
+ customer: "Cust",
246
+ description: "Desc",
247
+ quantity: "Qty",
248
+ amount: "Amt",
249
+ total: "Tot",
250
+ priority: "Pri",
251
+ status: "Stat",
252
+ created_at: "Created",
253
+ updated_at: "Updated",
254
+ email: "Email",
255
+ phone: "Ph",
256
+ category: "Cat",
257
+ // Time fields
258
+ time: "Time",
259
+ timeAgo: "Time",
260
+ timestamp: "Time",
261
+ // ID fields
262
+ order_id: "#",
263
+ id: "#",
264
+ transaction_id: "Txn"
265
+ };
266
+ const TABLE_BREAKPOINTS = {
267
+ /** Below this width (px), always use cards */
268
+ minForTable: 400,
269
+ /** Buffer (px) to add when calculating if columns fit */
270
+ widthBuffer: 50,
271
+ /** Minimum columns to show in compact mode */
272
+ minCompactColumns: 3,
273
+ /** Maximum columns in compact mode */
274
+ maxCompactColumns: 6
275
+ };
276
+ const IMPORTANCE_VISIBILITY = {
277
+ full: ["primary", "secondary", "tertiary", "critical", "high", "medium", "low", "minimal"],
278
+ compact: ["primary", "secondary", "critical", "high", "medium"],
279
+ cards: ["primary", "critical", "high"]
280
+ };
281
+ function getColumnWidth(type, mode = "full") {
282
+ if (mode === "compact" && type in COMPACT_COLUMN_WIDTHS) {
283
+ return COMPACT_COLUMN_WIDTHS[type];
284
+ }
285
+ return COLUMN_WIDTHS[type] ?? COLUMN_WIDTHS.text;
286
+ }
287
+ function calculateTableWidth(columns, mode = "full") {
288
+ return columns.reduce((sum, col) => sum + getColumnWidth(col.type, mode), 0);
289
+ }
290
+ function getCompactLabel(fieldName, originalLabel) {
291
+ const normalized = fieldName.toLowerCase();
292
+ return COMPACT_LABELS[normalized] ?? originalLabel;
293
+ }
294
+ function getCompactFormatter(type) {
295
+ return COMPACT_FORMATTERS[type];
296
+ }
297
+ function determineTableMode(containerWidth, columns) {
298
+ const { minForTable, widthBuffer } = TABLE_BREAKPOINTS;
299
+ if (containerWidth < minForTable) {
300
+ return "cards";
301
+ }
302
+ const fullWidth = calculateTableWidth(columns, "full");
303
+ const compactColumns = columns.filter(
304
+ (col) => IMPORTANCE_VISIBILITY.compact.includes(
305
+ col.importance ?? "medium"
306
+ )
307
+ );
308
+ const compactWidth = calculateTableWidth(compactColumns, "compact");
309
+ if (fullWidth + widthBuffer <= containerWidth) {
310
+ return "full";
311
+ }
312
+ if (compactWidth + widthBuffer <= containerWidth) {
313
+ return "compact";
314
+ }
315
+ return "cards";
316
+ }
182
317
  const breakpoints = {
183
318
  "2xs": 400,
184
319
  xs: 500,
@@ -2052,6 +2187,498 @@ function renderField(value, fieldName, fieldType, breakpoint, item, customRender
2052
2187
  function cn(...inputs) {
2053
2188
  return tailwindMerge.twMerge(clsx.clsx(inputs));
2054
2189
  }
2190
+ const IconBadge = ({
2191
+ children,
2192
+ icon,
2193
+ variant = "category",
2194
+ category = 1,
2195
+ status = "neutral",
2196
+ size = "md",
2197
+ interactive = false,
2198
+ onClick,
2199
+ className,
2200
+ tooltip
2201
+ }) => {
2202
+ const currentSize = useResponsiveValue(size);
2203
+ const sizeConfig = responsiveScales.components.iconBadge[currentSize];
2204
+ const sizeClasses2 = {
2205
+ xs: `${(sizeConfig == null ? void 0 : sizeConfig.size) || "w-6 h-6"} ${(sizeConfig == null ? void 0 : sizeConfig.text) || "text-[10px]"} ${(sizeConfig == null ? void 0 : sizeConfig.rounded) || "rounded-md"}`,
2206
+ sm: "w-8 h-8 text-xs rounded-lg",
2207
+ md: "w-10 h-10 text-sm rounded-xl",
2208
+ lg: "w-12 h-12 text-base rounded-xl"
2209
+ };
2210
+ const baseClasses = cn(
2211
+ "inline-flex items-center justify-center font-bold shadow-md",
2212
+ sizeClasses2[currentSize],
2213
+ interactive && [
2214
+ "cursor-pointer",
2215
+ getAnimationClasses({
2216
+ ...animationPresets.dataBadge,
2217
+ size: currentSize === "lg" ? "lg" : currentSize === "sm" || currentSize === "xs" ? "sm" : "md"
2218
+ })
2219
+ ],
2220
+ className
2221
+ );
2222
+ const gradientClasses = variant === "category" ? `bg-gradient-to-br from-category-${category} to-category-${Math.min(category + 1, 8)}` : variant === "status" ? `bg-gradient-to-br from-status-${status} to-status-${status}` : "bg-gradient-to-br from-category-1 to-category-2";
2223
+ const badge = /* @__PURE__ */ jsxRuntime.jsxs(
2224
+ "div",
2225
+ {
2226
+ className: cn(baseClasses, gradientClasses, "animate-fade-in"),
2227
+ onClick,
2228
+ role: interactive ? "button" : void 0,
2229
+ tabIndex: interactive ? 0 : void 0,
2230
+ onKeyDown: interactive ? (e) => {
2231
+ if (e.key === "Enter" || e.key === " ") {
2232
+ e.preventDefault();
2233
+ onClick == null ? void 0 : onClick();
2234
+ }
2235
+ } : void 0,
2236
+ "data-component-name": "IconBadge",
2237
+ children: [
2238
+ icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary-foreground drop-shadow", children: icon }),
2239
+ children && !icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary-foreground font-bold", children })
2240
+ ]
2241
+ }
2242
+ );
2243
+ if (tooltip) {
2244
+ return /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: tooltip, position: "top", size: "sm", children: badge });
2245
+ }
2246
+ return badge;
2247
+ };
2248
+ const valueColorMap = {
2249
+ success: "text-green-600",
2250
+ error: "text-red-600",
2251
+ warning: "text-yellow-600",
2252
+ info: "text-blue-600",
2253
+ neutral: "text-muted-foreground",
2254
+ default: "text-foreground"
2255
+ };
2256
+ function ListCard({
2257
+ icon,
2258
+ title,
2259
+ subtitle,
2260
+ metadata,
2261
+ value,
2262
+ badge,
2263
+ onClick,
2264
+ className,
2265
+ children
2266
+ }) {
2267
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2268
+ "div",
2269
+ {
2270
+ className: cn(
2271
+ "flex items-start gap-3 p-3 rounded-lg border border-border transition-colors",
2272
+ onClick && "hover:bg-muted/50 cursor-pointer",
2273
+ className
2274
+ ),
2275
+ onClick,
2276
+ children: [
2277
+ icon && /* @__PURE__ */ jsxRuntime.jsx(
2278
+ IconBadge,
2279
+ {
2280
+ variant: icon.variant || "category",
2281
+ category: icon.variant === "category" ? icon.category || 1 : void 0,
2282
+ status: icon.variant === "status" ? icon.status || "neutral" : void 0,
2283
+ size: icon.size || "sm",
2284
+ icon: icon.icon,
2285
+ tooltip: icon.tooltip
2286
+ }
2287
+ ),
2288
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
2289
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
2290
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
2291
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-medium text-foreground truncate", children: title }),
2292
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: subtitle }),
2293
+ metadata && metadata.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3 mt-1", children: metadata.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: item }, index)) })
2294
+ ] }),
2295
+ (value || badge) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
2296
+ value && /* @__PURE__ */ jsxRuntime.jsx(
2297
+ "p",
2298
+ {
2299
+ className: cn(
2300
+ "text-sm font-semibold",
2301
+ value.variant && valueColorMap[value.variant],
2302
+ value.className
2303
+ ),
2304
+ children: value.text
2305
+ }
2306
+ ),
2307
+ badge && /* @__PURE__ */ jsxRuntime.jsx(
2308
+ DataBadge,
2309
+ {
2310
+ variant: badge.variant,
2311
+ status: badge.variant === "status" ? badge.status : void 0,
2312
+ category: badge.variant === "category" ? badge.category : void 0,
2313
+ size: badge.size || "sm",
2314
+ icon: badge.icon,
2315
+ className: "mt-1",
2316
+ children: badge.text
2317
+ }
2318
+ )
2319
+ ] })
2320
+ ] }),
2321
+ children
2322
+ ] })
2323
+ ]
2324
+ }
2325
+ );
2326
+ }
2327
+ const STATUS_COLOR_MAP = {
2328
+ // Success states
2329
+ active: "success",
2330
+ completed: "success",
2331
+ approved: "success",
2332
+ paid: "success",
2333
+ delivered: "success",
2334
+ resolved: "success",
2335
+ closed: "success",
2336
+ // Warning states
2337
+ pending: "warning",
2338
+ processing: "warning",
2339
+ review: "warning",
2340
+ draft: "warning",
2341
+ waiting: "warning",
2342
+ // Error states
2343
+ failed: "error",
2344
+ rejected: "error",
2345
+ cancelled: "error",
2346
+ overdue: "error",
2347
+ blocked: "error",
2348
+ // Info states
2349
+ new: "info",
2350
+ open: "info",
2351
+ in_progress: "info",
2352
+ scheduled: "info",
2353
+ // Neutral (default)
2354
+ inactive: "neutral",
2355
+ archived: "neutral",
2356
+ unknown: "neutral"
2357
+ };
2358
+ function getStatusColor(value) {
2359
+ if (typeof value !== "string") return "neutral";
2360
+ const normalized = value.toLowerCase().replace(/[_-]/g, "_");
2361
+ return STATUS_COLOR_MAP[normalized] ?? "neutral";
2362
+ }
2363
+ const CATEGORY_MAP = {
2364
+ account: 1,
2365
+ customer: 1,
2366
+ client: 1,
2367
+ user: 2,
2368
+ contact: 2,
2369
+ person: 2,
2370
+ order: 3,
2371
+ sale: 3,
2372
+ transaction: 3,
2373
+ product: 4,
2374
+ item: 4,
2375
+ inventory: 4,
2376
+ task: 5,
2377
+ activity: 5,
2378
+ event: 5,
2379
+ file: 6,
2380
+ document: 6,
2381
+ report: 6,
2382
+ tag: 7,
2383
+ category: 7,
2384
+ label: 7,
2385
+ default: 8
2386
+ };
2387
+ function getCategoryForEntity(entityType) {
2388
+ if (!entityType) return 8;
2389
+ const normalized = entityType.toLowerCase();
2390
+ for (const [key, value] of Object.entries(CATEGORY_MAP)) {
2391
+ if (normalized.includes(key)) return value;
2392
+ }
2393
+ return 8;
2394
+ }
2395
+ function getIconForEntity(entityType) {
2396
+ if (!entityType) return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Circle, { className: "w-4 h-4" });
2397
+ const normalized = entityType.toLowerCase();
2398
+ if (normalized.includes("account") || normalized.includes("customer") || normalized.includes("client")) {
2399
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Building2, { className: "w-4 h-4" });
2400
+ }
2401
+ if (normalized.includes("user") || normalized.includes("contact") || normalized.includes("person")) {
2402
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "w-4 h-4" });
2403
+ }
2404
+ if (normalized.includes("product") || normalized.includes("item")) {
2405
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Package, { className: "w-4 h-4" });
2406
+ }
2407
+ if (normalized.includes("file") || normalized.includes("document")) {
2408
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "w-4 h-4" });
2409
+ }
2410
+ if (normalized.includes("tag") || normalized.includes("category")) {
2411
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Tag, { className: "w-4 h-4" });
2412
+ }
2413
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Circle, { className: "w-4 h-4" });
2414
+ }
2415
+ function normalizeImportance(importance) {
2416
+ switch (importance) {
2417
+ case "critical":
2418
+ case "high":
2419
+ case "primary":
2420
+ return "primary";
2421
+ case "medium":
2422
+ case "secondary":
2423
+ return "secondary";
2424
+ default:
2425
+ return "tertiary";
2426
+ }
2427
+ }
2428
+ function isPrimaryImportance(importance) {
2429
+ return normalizeImportance(importance) === "primary";
2430
+ }
2431
+ function formatValueForCard(value, type, format) {
2432
+ if (value === null || value === void 0) return "—";
2433
+ switch (type) {
2434
+ case "money": {
2435
+ const num = Number(value);
2436
+ const currency = (format == null ? void 0 : format.currency) ?? "USD";
2437
+ if (num >= 1e6) {
2438
+ return new Intl.NumberFormat("en-US", {
2439
+ style: "currency",
2440
+ currency,
2441
+ notation: "compact",
2442
+ maximumFractionDigits: 1
2443
+ }).format(num);
2444
+ }
2445
+ if (num >= 1e3) {
2446
+ return new Intl.NumberFormat("en-US", {
2447
+ style: "currency",
2448
+ currency,
2449
+ notation: "compact",
2450
+ maximumFractionDigits: 1
2451
+ }).format(num);
2452
+ }
2453
+ return new Intl.NumberFormat("en-US", {
2454
+ style: "currency",
2455
+ currency,
2456
+ maximumFractionDigits: 0
2457
+ }).format(num);
2458
+ }
2459
+ case "percent": {
2460
+ const num = Number(value);
2461
+ return `${num.toFixed(0)}%`;
2462
+ }
2463
+ case "number": {
2464
+ const num = Number(value);
2465
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
2466
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
2467
+ return String(num);
2468
+ }
2469
+ case "date":
2470
+ case "datetime": {
2471
+ const date = new Date(String(value));
2472
+ if (isNaN(date.getTime())) return String(value);
2473
+ return date.toLocaleDateString("en-US", {
2474
+ month: "short",
2475
+ day: "numeric"
2476
+ });
2477
+ }
2478
+ case "boolean":
2479
+ return value ? "Yes" : "No";
2480
+ default:
2481
+ return String(value);
2482
+ }
2483
+ }
2484
+ function autoDetectMapping(columns) {
2485
+ var _a, _b, _c, _d, _e, _f;
2486
+ const primaryCols = columns.filter((c) => isPrimaryImportance(c.importance));
2487
+ const titleField = ((_a = primaryCols.find((c) => ["text", "entity", "user"].includes(c.type))) == null ? void 0 : _a.field) ?? ((_b = primaryCols[0]) == null ? void 0 : _b.field) ?? ((_c = columns[0]) == null ? void 0 : _c.field);
2488
+ const valueField = (_d = primaryCols.find((c) => c.type === "money")) == null ? void 0 : _d.field;
2489
+ const statusField = (_e = primaryCols.find(
2490
+ (c) => c.type === "status" || c.type === "badge"
2491
+ )) == null ? void 0 : _e.field;
2492
+ const usedForMain = new Set([titleField, valueField, statusField].filter(Boolean));
2493
+ const remainingPrimary = primaryCols.filter((c) => !usedForMain.has(c.field));
2494
+ const subtitleField = (_f = remainingPrimary[0]) == null ? void 0 : _f.field;
2495
+ const usedFields = new Set(
2496
+ [titleField, valueField, statusField, subtitleField].filter(Boolean)
2497
+ );
2498
+ const metadataFields = primaryCols.filter((c) => !usedFields.has(c.field)).slice(0, 3).map((c) => c.field);
2499
+ return {
2500
+ titleField: titleField ?? "id",
2501
+ subtitleField,
2502
+ valueField,
2503
+ statusField,
2504
+ metadataFields,
2505
+ iconConfig: void 0
2506
+ };
2507
+ }
2508
+ function entityToListCardProps(item, columns, mapping, entityType) {
2509
+ const autoMapping = autoDetectMapping(columns);
2510
+ const effectiveMapping = { ...autoMapping, ...mapping };
2511
+ const {
2512
+ titleField,
2513
+ subtitleField,
2514
+ valueField,
2515
+ statusField,
2516
+ metadataFields,
2517
+ iconConfig
2518
+ } = effectiveMapping;
2519
+ const getColumn = (field) => field ? columns.find((c) => c.field === field) : void 0;
2520
+ const title = item[titleField] != null ? String(item[titleField]) : "—";
2521
+ const subtitle = subtitleField && item[subtitleField] != null ? String(item[subtitleField]) : void 0;
2522
+ let value;
2523
+ if (valueField && item[valueField] != null) {
2524
+ const valueCol = getColumn(valueField);
2525
+ const formatted = formatValueForCard(
2526
+ item[valueField],
2527
+ (valueCol == null ? void 0 : valueCol.type) ?? "text",
2528
+ valueCol == null ? void 0 : valueCol.format
2529
+ );
2530
+ value = {
2531
+ text: formatted,
2532
+ variant: "success"
2533
+ // Money typically green
2534
+ };
2535
+ }
2536
+ let badge;
2537
+ if (statusField && item[statusField] != null) {
2538
+ const statusValue = String(item[statusField]);
2539
+ badge = {
2540
+ text: statusValue,
2541
+ variant: "status",
2542
+ status: getStatusColor(statusValue),
2543
+ size: "sm"
2544
+ };
2545
+ }
2546
+ const metadata = metadataFields == null ? void 0 : metadataFields.map((field) => {
2547
+ const col = getColumn(field);
2548
+ if (!col || item[field] == null) return null;
2549
+ return formatValueForCard(item[field], col.type, col.format);
2550
+ }).filter((v) => v !== null);
2551
+ let icon;
2552
+ if (iconConfig) {
2553
+ const iconValue = iconConfig.field ? item[iconConfig.field] : void 0;
2554
+ if (iconConfig.variant === "status" && iconValue) {
2555
+ icon = {
2556
+ icon: iconConfig.icon ?? getIconForEntity(entityType),
2557
+ variant: "status",
2558
+ status: getStatusColor(iconValue),
2559
+ size: "sm"
2560
+ };
2561
+ } else {
2562
+ icon = {
2563
+ icon: iconConfig.icon ?? getIconForEntity(entityType),
2564
+ variant: "category",
2565
+ category: getCategoryForEntity(entityType),
2566
+ size: "sm"
2567
+ };
2568
+ }
2569
+ } else {
2570
+ icon = {
2571
+ icon: getIconForEntity(entityType),
2572
+ variant: "category",
2573
+ category: getCategoryForEntity(entityType),
2574
+ size: "sm"
2575
+ };
2576
+ }
2577
+ return {
2578
+ icon,
2579
+ title,
2580
+ subtitle,
2581
+ metadata,
2582
+ value,
2583
+ badge
2584
+ };
2585
+ }
2586
+ const HEADER_ABBREVIATIONS = {
2587
+ customer: "Cust",
2588
+ account: "Acct",
2589
+ amount: "Amt",
2590
+ quantity: "Qty",
2591
+ priority: "Pri",
2592
+ status: "Status",
2593
+ created: "Created",
2594
+ updated: "Updated",
2595
+ description: "Desc",
2596
+ total: "Total",
2597
+ items: "Items",
2598
+ number: "#",
2599
+ date: "Date",
2600
+ time: "Time"
2601
+ };
2602
+ function abbreviateHeader(label) {
2603
+ const lower = label.toLowerCase();
2604
+ for (const [key, abbrev] of Object.entries(HEADER_ABBREVIATIONS)) {
2605
+ if (lower.includes(key)) return abbrev;
2606
+ }
2607
+ if (label.length > 8) return label.slice(0, 6) + "…";
2608
+ return label;
2609
+ }
2610
+ function generateCompactColumns(columns, options = {}) {
2611
+ const {
2612
+ maxColumns = 5,
2613
+ abbreviateHeaders = true,
2614
+ requiredFields = [],
2615
+ excludeFields = []
2616
+ } = options;
2617
+ const excludeSet = new Set(excludeFields);
2618
+ const requiredSet = new Set(requiredFields);
2619
+ const filteredColumns = columns.filter((col) => {
2620
+ if (excludeSet.has(col.field)) return false;
2621
+ return true;
2622
+ });
2623
+ const required = filteredColumns.filter((c) => requiredSet.has(c.field));
2624
+ const primary = filteredColumns.filter(
2625
+ (c) => !requiredSet.has(c.field) && normalizeImportance(c.importance) === "primary"
2626
+ );
2627
+ const secondary = filteredColumns.filter(
2628
+ (c) => !requiredSet.has(c.field) && normalizeImportance(c.importance) === "secondary"
2629
+ );
2630
+ const selectedColumns = [...required, ...primary, ...secondary].slice(
2631
+ 0,
2632
+ maxColumns
2633
+ );
2634
+ return selectedColumns.map((col) => ({
2635
+ key: col.field,
2636
+ header: abbreviateHeaders ? abbreviateHeader(col.label) : col.label,
2637
+ sortable: col.sortable,
2638
+ type: col.type,
2639
+ format: col.format,
2640
+ // Compact widths
2641
+ width: getCompactWidth(col.type)
2642
+ }));
2643
+ }
2644
+ function getCompactWidth(type) {
2645
+ switch (type) {
2646
+ case "money":
2647
+ return "70px";
2648
+ case "number":
2649
+ case "percent":
2650
+ return "50px";
2651
+ case "status":
2652
+ case "badge":
2653
+ return "80px";
2654
+ case "date":
2655
+ return "70px";
2656
+ case "datetime":
2657
+ return "90px";
2658
+ case "boolean":
2659
+ return "50px";
2660
+ default:
2661
+ return "120px";
2662
+ }
2663
+ }
2664
+ function createMobileCardRenderer(columns, mapping, entityType) {
2665
+ return ({ data, onItemClick }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: data.map((item, index) => {
2666
+ const cardProps = entityToListCardProps(
2667
+ item,
2668
+ columns,
2669
+ mapping,
2670
+ entityType
2671
+ );
2672
+ return /* @__PURE__ */ jsxRuntime.jsx(
2673
+ ListCard,
2674
+ {
2675
+ ...cardProps,
2676
+ onClick: onItemClick ? () => onItemClick(item) : void 0
2677
+ },
2678
+ item.id ?? index
2679
+ );
2680
+ }) });
2681
+ }
2055
2682
  let globalAuthService = null;
2056
2683
  function setGlobalAuthService(authService) {
2057
2684
  globalAuthService = authService;
@@ -2217,10 +2844,10 @@ function useApiMutation(mutationFn, options) {
2217
2844
  const queryClient = reactQuery.useQueryClient();
2218
2845
  return reactQuery.useMutation({
2219
2846
  mutationFn,
2220
- onSuccess: (data, variables, context) => {
2847
+ onSuccess: (...args) => {
2221
2848
  var _a;
2222
- queryClient.invalidateQueries();
2223
- (_a = options == null ? void 0 : options.onSuccess) == null ? void 0 : _a.call(options, data, variables, context);
2849
+ void queryClient.invalidateQueries({});
2850
+ (_a = options == null ? void 0 : options.onSuccess) == null ? void 0 : _a.call(options, ...args);
2224
2851
  },
2225
2852
  ...options
2226
2853
  });
@@ -2295,43 +2922,87 @@ function useFieldMetadata({
2295
2922
  }
2296
2923
  function useResponsiveTable(options = {}) {
2297
2924
  const {
2925
+ columns,
2298
2926
  initialView = "full",
2299
2927
  autoSwitch = true,
2300
- minWidthForCompact = 400,
2928
+ minWidthForCompact = TABLE_BREAKPOINTS.minForTable,
2301
2929
  debounceMs = 100,
2302
2930
  onViewChange
2303
2931
  } = options;
2304
2932
  const [viewMode, setViewModeState] = React.useState(initialView);
2305
2933
  const [isAutoSwitching, setIsAutoSwitching] = React.useState(autoSwitch);
2934
+ const [containerWidth, setContainerWidth] = React.useState(0);
2306
2935
  const [overflowState, setOverflowState] = React.useState({
2307
2936
  fullOverflows: false,
2308
2937
  compactOverflows: false
2309
2938
  });
2939
+ const containerRef = React.useRef(null);
2310
2940
  const fullTableRef = React.useRef(null);
2311
2941
  const compactTableRef = React.useRef(null);
2312
2942
  const debounceTimerRef = React.useRef(null);
2313
2943
  const isTransitioningRef = React.useRef(false);
2944
+ const compactColumns = React.useCallback(() => {
2945
+ if (!columns) return null;
2946
+ const validImportance = IMPORTANCE_VISIBILITY.compact;
2947
+ const filtered = columns.filter(
2948
+ (col) => validImportance.includes(col.importance ?? "medium")
2949
+ );
2950
+ const limited = filtered.slice(0, TABLE_BREAKPOINTS.maxCompactColumns);
2951
+ return limited.map((col) => ({
2952
+ ...col,
2953
+ label: getCompactLabel(col.field, col.label)
2954
+ }));
2955
+ }, [columns])();
2956
+ const calculatedWidths = React.useCallback(() => {
2957
+ if (!columns) {
2958
+ return { full: 0, compact: 0, container: containerWidth };
2959
+ }
2960
+ const fullWidth = calculateTableWidth(
2961
+ columns.map((c) => ({ type: c.type })),
2962
+ "full"
2963
+ );
2964
+ const compactCols = compactColumns ?? [];
2965
+ const compactWidth = calculateTableWidth(
2966
+ compactCols.map((c) => ({ type: c.type })),
2967
+ "compact"
2968
+ );
2969
+ return {
2970
+ full: fullWidth,
2971
+ compact: compactWidth,
2972
+ container: containerWidth
2973
+ };
2974
+ }, [columns, compactColumns, containerWidth])();
2314
2975
  const hasOverflow = React.useCallback((element) => {
2315
2976
  if (!element) return false;
2316
2977
  return element.scrollWidth > element.clientWidth + 2;
2317
2978
  }, []);
2318
2979
  const determineView = React.useCallback(() => {
2319
- var _a, _b, _c, _d;
2320
- const containerWidth = ((_b = (_a = fullTableRef.current) == null ? void 0 : _a.parentElement) == null ? void 0 : _b.clientWidth) || ((_d = (_c = compactTableRef.current) == null ? void 0 : _c.parentElement) == null ? void 0 : _d.clientWidth) || window.innerWidth;
2321
- if (containerWidth < minWidthForCompact) {
2980
+ var _a, _b, _c, _d, _e;
2981
+ if (columns && containerWidth > 0) {
2982
+ const { full, compact } = calculatedWidths;
2983
+ const buffer = TABLE_BREAKPOINTS.widthBuffer;
2984
+ if (containerWidth < minWidthForCompact) {
2985
+ return "cards";
2986
+ }
2987
+ if (full + buffer <= containerWidth) {
2988
+ return "full";
2989
+ }
2990
+ if (compact + buffer <= containerWidth) {
2991
+ return "compact";
2992
+ }
2993
+ return "cards";
2994
+ }
2995
+ const width = ((_a = containerRef.current) == null ? void 0 : _a.clientWidth) || ((_c = (_b = fullTableRef.current) == null ? void 0 : _b.parentElement) == null ? void 0 : _c.clientWidth) || ((_e = (_d = compactTableRef.current) == null ? void 0 : _d.parentElement) == null ? void 0 : _e.clientWidth) || window.innerWidth;
2996
+ if (width < minWidthForCompact) {
2322
2997
  return "cards";
2323
2998
  }
2324
2999
  const fullOverflows = hasOverflow(fullTableRef.current);
2325
3000
  const compactOverflows = hasOverflow(compactTableRef.current);
2326
3001
  setOverflowState({ fullOverflows, compactOverflows });
2327
- if (!fullOverflows) {
2328
- return "full";
2329
- }
2330
- if (!compactOverflows) {
2331
- return "compact";
2332
- }
3002
+ if (!fullOverflows) return "full";
3003
+ if (!compactOverflows) return "compact";
2333
3004
  return "cards";
2334
- }, [hasOverflow, minWidthForCompact]);
3005
+ }, [columns, containerWidth, calculatedWidths, minWidthForCompact, hasOverflow]);
2335
3006
  const checkAndUpdate = React.useCallback(() => {
2336
3007
  if (!isAutoSwitching || isTransitioningRef.current) return;
2337
3008
  if (debounceTimerRef.current) {
@@ -2363,42 +3034,49 @@ function useResponsiveTable(options = {}) {
2363
3034
  React.useEffect(() => {
2364
3035
  var _a, _b;
2365
3036
  if (!isAutoSwitching) return;
2366
- const initialCheckTimer = setTimeout(checkAndUpdate, 50);
3037
+ const updateContainerWidth = () => {
3038
+ var _a2;
3039
+ const ref = containerRef.current || ((_a2 = fullTableRef.current) == null ? void 0 : _a2.parentElement);
3040
+ if (ref) {
3041
+ setContainerWidth(ref.clientWidth);
3042
+ }
3043
+ };
3044
+ const initialTimer = setTimeout(updateContainerWidth, 50);
2367
3045
  const resizeObserver = new ResizeObserver(() => {
3046
+ updateContainerWidth();
2368
3047
  checkAndUpdate();
2369
3048
  });
2370
- if (fullTableRef.current) {
2371
- resizeObserver.observe(fullTableRef.current);
3049
+ const observeTarget = containerRef.current || ((_a = fullTableRef.current) == null ? void 0 : _a.parentElement) || ((_b = compactTableRef.current) == null ? void 0 : _b.parentElement);
3050
+ if (observeTarget) {
3051
+ resizeObserver.observe(observeTarget);
2372
3052
  }
2373
- if (compactTableRef.current) {
2374
- resizeObserver.observe(compactTableRef.current);
2375
- }
2376
- const parent = ((_a = fullTableRef.current) == null ? void 0 : _a.parentElement) || ((_b = compactTableRef.current) == null ? void 0 : _b.parentElement);
2377
- if (parent) {
2378
- resizeObserver.observe(parent);
3053
+ if (!columns) {
3054
+ if (fullTableRef.current) resizeObserver.observe(fullTableRef.current);
3055
+ if (compactTableRef.current) resizeObserver.observe(compactTableRef.current);
2379
3056
  }
2380
3057
  window.addEventListener("resize", checkAndUpdate);
2381
3058
  return () => {
2382
- clearTimeout(initialCheckTimer);
2383
- if (debounceTimerRef.current) {
2384
- clearTimeout(debounceTimerRef.current);
2385
- }
3059
+ clearTimeout(initialTimer);
3060
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
2386
3061
  resizeObserver.disconnect();
2387
3062
  window.removeEventListener("resize", checkAndUpdate);
2388
3063
  };
2389
- }, [isAutoSwitching, checkAndUpdate]);
3064
+ }, [isAutoSwitching, columns, checkAndUpdate]);
2390
3065
  React.useEffect(() => {
2391
3066
  if (!isAutoSwitching) return;
2392
3067
  const timer = setTimeout(checkAndUpdate, 100);
2393
3068
  return () => clearTimeout(timer);
2394
- }, [viewMode, isAutoSwitching, checkAndUpdate]);
3069
+ }, [viewMode, containerWidth, isAutoSwitching, checkAndUpdate]);
2395
3070
  return {
2396
3071
  viewMode,
2397
3072
  setViewMode,
2398
3073
  enableAutoSwitch,
2399
3074
  isAutoSwitching,
3075
+ containerRef,
2400
3076
  fullTableRef,
2401
3077
  compactTableRef,
3078
+ compactColumns,
3079
+ calculatedWidths,
2402
3080
  overflowState
2403
3081
  };
2404
3082
  }
@@ -2417,6 +3095,154 @@ function useOverflowDetection(ref) {
2417
3095
  }, [ref]);
2418
3096
  return hasOverflow;
2419
3097
  }
3098
+ const IMPORTANCE_PRIORITY = {
3099
+ minimal: 0,
3100
+ low: 1,
3101
+ tertiary: 2,
3102
+ medium: 3,
3103
+ secondary: 4,
3104
+ high: 5,
3105
+ critical: 6,
3106
+ primary: 7
3107
+ };
3108
+ function getColumnModeWidth(col, mode) {
3109
+ if (mode === "hidden") return 0;
3110
+ return getColumnWidth(col.type, mode === "compact" ? "compact" : "full");
3111
+ }
3112
+ function calculateAdaptiveWidth(columns, modes) {
3113
+ return columns.reduce((sum, col) => sum + getColumnModeWidth(col, modes[col.field] ?? "full"), 0);
3114
+ }
3115
+ function calculateColumnModes(columns, containerWidth, protectedColumns, minColumnsVisible) {
3116
+ const buffer = TABLE_BREAKPOINTS.widthBuffer;
3117
+ const sortedByImportance = [...columns].sort((a, b) => {
3118
+ const priorityA = IMPORTANCE_PRIORITY[a.importance ?? "tertiary"];
3119
+ const priorityB = IMPORTANCE_PRIORITY[b.importance ?? "tertiary"];
3120
+ return priorityA - priorityB;
3121
+ });
3122
+ const modes = {};
3123
+ columns.forEach((col) => modes[col.field] = "full");
3124
+ let currentWidth = calculateAdaptiveWidth(columns, modes);
3125
+ if (currentWidth + buffer <= containerWidth) {
3126
+ return { modes, degradationLevel: 0 };
3127
+ }
3128
+ let degradationLevel = 1;
3129
+ for (const col of sortedByImportance) {
3130
+ if (protectedColumns.has(col.field)) continue;
3131
+ const priority = IMPORTANCE_PRIORITY[col.importance ?? "tertiary"];
3132
+ if (priority > 2) break;
3133
+ modes[col.field] = "compact";
3134
+ currentWidth = calculateAdaptiveWidth(columns, modes);
3135
+ if (currentWidth + buffer <= containerWidth) return { modes, degradationLevel };
3136
+ }
3137
+ degradationLevel = 2;
3138
+ for (const col of sortedByImportance) {
3139
+ if (protectedColumns.has(col.field)) continue;
3140
+ const priority = IMPORTANCE_PRIORITY[col.importance ?? "tertiary"];
3141
+ if (priority <= 2) continue;
3142
+ if (priority > 4) break;
3143
+ modes[col.field] = "compact";
3144
+ currentWidth = calculateAdaptiveWidth(columns, modes);
3145
+ if (currentWidth + buffer <= containerWidth) return { modes, degradationLevel };
3146
+ }
3147
+ degradationLevel = 3;
3148
+ let visibleCount = columns.length;
3149
+ for (const col of sortedByImportance) {
3150
+ const priority = IMPORTANCE_PRIORITY[col.importance ?? "tertiary"];
3151
+ if (priority > 2) break;
3152
+ if (visibleCount <= minColumnsVisible) break;
3153
+ modes[col.field] = "hidden";
3154
+ visibleCount--;
3155
+ currentWidth = calculateAdaptiveWidth(columns, modes);
3156
+ if (currentWidth + buffer <= containerWidth) return { modes, degradationLevel };
3157
+ }
3158
+ degradationLevel = 4;
3159
+ for (const col of sortedByImportance) {
3160
+ const priority = IMPORTANCE_PRIORITY[col.importance ?? "tertiary"];
3161
+ if (priority <= 2) continue;
3162
+ if (priority > 4) break;
3163
+ if (visibleCount <= minColumnsVisible) break;
3164
+ modes[col.field] = "hidden";
3165
+ visibleCount--;
3166
+ currentWidth = calculateAdaptiveWidth(columns, modes);
3167
+ if (currentWidth + buffer <= containerWidth) return { modes, degradationLevel };
3168
+ }
3169
+ degradationLevel = 5;
3170
+ for (const col of sortedByImportance) {
3171
+ if (modes[col.field] === "hidden") continue;
3172
+ modes[col.field] = "compact";
3173
+ currentWidth = calculateAdaptiveWidth(columns, modes);
3174
+ if (currentWidth + buffer <= containerWidth) return { modes, degradationLevel };
3175
+ }
3176
+ return { modes, degradationLevel: 6 };
3177
+ }
3178
+ function useAdaptiveTable(options) {
3179
+ const {
3180
+ columns,
3181
+ protectedColumns = [],
3182
+ minColumnsVisible = 2,
3183
+ minWidthForTable = TABLE_BREAKPOINTS.minForTable,
3184
+ debounceMs = 100,
3185
+ onModeChange
3186
+ } = options;
3187
+ const containerRef = React.useRef(null);
3188
+ const [containerWidth, setContainerWidth] = React.useState(0);
3189
+ const debounceTimerRef = React.useRef(null);
3190
+ const protectedSet = React.useMemo(() => new Set(protectedColumns), [protectedColumns]);
3191
+ const { modes: columnModes, degradationLevel } = React.useMemo(() => {
3192
+ if (containerWidth === 0) {
3193
+ const modes = {};
3194
+ columns.forEach((col) => modes[col.field] = "full");
3195
+ return { modes, degradationLevel: 0 };
3196
+ }
3197
+ return calculateColumnModes(columns, containerWidth, protectedSet, minColumnsVisible);
3198
+ }, [columns, containerWidth, protectedSet, minColumnsVisible]);
3199
+ const visibleColumns = React.useMemo(() => {
3200
+ return columns.filter((col) => columnModes[col.field] !== "hidden").map(
3201
+ (col) => columnModes[col.field] === "compact" ? { ...col, label: getCompactLabel(col.field, col.label) } : col
3202
+ );
3203
+ }, [columns, columnModes]);
3204
+ const calculatedWidth = React.useMemo(
3205
+ () => calculateAdaptiveWidth(columns, columnModes),
3206
+ [columns, columnModes]
3207
+ );
3208
+ const viewMode = React.useMemo(() => {
3209
+ if (containerWidth > 0 && containerWidth < minWidthForTable) return "cards";
3210
+ if (visibleColumns.length < minColumnsVisible) return "cards";
3211
+ return "table";
3212
+ }, [containerWidth, minWidthForTable, visibleColumns.length, minColumnsVisible]);
3213
+ React.useEffect(() => {
3214
+ const updateWidth = () => {
3215
+ if (containerRef.current) setContainerWidth(containerRef.current.clientWidth);
3216
+ };
3217
+ const initialTimer = setTimeout(updateWidth, 50);
3218
+ const resizeObserver = new ResizeObserver(() => {
3219
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
3220
+ debounceTimerRef.current = setTimeout(updateWidth, debounceMs);
3221
+ });
3222
+ if (containerRef.current) resizeObserver.observe(containerRef.current);
3223
+ return () => {
3224
+ clearTimeout(initialTimer);
3225
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
3226
+ resizeObserver.disconnect();
3227
+ };
3228
+ }, [debounceMs]);
3229
+ const prevModesRef = React.useRef(columnModes);
3230
+ React.useEffect(() => {
3231
+ if (onModeChange && columnModes !== prevModesRef.current) {
3232
+ onModeChange(columnModes);
3233
+ prevModesRef.current = columnModes;
3234
+ }
3235
+ }, [columnModes, onModeChange]);
3236
+ return {
3237
+ containerRef,
3238
+ columnModes,
3239
+ visibleColumns,
3240
+ viewMode,
3241
+ calculatedWidth,
3242
+ containerWidth,
3243
+ degradationLevel
3244
+ };
3245
+ }
2420
3246
  function useEntityData(entity, options = {}) {
2421
3247
  var _a, _b, _c;
2422
3248
  const {
@@ -4299,8 +5125,9 @@ function normalizeColumn(col) {
4299
5125
  return col;
4300
5126
  }
4301
5127
  function DataTable({
4302
- data,
4303
- columns,
5128
+ data: dataProp,
5129
+ columns: columnsProp,
5130
+ query,
4304
5131
  searchPlaceholder = "Search...",
4305
5132
  pageSize = 10,
4306
5133
  showPagination = true,
@@ -4309,12 +5136,12 @@ function DataTable({
4309
5136
  emptyMessage = "No data available",
4310
5137
  className = "",
4311
5138
  hover = false,
4312
- isLoading = false,
5139
+ isLoading: isLoadingProp = false,
4313
5140
  loadingItemCount = 5,
4314
5141
  responsive = true,
4315
5142
  renderMobileCard,
4316
5143
  ui,
4317
- error = null,
5144
+ error: errorProp = null,
4318
5145
  errorTitle,
4319
5146
  errorRetry,
4320
5147
  selectable = false,
@@ -4329,6 +5156,10 @@ function DataTable({
4329
5156
  const [currentPage, setCurrentPage] = React.useState(1);
4330
5157
  const currentBreakpoint = useBreakpoint();
4331
5158
  const isMobile = currentBreakpoint === "xs" || currentBreakpoint === "2xs" || currentBreakpoint === "sm";
5159
+ const data = dataProp ?? (query == null ? void 0 : query.data) ?? [];
5160
+ const columns = columnsProp ?? (query == null ? void 0 : query.columns) ?? [];
5161
+ const isLoading = isLoadingProp || (query == null ? void 0 : query.isLoading) || (query == null ? void 0 : query.isLoadingMetadata) || false;
5162
+ const error = errorProp ?? (query == null ? void 0 : query.error) ?? (query == null ? void 0 : query.metadataError) ?? null;
4332
5163
  const normalizedColumns = React.useMemo(
4333
5164
  () => columns.map((col) => normalizeColumn(col)),
4334
5165
  [columns]
@@ -4715,64 +5546,6 @@ const TableHead = TableHead$1;
4715
5546
  const TableHeader = TableHeader$1;
4716
5547
  const TableRow = TableRow$1;
4717
5548
  const TableFooter = TableFooter$1;
4718
- const IconBadge = ({
4719
- children,
4720
- icon,
4721
- variant = "category",
4722
- category = 1,
4723
- status = "neutral",
4724
- size = "md",
4725
- interactive = false,
4726
- onClick,
4727
- className,
4728
- tooltip
4729
- }) => {
4730
- const currentSize = useResponsiveValue(size);
4731
- const sizeConfig = responsiveScales.components.iconBadge[currentSize];
4732
- const sizeClasses2 = {
4733
- xs: `${(sizeConfig == null ? void 0 : sizeConfig.size) || "w-6 h-6"} ${(sizeConfig == null ? void 0 : sizeConfig.text) || "text-[10px]"} ${(sizeConfig == null ? void 0 : sizeConfig.rounded) || "rounded-md"}`,
4734
- sm: "w-8 h-8 text-xs rounded-lg",
4735
- md: "w-10 h-10 text-sm rounded-xl",
4736
- lg: "w-12 h-12 text-base rounded-xl"
4737
- };
4738
- const baseClasses = cn(
4739
- "inline-flex items-center justify-center font-bold shadow-md",
4740
- sizeClasses2[currentSize],
4741
- interactive && [
4742
- "cursor-pointer",
4743
- getAnimationClasses({
4744
- ...animationPresets.dataBadge,
4745
- size: currentSize === "lg" ? "lg" : currentSize === "sm" || currentSize === "xs" ? "sm" : "md"
4746
- })
4747
- ],
4748
- className
4749
- );
4750
- const gradientClasses = variant === "category" ? `bg-gradient-to-br from-category-${category} to-category-${Math.min(category + 1, 8)}` : variant === "status" ? `bg-gradient-to-br from-status-${status} to-status-${status}` : "bg-gradient-to-br from-category-1 to-category-2";
4751
- const badge = /* @__PURE__ */ jsxRuntime.jsxs(
4752
- "div",
4753
- {
4754
- className: cn(baseClasses, gradientClasses, "animate-fade-in"),
4755
- onClick,
4756
- role: interactive ? "button" : void 0,
4757
- tabIndex: interactive ? 0 : void 0,
4758
- onKeyDown: interactive ? (e) => {
4759
- if (e.key === "Enter" || e.key === " ") {
4760
- e.preventDefault();
4761
- onClick == null ? void 0 : onClick();
4762
- }
4763
- } : void 0,
4764
- "data-component-name": "IconBadge",
4765
- children: [
4766
- icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary-foreground drop-shadow", children: icon }),
4767
- children && !icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary-foreground font-bold", children })
4768
- ]
4769
- }
4770
- );
4771
- if (tooltip) {
4772
- return /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: tooltip, position: "top", size: "sm", children: badge });
4773
- }
4774
- return badge;
4775
- };
4776
5549
  const Chart = ({
4777
5550
  title,
4778
5551
  subtitle,
@@ -5535,85 +6308,6 @@ const ProgressBar = ({
5535
6308
  )
5536
6309
  ] });
5537
6310
  };
5538
- const valueColorMap = {
5539
- success: "text-green-600",
5540
- error: "text-red-600",
5541
- warning: "text-yellow-600",
5542
- info: "text-blue-600",
5543
- neutral: "text-muted-foreground",
5544
- default: "text-foreground"
5545
- };
5546
- function ListCard({
5547
- icon,
5548
- title,
5549
- subtitle,
5550
- metadata,
5551
- value,
5552
- badge,
5553
- onClick,
5554
- className,
5555
- children
5556
- }) {
5557
- return /* @__PURE__ */ jsxRuntime.jsxs(
5558
- "div",
5559
- {
5560
- className: cn(
5561
- "flex items-start gap-3 p-3 rounded-lg border border-border transition-colors",
5562
- onClick && "hover:bg-muted/50 cursor-pointer",
5563
- className
5564
- ),
5565
- onClick,
5566
- children: [
5567
- icon && /* @__PURE__ */ jsxRuntime.jsx(
5568
- IconBadge,
5569
- {
5570
- variant: icon.variant || "category",
5571
- category: icon.variant === "category" ? icon.category || 1 : void 0,
5572
- status: icon.variant === "status" ? icon.status || "neutral" : void 0,
5573
- size: icon.size || "sm",
5574
- icon: icon.icon,
5575
- tooltip: icon.tooltip
5576
- }
5577
- ),
5578
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
5579
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
5580
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
5581
- /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-medium text-foreground truncate", children: title }),
5582
- subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: subtitle }),
5583
- metadata && metadata.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3 mt-1", children: metadata.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: item }, index)) })
5584
- ] }),
5585
- (value || badge) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
5586
- value && /* @__PURE__ */ jsxRuntime.jsx(
5587
- "p",
5588
- {
5589
- className: cn(
5590
- "text-sm font-semibold",
5591
- value.variant && valueColorMap[value.variant],
5592
- value.className
5593
- ),
5594
- children: value.text
5595
- }
5596
- ),
5597
- badge && /* @__PURE__ */ jsxRuntime.jsx(
5598
- DataBadge,
5599
- {
5600
- variant: badge.variant,
5601
- status: badge.variant === "status" ? badge.status : void 0,
5602
- category: badge.variant === "category" ? badge.category : void 0,
5603
- size: badge.size || "sm",
5604
- icon: badge.icon,
5605
- className: "mt-1",
5606
- children: badge.text
5607
- }
5608
- )
5609
- ] })
5610
- ] }),
5611
- children
5612
- ] })
5613
- ]
5614
- }
5615
- );
5616
- }
5617
6311
  const TruncatedText = ({
5618
6312
  text,
5619
6313
  className,
@@ -8934,6 +9628,13 @@ const defaultShowcaseNavigation = [
8934
9628
  path: "/admin/dashboard",
8935
9629
  category: 2
8936
9630
  },
9631
+ {
9632
+ value: "admin-dashboard-v2",
9633
+ label: "Admin Dashboard V2",
9634
+ icon: "Shield",
9635
+ path: "/admin/dashboard-v2",
9636
+ category: 2
9637
+ },
8937
9638
  {
8938
9639
  value: "admin-users",
8939
9640
  label: "User Management",
@@ -9204,7 +9905,6 @@ const Sidebar = ({ className }) => {
9204
9905
  );
9205
9906
  };
9206
9907
  const AppHeader = ({ className }) => {
9207
- const isTrialMode = false;
9208
9908
  return /* @__PURE__ */ jsxRuntime.jsx(
9209
9909
  "header",
9210
9910
  {
@@ -9217,7 +9917,7 @@ const AppHeader = ({ className }) => {
9217
9917
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-16 items-center justify-between", children: [
9218
9918
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
9219
9919
  /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-bold text-foreground", children: "Frontend Template" }),
9220
- isTrialMode
9920
+ /* @__PURE__ */ jsxRuntime.jsx(DataBadge, { variant: "status", status: "info", display: "icon-text", children: "Trial Mode" })
9221
9921
  ] }),
9222
9922
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 max-w-2xl mx-8", children: /* @__PURE__ */ jsxRuntime.jsx(
9223
9923
  GlobalSearch,
@@ -14001,18 +14701,41 @@ function createReactApp(config) {
14001
14701
  enableRouting = true,
14002
14702
  auth,
14003
14703
  navigation,
14004
- customProviders = []
14704
+ customProviders = [],
14705
+ persistQueryCache = false,
14706
+ queryCacheKey = "PATTERN_STACK_QUERY_CACHE"
14005
14707
  } = appConfig;
14006
14708
  const queryClient = new reactQuery.QueryClient({
14007
14709
  defaultOptions: {
14008
14710
  queries: {
14009
14711
  retry: 1,
14010
14712
  refetchOnWindowFocus: false,
14011
- staleTime: 5 * 60 * 1e3
14713
+ staleTime: 5 * 60 * 1e3,
14012
14714
  // 5 minutes
14715
+ // Keep data longer when persistence is enabled
14716
+ gcTime: persistQueryCache ? 1e3 * 60 * 60 * 24 : 1e3 * 60 * 5
14013
14717
  }
14014
14718
  }
14015
14719
  });
14720
+ if (persistQueryCache && typeof window !== "undefined") {
14721
+ import("@tanstack/react-query-persist-client").then(({ persistQueryClient }) => {
14722
+ import("@tanstack/query-sync-storage-persister").then(({ createSyncStoragePersister }) => {
14723
+ const persister = createSyncStoragePersister({
14724
+ storage: window.localStorage,
14725
+ key: queryCacheKey
14726
+ });
14727
+ persistQueryClient({
14728
+ queryClient,
14729
+ persister,
14730
+ maxAge: 1e3 * 60 * 60 * 24
14731
+ // 24 hours
14732
+ });
14733
+ console.info("[app] Query cache persistence enabled");
14734
+ });
14735
+ }).catch((err) => {
14736
+ console.warn("[app] Query persistence not available:", err.message);
14737
+ });
14738
+ }
14016
14739
  const routes = [];
14017
14740
  const DefaultHome = () => /* @__PURE__ */ jsxRuntime.jsx(DashboardTemplate, { title, description, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center min-h-[400px] text-center", children: [
14018
14741
  /* @__PURE__ */ jsxRuntime.jsxs("h1", { className: "text-4xl font-bold text-foreground mb-4", children: [
@@ -14031,7 +14754,7 @@ function createReactApp(config) {
14031
14754
  ] }) });
14032
14755
  const createProviderTree = (children) => {
14033
14756
  let tree = children;
14034
- customProviders.reverse().forEach((Provider) => {
14757
+ [...customProviders].reverse().forEach((Provider) => {
14035
14758
  tree = /* @__PURE__ */ jsxRuntime.jsx(Provider, { children: tree }, Provider.name);
14036
14759
  });
14037
14760
  if (enableRouting) {
@@ -14074,10 +14797,10 @@ function createReactApp(config) {
14074
14797
  }
14075
14798
  return component;
14076
14799
  };
14077
- const AppContent = () => /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Routes, { children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Route, { path: "/", element: /* @__PURE__ */ jsxRuntime.jsx(AppLayout, {}), children: [
14800
+ const AppContent = () => /* @__PURE__ */ jsxRuntime.jsx(React.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-screen", children: "Loading..." }), children: /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Routes, { children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Route, { path: "/", element: /* @__PURE__ */ jsxRuntime.jsx(AppLayout, {}), children: [
14078
14801
  /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: wrapWithProtection("/", /* @__PURE__ */ jsxRuntime.jsx(DefaultHome, {})) }),
14079
14802
  routes.map(({ path, component }, index) => /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path, element: wrapWithProtection(path, component) }, index))
14080
- ] }) });
14803
+ ] }) }) });
14081
14804
  return createProviderTree(/* @__PURE__ */ jsxRuntime.jsx(AppContent, {}));
14082
14805
  },
14083
14806
  mount(elementId = "root") {
@@ -17272,6 +17995,10 @@ exports.Badge = Badge;
17272
17995
  exports.Breadcrumb = Breadcrumb;
17273
17996
  exports.BulkSelectionBar = BulkSelectionBar;
17274
17997
  exports.Button = Button;
17998
+ exports.COLUMN_WIDTHS = COLUMN_WIDTHS;
17999
+ exports.COMPACT_COLUMN_WIDTHS = COMPACT_COLUMN_WIDTHS;
18000
+ exports.COMPACT_FORMATTERS = COMPACT_FORMATTERS;
18001
+ exports.COMPACT_LABELS = COMPACT_LABELS;
17275
18002
  exports.Card = Card;
17276
18003
  exports.CardContent = CardContent;
17277
18004
  exports.CardDescription = CardDescription;
@@ -17330,6 +18057,7 @@ exports.FileUpload = FileUpload;
17330
18057
  exports.FormField = FormField;
17331
18058
  exports.FormGroup = FormGroup;
17332
18059
  exports.GlobalSearch = GlobalSearch;
18060
+ exports.IMPORTANCE_VISIBILITY = IMPORTANCE_VISIBILITY;
17333
18061
  exports.Icon = Icon;
17334
18062
  exports.IconBadge = IconBadge;
17335
18063
  exports.Input = Input;
@@ -17385,6 +18113,7 @@ exports.Spinner = Spinner;
17385
18113
  exports.StatCard = StatCard;
17386
18114
  exports.StyleGuide = StyleGuide;
17387
18115
  exports.Switch = Switch;
18116
+ exports.TABLE_BREAKPOINTS = TABLE_BREAKPOINTS;
17388
18117
  exports.Table = Table;
17389
18118
  exports.TableBody = TableBody;
17390
18119
  exports.TableCaption = TableCaption;
@@ -17407,19 +18136,27 @@ exports.UserMenu = UserMenu;
17407
18136
  exports.animationPresets = animationPresets;
17408
18137
  exports.apiClient = apiClient;
17409
18138
  exports.breakpoints = breakpoints;
18139
+ exports.calculateTableWidth = calculateTableWidth;
17410
18140
  exports.cn = cn;
17411
18141
  exports.createAPIDataTemplate = createAPIDataTemplate;
17412
18142
  exports.createApiClient = createApiClient;
18143
+ exports.createMobileCardRenderer = createMobileCardRenderer;
17413
18144
  exports.createReactApp = createReactApp;
17414
18145
  exports.createSimpleApp = createSimpleApp;
17415
18146
  exports.defaultFieldRenderers = defaultFieldRenderers;
17416
18147
  exports.detectBulkOperationType = detectBulkOperationType;
17417
18148
  exports.detectUIConfig = detectUIConfig;
18149
+ exports.determineTableMode = determineTableMode;
18150
+ exports.entityToListCardProps = entityToListCardProps;
17418
18151
  exports.env = env;
17419
18152
  exports.formatNumberWithTooltip = formatNumberWithTooltip;
17420
18153
  exports.generateBulkOperationName = generateBulkOperationName;
18154
+ exports.generateCompactColumns = generateCompactColumns;
17421
18155
  exports.getAnimationClasses = getAnimationClasses;
17422
18156
  exports.getChartHeight = getChartHeight;
18157
+ exports.getColumnWidth = getColumnWidth;
18158
+ exports.getCompactFormatter = getCompactFormatter;
18159
+ exports.getCompactLabel = getCompactLabel;
17423
18160
  exports.getContainerHeightClass = getContainerHeightClass;
17424
18161
  exports.getCurrentBreakpoint = getCurrentBreakpoint;
17425
18162
  exports.getFieldType = getFieldType;
@@ -17438,6 +18175,7 @@ exports.renderField = renderField;
17438
18175
  exports.responsiveScales = responsiveScales;
17439
18176
  exports.setGlobalAuthService = setGlobalAuthService;
17440
18177
  exports.tooltipContent = tooltipContent;
18178
+ exports.useAdaptiveTable = useAdaptiveTable;
17441
18179
  exports.useApiMutation = useApiMutation;
17442
18180
  exports.useApiQuery = useApiQuery;
17443
18181
  exports.useAuth = useAuth;