@pattern-stack/frontend-patterns 0.2.0-alpha.7 → 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.
package/dist/index.es.js CHANGED
@@ -7,7 +7,7 @@ import { twMerge } from "tailwind-merge";
7
7
  import axios from "axios";
8
8
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
9
9
  import * as React from "react";
10
- import React__default, { useRef, useState, useEffect, useMemo, forwardRef, useCallback, useContext, createContext, useId, Component } from "react";
10
+ import React__default, { useRef, useState, useEffect, useMemo, forwardRef, useCallback, useContext, createContext, useId, Component, Suspense } from "react";
11
11
  import * as SheetPrimitive from "@radix-ui/react-dialog";
12
12
  import { Map as Map$1, Calculator, Brain, User, Handshake, Truck, Building, HelpCircle, Info, AlertCircle, Check, ArrowDown, ArrowUp, ArrowLeft, ArrowRight, ChevronUp, ChevronLeft, ChevronDown, ChevronRight, Music, Video, Image, Folder, File, Flag, Tag, Bookmark, Heart, Star, MapPin, Clock, Calendar, Phone, Mail, Unlock, Lock, Share, Upload, Download, Eye, Trash2, Edit, Plus, Search, Bell, Settings, Home, Layout, TrendingUp, Database, BarChart3, Users, Shield, X, Menu, Palette, Circle, Building2, Package, FileText, Loader2, EyeOff, InfoIcon, ChevronsUpDown, AreaChart, LineChart, TrendingDown, Minus, AlertTriangle, Activity, WifiOff, CloudOff, Cloud, CheckCircle, FileX, Sun, Moon, LogOut, Square, Waves, Zap, TreePine, Sparkles, Sunset, DollarSign, ShoppingCart, Target, ExternalLink, MoreHorizontal, Filter, Edit2, Undo2, RefreshCw, History, Save, Copy, Grid3X3, Layers, XCircle, Briefcase } from "lucide-react";
13
13
  import * as LabelPrimitive from "@radix-ui/react-label";
@@ -136,6 +136,141 @@ const RESPONSIVE_CHART_HEIGHTS = {
136
136
  secondary: DASHBOARD_CHART_HEIGHTS.medium
137
137
  }
138
138
  };
139
+ const COLUMN_WIDTHS = {
140
+ // Text
141
+ text: 180,
142
+ password: 120,
143
+ // Numbers
144
+ number: 80,
145
+ money: 100,
146
+ percent: 70,
147
+ // Dates
148
+ date: 100,
149
+ datetime: 140,
150
+ // Contact/Links
151
+ email: 180,
152
+ phone: 120,
153
+ url: 160,
154
+ // Visual indicators
155
+ status: 90,
156
+ badge: 90,
157
+ boolean: 60,
158
+ rating: 80,
159
+ color: 60,
160
+ // Entity references
161
+ entity: 140,
162
+ user: 140,
163
+ // Special
164
+ image: 60,
165
+ file: 100,
166
+ json: 200
167
+ };
168
+ const COMPACT_COLUMN_WIDTHS = {
169
+ text: 120,
170
+ // With truncation
171
+ money: 60,
172
+ // $2.5k format
173
+ date: 55,
174
+ // "Dec 5" or "Tu"
175
+ datetime: 65,
176
+ // "5m ago"
177
+ email: 100,
178
+ // Truncated
179
+ phone: 80,
180
+ url: 80,
181
+ entity: 100,
182
+ user: 80,
183
+ json: 80
184
+ };
185
+ const COMPACT_FORMATTERS = {
186
+ money: "compactCurrency",
187
+ // $2,456.99 → $2.5k
188
+ date: "shortDate",
189
+ // 2024-01-15 → Dec 5
190
+ datetime: "relative",
191
+ // 2024-01-15T10:30 → 5m ago
192
+ text: "truncate",
193
+ // Long customer name → Long cust...
194
+ email: "truncate",
195
+ url: "domain",
196
+ // https://example.com/path → example.com
197
+ number: "compact"
198
+ // 1234567 → 1.2M
199
+ };
200
+ const COMPACT_LABELS = {
201
+ // Common field names → short versions
202
+ customer: "Cust",
203
+ description: "Desc",
204
+ quantity: "Qty",
205
+ amount: "Amt",
206
+ total: "Tot",
207
+ priority: "Pri",
208
+ status: "Stat",
209
+ created_at: "Created",
210
+ updated_at: "Updated",
211
+ email: "Email",
212
+ phone: "Ph",
213
+ category: "Cat",
214
+ // Time fields
215
+ time: "Time",
216
+ timeAgo: "Time",
217
+ timestamp: "Time",
218
+ // ID fields
219
+ order_id: "#",
220
+ id: "#",
221
+ transaction_id: "Txn"
222
+ };
223
+ const TABLE_BREAKPOINTS = {
224
+ /** Below this width (px), always use cards */
225
+ minForTable: 400,
226
+ /** Buffer (px) to add when calculating if columns fit */
227
+ widthBuffer: 50,
228
+ /** Minimum columns to show in compact mode */
229
+ minCompactColumns: 3,
230
+ /** Maximum columns in compact mode */
231
+ maxCompactColumns: 6
232
+ };
233
+ const IMPORTANCE_VISIBILITY = {
234
+ full: ["primary", "secondary", "tertiary", "critical", "high", "medium", "low", "minimal"],
235
+ compact: ["primary", "secondary", "critical", "high", "medium"],
236
+ cards: ["primary", "critical", "high"]
237
+ };
238
+ function getColumnWidth(type, mode = "full") {
239
+ if (mode === "compact" && type in COMPACT_COLUMN_WIDTHS) {
240
+ return COMPACT_COLUMN_WIDTHS[type];
241
+ }
242
+ return COLUMN_WIDTHS[type] ?? COLUMN_WIDTHS.text;
243
+ }
244
+ function calculateTableWidth(columns, mode = "full") {
245
+ return columns.reduce((sum, col) => sum + getColumnWidth(col.type, mode), 0);
246
+ }
247
+ function getCompactLabel(fieldName, originalLabel) {
248
+ const normalized = fieldName.toLowerCase();
249
+ return COMPACT_LABELS[normalized] ?? originalLabel;
250
+ }
251
+ function getCompactFormatter(type) {
252
+ return COMPACT_FORMATTERS[type];
253
+ }
254
+ function determineTableMode(containerWidth, columns) {
255
+ const { minForTable, widthBuffer } = TABLE_BREAKPOINTS;
256
+ if (containerWidth < minForTable) {
257
+ return "cards";
258
+ }
259
+ const fullWidth = calculateTableWidth(columns, "full");
260
+ const compactColumns = columns.filter(
261
+ (col) => IMPORTANCE_VISIBILITY.compact.includes(
262
+ col.importance ?? "medium"
263
+ )
264
+ );
265
+ const compactWidth = calculateTableWidth(compactColumns, "compact");
266
+ if (fullWidth + widthBuffer <= containerWidth) {
267
+ return "full";
268
+ }
269
+ if (compactWidth + widthBuffer <= containerWidth) {
270
+ return "compact";
271
+ }
272
+ return "cards";
273
+ }
139
274
  const breakpoints = {
140
275
  "2xs": 400,
141
276
  xs: 500,
@@ -2009,6 +2144,143 @@ function renderField(value, fieldName, fieldType, breakpoint, item, customRender
2009
2144
  function cn(...inputs) {
2010
2145
  return twMerge(clsx(inputs));
2011
2146
  }
2147
+ const IconBadge = ({
2148
+ children,
2149
+ icon,
2150
+ variant = "category",
2151
+ category = 1,
2152
+ status = "neutral",
2153
+ size = "md",
2154
+ interactive = false,
2155
+ onClick,
2156
+ className,
2157
+ tooltip
2158
+ }) => {
2159
+ const currentSize = useResponsiveValue(size);
2160
+ const sizeConfig = responsiveScales.components.iconBadge[currentSize];
2161
+ const sizeClasses2 = {
2162
+ 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"}`,
2163
+ sm: "w-8 h-8 text-xs rounded-lg",
2164
+ md: "w-10 h-10 text-sm rounded-xl",
2165
+ lg: "w-12 h-12 text-base rounded-xl"
2166
+ };
2167
+ const baseClasses = cn(
2168
+ "inline-flex items-center justify-center font-bold shadow-md",
2169
+ sizeClasses2[currentSize],
2170
+ interactive && [
2171
+ "cursor-pointer",
2172
+ getAnimationClasses({
2173
+ ...animationPresets.dataBadge,
2174
+ size: currentSize === "lg" ? "lg" : currentSize === "sm" || currentSize === "xs" ? "sm" : "md"
2175
+ })
2176
+ ],
2177
+ className
2178
+ );
2179
+ 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";
2180
+ const badge = /* @__PURE__ */ jsxs(
2181
+ "div",
2182
+ {
2183
+ className: cn(baseClasses, gradientClasses, "animate-fade-in"),
2184
+ onClick,
2185
+ role: interactive ? "button" : void 0,
2186
+ tabIndex: interactive ? 0 : void 0,
2187
+ onKeyDown: interactive ? (e) => {
2188
+ if (e.key === "Enter" || e.key === " ") {
2189
+ e.preventDefault();
2190
+ onClick == null ? void 0 : onClick();
2191
+ }
2192
+ } : void 0,
2193
+ "data-component-name": "IconBadge",
2194
+ children: [
2195
+ icon && /* @__PURE__ */ jsx("span", { className: "text-primary-foreground drop-shadow", children: icon }),
2196
+ children && !icon && /* @__PURE__ */ jsx("span", { className: "text-primary-foreground font-bold", children })
2197
+ ]
2198
+ }
2199
+ );
2200
+ if (tooltip) {
2201
+ return /* @__PURE__ */ jsx(Tooltip, { content: tooltip, position: "top", size: "sm", children: badge });
2202
+ }
2203
+ return badge;
2204
+ };
2205
+ const valueColorMap = {
2206
+ success: "text-green-600",
2207
+ error: "text-red-600",
2208
+ warning: "text-yellow-600",
2209
+ info: "text-blue-600",
2210
+ neutral: "text-muted-foreground",
2211
+ default: "text-foreground"
2212
+ };
2213
+ function ListCard({
2214
+ icon,
2215
+ title,
2216
+ subtitle,
2217
+ metadata,
2218
+ value,
2219
+ badge,
2220
+ onClick,
2221
+ className,
2222
+ children
2223
+ }) {
2224
+ return /* @__PURE__ */ jsxs(
2225
+ "div",
2226
+ {
2227
+ className: cn(
2228
+ "flex items-start gap-3 p-3 rounded-lg border border-border transition-colors",
2229
+ onClick && "hover:bg-muted/50 cursor-pointer",
2230
+ className
2231
+ ),
2232
+ onClick,
2233
+ children: [
2234
+ icon && /* @__PURE__ */ jsx(
2235
+ IconBadge,
2236
+ {
2237
+ variant: icon.variant || "category",
2238
+ category: icon.variant === "category" ? icon.category || 1 : void 0,
2239
+ status: icon.variant === "status" ? icon.status || "neutral" : void 0,
2240
+ size: icon.size || "sm",
2241
+ icon: icon.icon,
2242
+ tooltip: icon.tooltip
2243
+ }
2244
+ ),
2245
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
2246
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
2247
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
2248
+ /* @__PURE__ */ jsx("h4", { className: "text-sm font-medium text-foreground truncate", children: title }),
2249
+ subtitle && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: subtitle }),
2250
+ metadata && metadata.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 mt-1", children: metadata.map((item, index) => /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: item }, index)) })
2251
+ ] }),
2252
+ (value || badge) && /* @__PURE__ */ jsxs("div", { className: "text-right", children: [
2253
+ value && /* @__PURE__ */ jsx(
2254
+ "p",
2255
+ {
2256
+ className: cn(
2257
+ "text-sm font-semibold",
2258
+ value.variant && valueColorMap[value.variant],
2259
+ value.className
2260
+ ),
2261
+ children: value.text
2262
+ }
2263
+ ),
2264
+ badge && /* @__PURE__ */ jsx(
2265
+ DataBadge,
2266
+ {
2267
+ variant: badge.variant,
2268
+ status: badge.variant === "status" ? badge.status : void 0,
2269
+ category: badge.variant === "category" ? badge.category : void 0,
2270
+ size: badge.size || "sm",
2271
+ icon: badge.icon,
2272
+ className: "mt-1",
2273
+ children: badge.text
2274
+ }
2275
+ )
2276
+ ] })
2277
+ ] }),
2278
+ children
2279
+ ] })
2280
+ ]
2281
+ }
2282
+ );
2283
+ }
2012
2284
  const STATUS_COLOR_MAP = {
2013
2285
  // Success states
2014
2286
  active: "success",
@@ -2347,7 +2619,6 @@ function getCompactWidth(type) {
2347
2619
  }
2348
2620
  }
2349
2621
  function createMobileCardRenderer(columns, mapping, entityType) {
2350
- const { ListCard: ListCard2 } = require("../components/data/ListCard");
2351
2622
  return ({ data, onItemClick }) => /* @__PURE__ */ jsx("div", { className: "space-y-2", children: data.map((item, index) => {
2352
2623
  const cardProps = entityToListCardProps(
2353
2624
  item,
@@ -2356,7 +2627,7 @@ function createMobileCardRenderer(columns, mapping, entityType) {
2356
2627
  entityType
2357
2628
  );
2358
2629
  return /* @__PURE__ */ jsx(
2359
- ListCard2,
2630
+ ListCard,
2360
2631
  {
2361
2632
  ...cardProps,
2362
2633
  onClick: onItemClick ? () => onItemClick(item) : void 0
@@ -2530,10 +2801,10 @@ function useApiMutation(mutationFn, options) {
2530
2801
  const queryClient = useQueryClient();
2531
2802
  return useMutation({
2532
2803
  mutationFn,
2533
- onSuccess: (data, variables, context) => {
2804
+ onSuccess: (...args) => {
2534
2805
  var _a;
2535
- queryClient.invalidateQueries();
2536
- (_a = options == null ? void 0 : options.onSuccess) == null ? void 0 : _a.call(options, data, variables, context);
2806
+ void queryClient.invalidateQueries({});
2807
+ (_a = options == null ? void 0 : options.onSuccess) == null ? void 0 : _a.call(options, ...args);
2537
2808
  },
2538
2809
  ...options
2539
2810
  });
@@ -2608,43 +2879,87 @@ function useFieldMetadata({
2608
2879
  }
2609
2880
  function useResponsiveTable(options = {}) {
2610
2881
  const {
2882
+ columns,
2611
2883
  initialView = "full",
2612
2884
  autoSwitch = true,
2613
- minWidthForCompact = 400,
2885
+ minWidthForCompact = TABLE_BREAKPOINTS.minForTable,
2614
2886
  debounceMs = 100,
2615
2887
  onViewChange
2616
2888
  } = options;
2617
2889
  const [viewMode, setViewModeState] = useState(initialView);
2618
2890
  const [isAutoSwitching, setIsAutoSwitching] = useState(autoSwitch);
2891
+ const [containerWidth, setContainerWidth] = useState(0);
2619
2892
  const [overflowState, setOverflowState] = useState({
2620
2893
  fullOverflows: false,
2621
2894
  compactOverflows: false
2622
2895
  });
2896
+ const containerRef = useRef(null);
2623
2897
  const fullTableRef = useRef(null);
2624
2898
  const compactTableRef = useRef(null);
2625
2899
  const debounceTimerRef = useRef(null);
2626
2900
  const isTransitioningRef = useRef(false);
2901
+ const compactColumns = useCallback(() => {
2902
+ if (!columns) return null;
2903
+ const validImportance = IMPORTANCE_VISIBILITY.compact;
2904
+ const filtered = columns.filter(
2905
+ (col) => validImportance.includes(col.importance ?? "medium")
2906
+ );
2907
+ const limited = filtered.slice(0, TABLE_BREAKPOINTS.maxCompactColumns);
2908
+ return limited.map((col) => ({
2909
+ ...col,
2910
+ label: getCompactLabel(col.field, col.label)
2911
+ }));
2912
+ }, [columns])();
2913
+ const calculatedWidths = useCallback(() => {
2914
+ if (!columns) {
2915
+ return { full: 0, compact: 0, container: containerWidth };
2916
+ }
2917
+ const fullWidth = calculateTableWidth(
2918
+ columns.map((c) => ({ type: c.type })),
2919
+ "full"
2920
+ );
2921
+ const compactCols = compactColumns ?? [];
2922
+ const compactWidth = calculateTableWidth(
2923
+ compactCols.map((c) => ({ type: c.type })),
2924
+ "compact"
2925
+ );
2926
+ return {
2927
+ full: fullWidth,
2928
+ compact: compactWidth,
2929
+ container: containerWidth
2930
+ };
2931
+ }, [columns, compactColumns, containerWidth])();
2627
2932
  const hasOverflow = useCallback((element) => {
2628
2933
  if (!element) return false;
2629
2934
  return element.scrollWidth > element.clientWidth + 2;
2630
2935
  }, []);
2631
2936
  const determineView = useCallback(() => {
2632
- var _a, _b, _c, _d;
2633
- 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;
2634
- if (containerWidth < minWidthForCompact) {
2937
+ var _a, _b, _c, _d, _e;
2938
+ if (columns && containerWidth > 0) {
2939
+ const { full, compact } = calculatedWidths;
2940
+ const buffer = TABLE_BREAKPOINTS.widthBuffer;
2941
+ if (containerWidth < minWidthForCompact) {
2942
+ return "cards";
2943
+ }
2944
+ if (full + buffer <= containerWidth) {
2945
+ return "full";
2946
+ }
2947
+ if (compact + buffer <= containerWidth) {
2948
+ return "compact";
2949
+ }
2950
+ return "cards";
2951
+ }
2952
+ 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;
2953
+ if (width < minWidthForCompact) {
2635
2954
  return "cards";
2636
2955
  }
2637
2956
  const fullOverflows = hasOverflow(fullTableRef.current);
2638
2957
  const compactOverflows = hasOverflow(compactTableRef.current);
2639
2958
  setOverflowState({ fullOverflows, compactOverflows });
2640
- if (!fullOverflows) {
2641
- return "full";
2642
- }
2643
- if (!compactOverflows) {
2644
- return "compact";
2645
- }
2959
+ if (!fullOverflows) return "full";
2960
+ if (!compactOverflows) return "compact";
2646
2961
  return "cards";
2647
- }, [hasOverflow, minWidthForCompact]);
2962
+ }, [columns, containerWidth, calculatedWidths, minWidthForCompact, hasOverflow]);
2648
2963
  const checkAndUpdate = useCallback(() => {
2649
2964
  if (!isAutoSwitching || isTransitioningRef.current) return;
2650
2965
  if (debounceTimerRef.current) {
@@ -2676,42 +2991,49 @@ function useResponsiveTable(options = {}) {
2676
2991
  useEffect(() => {
2677
2992
  var _a, _b;
2678
2993
  if (!isAutoSwitching) return;
2679
- const initialCheckTimer = setTimeout(checkAndUpdate, 50);
2994
+ const updateContainerWidth = () => {
2995
+ var _a2;
2996
+ const ref = containerRef.current || ((_a2 = fullTableRef.current) == null ? void 0 : _a2.parentElement);
2997
+ if (ref) {
2998
+ setContainerWidth(ref.clientWidth);
2999
+ }
3000
+ };
3001
+ const initialTimer = setTimeout(updateContainerWidth, 50);
2680
3002
  const resizeObserver = new ResizeObserver(() => {
3003
+ updateContainerWidth();
2681
3004
  checkAndUpdate();
2682
3005
  });
2683
- if (fullTableRef.current) {
2684
- resizeObserver.observe(fullTableRef.current);
2685
- }
2686
- if (compactTableRef.current) {
2687
- resizeObserver.observe(compactTableRef.current);
3006
+ const observeTarget = containerRef.current || ((_a = fullTableRef.current) == null ? void 0 : _a.parentElement) || ((_b = compactTableRef.current) == null ? void 0 : _b.parentElement);
3007
+ if (observeTarget) {
3008
+ resizeObserver.observe(observeTarget);
2688
3009
  }
2689
- const parent = ((_a = fullTableRef.current) == null ? void 0 : _a.parentElement) || ((_b = compactTableRef.current) == null ? void 0 : _b.parentElement);
2690
- if (parent) {
2691
- resizeObserver.observe(parent);
3010
+ if (!columns) {
3011
+ if (fullTableRef.current) resizeObserver.observe(fullTableRef.current);
3012
+ if (compactTableRef.current) resizeObserver.observe(compactTableRef.current);
2692
3013
  }
2693
3014
  window.addEventListener("resize", checkAndUpdate);
2694
3015
  return () => {
2695
- clearTimeout(initialCheckTimer);
2696
- if (debounceTimerRef.current) {
2697
- clearTimeout(debounceTimerRef.current);
2698
- }
3016
+ clearTimeout(initialTimer);
3017
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
2699
3018
  resizeObserver.disconnect();
2700
3019
  window.removeEventListener("resize", checkAndUpdate);
2701
3020
  };
2702
- }, [isAutoSwitching, checkAndUpdate]);
3021
+ }, [isAutoSwitching, columns, checkAndUpdate]);
2703
3022
  useEffect(() => {
2704
3023
  if (!isAutoSwitching) return;
2705
3024
  const timer = setTimeout(checkAndUpdate, 100);
2706
3025
  return () => clearTimeout(timer);
2707
- }, [viewMode, isAutoSwitching, checkAndUpdate]);
3026
+ }, [viewMode, containerWidth, isAutoSwitching, checkAndUpdate]);
2708
3027
  return {
2709
3028
  viewMode,
2710
3029
  setViewMode,
2711
3030
  enableAutoSwitch,
2712
3031
  isAutoSwitching,
3032
+ containerRef,
2713
3033
  fullTableRef,
2714
3034
  compactTableRef,
3035
+ compactColumns,
3036
+ calculatedWidths,
2715
3037
  overflowState
2716
3038
  };
2717
3039
  }
@@ -2730,6 +3052,154 @@ function useOverflowDetection(ref) {
2730
3052
  }, [ref]);
2731
3053
  return hasOverflow;
2732
3054
  }
3055
+ const IMPORTANCE_PRIORITY = {
3056
+ minimal: 0,
3057
+ low: 1,
3058
+ tertiary: 2,
3059
+ medium: 3,
3060
+ secondary: 4,
3061
+ high: 5,
3062
+ critical: 6,
3063
+ primary: 7
3064
+ };
3065
+ function getColumnModeWidth(col, mode) {
3066
+ if (mode === "hidden") return 0;
3067
+ return getColumnWidth(col.type, mode === "compact" ? "compact" : "full");
3068
+ }
3069
+ function calculateAdaptiveWidth(columns, modes) {
3070
+ return columns.reduce((sum, col) => sum + getColumnModeWidth(col, modes[col.field] ?? "full"), 0);
3071
+ }
3072
+ function calculateColumnModes(columns, containerWidth, protectedColumns, minColumnsVisible) {
3073
+ const buffer = TABLE_BREAKPOINTS.widthBuffer;
3074
+ const sortedByImportance = [...columns].sort((a, b) => {
3075
+ const priorityA = IMPORTANCE_PRIORITY[a.importance ?? "tertiary"];
3076
+ const priorityB = IMPORTANCE_PRIORITY[b.importance ?? "tertiary"];
3077
+ return priorityA - priorityB;
3078
+ });
3079
+ const modes = {};
3080
+ columns.forEach((col) => modes[col.field] = "full");
3081
+ let currentWidth = calculateAdaptiveWidth(columns, modes);
3082
+ if (currentWidth + buffer <= containerWidth) {
3083
+ return { modes, degradationLevel: 0 };
3084
+ }
3085
+ let degradationLevel = 1;
3086
+ for (const col of sortedByImportance) {
3087
+ if (protectedColumns.has(col.field)) continue;
3088
+ const priority = IMPORTANCE_PRIORITY[col.importance ?? "tertiary"];
3089
+ if (priority > 2) break;
3090
+ modes[col.field] = "compact";
3091
+ currentWidth = calculateAdaptiveWidth(columns, modes);
3092
+ if (currentWidth + buffer <= containerWidth) return { modes, degradationLevel };
3093
+ }
3094
+ degradationLevel = 2;
3095
+ for (const col of sortedByImportance) {
3096
+ if (protectedColumns.has(col.field)) continue;
3097
+ const priority = IMPORTANCE_PRIORITY[col.importance ?? "tertiary"];
3098
+ if (priority <= 2) continue;
3099
+ if (priority > 4) break;
3100
+ modes[col.field] = "compact";
3101
+ currentWidth = calculateAdaptiveWidth(columns, modes);
3102
+ if (currentWidth + buffer <= containerWidth) return { modes, degradationLevel };
3103
+ }
3104
+ degradationLevel = 3;
3105
+ let visibleCount = columns.length;
3106
+ for (const col of sortedByImportance) {
3107
+ const priority = IMPORTANCE_PRIORITY[col.importance ?? "tertiary"];
3108
+ if (priority > 2) break;
3109
+ if (visibleCount <= minColumnsVisible) break;
3110
+ modes[col.field] = "hidden";
3111
+ visibleCount--;
3112
+ currentWidth = calculateAdaptiveWidth(columns, modes);
3113
+ if (currentWidth + buffer <= containerWidth) return { modes, degradationLevel };
3114
+ }
3115
+ degradationLevel = 4;
3116
+ for (const col of sortedByImportance) {
3117
+ const priority = IMPORTANCE_PRIORITY[col.importance ?? "tertiary"];
3118
+ if (priority <= 2) continue;
3119
+ if (priority > 4) break;
3120
+ if (visibleCount <= minColumnsVisible) break;
3121
+ modes[col.field] = "hidden";
3122
+ visibleCount--;
3123
+ currentWidth = calculateAdaptiveWidth(columns, modes);
3124
+ if (currentWidth + buffer <= containerWidth) return { modes, degradationLevel };
3125
+ }
3126
+ degradationLevel = 5;
3127
+ for (const col of sortedByImportance) {
3128
+ if (modes[col.field] === "hidden") continue;
3129
+ modes[col.field] = "compact";
3130
+ currentWidth = calculateAdaptiveWidth(columns, modes);
3131
+ if (currentWidth + buffer <= containerWidth) return { modes, degradationLevel };
3132
+ }
3133
+ return { modes, degradationLevel: 6 };
3134
+ }
3135
+ function useAdaptiveTable(options) {
3136
+ const {
3137
+ columns,
3138
+ protectedColumns = [],
3139
+ minColumnsVisible = 2,
3140
+ minWidthForTable = TABLE_BREAKPOINTS.minForTable,
3141
+ debounceMs = 100,
3142
+ onModeChange
3143
+ } = options;
3144
+ const containerRef = useRef(null);
3145
+ const [containerWidth, setContainerWidth] = useState(0);
3146
+ const debounceTimerRef = useRef(null);
3147
+ const protectedSet = useMemo(() => new Set(protectedColumns), [protectedColumns]);
3148
+ const { modes: columnModes, degradationLevel } = useMemo(() => {
3149
+ if (containerWidth === 0) {
3150
+ const modes = {};
3151
+ columns.forEach((col) => modes[col.field] = "full");
3152
+ return { modes, degradationLevel: 0 };
3153
+ }
3154
+ return calculateColumnModes(columns, containerWidth, protectedSet, minColumnsVisible);
3155
+ }, [columns, containerWidth, protectedSet, minColumnsVisible]);
3156
+ const visibleColumns = useMemo(() => {
3157
+ return columns.filter((col) => columnModes[col.field] !== "hidden").map(
3158
+ (col) => columnModes[col.field] === "compact" ? { ...col, label: getCompactLabel(col.field, col.label) } : col
3159
+ );
3160
+ }, [columns, columnModes]);
3161
+ const calculatedWidth = useMemo(
3162
+ () => calculateAdaptiveWidth(columns, columnModes),
3163
+ [columns, columnModes]
3164
+ );
3165
+ const viewMode = useMemo(() => {
3166
+ if (containerWidth > 0 && containerWidth < minWidthForTable) return "cards";
3167
+ if (visibleColumns.length < minColumnsVisible) return "cards";
3168
+ return "table";
3169
+ }, [containerWidth, minWidthForTable, visibleColumns.length, minColumnsVisible]);
3170
+ useEffect(() => {
3171
+ const updateWidth = () => {
3172
+ if (containerRef.current) setContainerWidth(containerRef.current.clientWidth);
3173
+ };
3174
+ const initialTimer = setTimeout(updateWidth, 50);
3175
+ const resizeObserver = new ResizeObserver(() => {
3176
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
3177
+ debounceTimerRef.current = setTimeout(updateWidth, debounceMs);
3178
+ });
3179
+ if (containerRef.current) resizeObserver.observe(containerRef.current);
3180
+ return () => {
3181
+ clearTimeout(initialTimer);
3182
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
3183
+ resizeObserver.disconnect();
3184
+ };
3185
+ }, [debounceMs]);
3186
+ const prevModesRef = useRef(columnModes);
3187
+ useEffect(() => {
3188
+ if (onModeChange && columnModes !== prevModesRef.current) {
3189
+ onModeChange(columnModes);
3190
+ prevModesRef.current = columnModes;
3191
+ }
3192
+ }, [columnModes, onModeChange]);
3193
+ return {
3194
+ containerRef,
3195
+ columnModes,
3196
+ visibleColumns,
3197
+ viewMode,
3198
+ calculatedWidth,
3199
+ containerWidth,
3200
+ degradationLevel
3201
+ };
3202
+ }
2733
3203
  function useEntityData(entity, options = {}) {
2734
3204
  var _a, _b, _c;
2735
3205
  const {
@@ -4612,8 +5082,9 @@ function normalizeColumn(col) {
4612
5082
  return col;
4613
5083
  }
4614
5084
  function DataTable({
4615
- data,
4616
- columns,
5085
+ data: dataProp,
5086
+ columns: columnsProp,
5087
+ query,
4617
5088
  searchPlaceholder = "Search...",
4618
5089
  pageSize = 10,
4619
5090
  showPagination = true,
@@ -4622,12 +5093,12 @@ function DataTable({
4622
5093
  emptyMessage = "No data available",
4623
5094
  className = "",
4624
5095
  hover = false,
4625
- isLoading = false,
5096
+ isLoading: isLoadingProp = false,
4626
5097
  loadingItemCount = 5,
4627
5098
  responsive = true,
4628
5099
  renderMobileCard,
4629
5100
  ui,
4630
- error = null,
5101
+ error: errorProp = null,
4631
5102
  errorTitle,
4632
5103
  errorRetry,
4633
5104
  selectable = false,
@@ -4642,6 +5113,10 @@ function DataTable({
4642
5113
  const [currentPage, setCurrentPage] = useState(1);
4643
5114
  const currentBreakpoint = useBreakpoint();
4644
5115
  const isMobile = currentBreakpoint === "xs" || currentBreakpoint === "2xs" || currentBreakpoint === "sm";
5116
+ const data = dataProp ?? (query == null ? void 0 : query.data) ?? [];
5117
+ const columns = columnsProp ?? (query == null ? void 0 : query.columns) ?? [];
5118
+ const isLoading = isLoadingProp || (query == null ? void 0 : query.isLoading) || (query == null ? void 0 : query.isLoadingMetadata) || false;
5119
+ const error = errorProp ?? (query == null ? void 0 : query.error) ?? (query == null ? void 0 : query.metadataError) ?? null;
4645
5120
  const normalizedColumns = useMemo(
4646
5121
  () => columns.map((col) => normalizeColumn(col)),
4647
5122
  [columns]
@@ -5028,64 +5503,6 @@ const TableHead = TableHead$1;
5028
5503
  const TableHeader = TableHeader$1;
5029
5504
  const TableRow = TableRow$1;
5030
5505
  const TableFooter = TableFooter$1;
5031
- const IconBadge = ({
5032
- children,
5033
- icon,
5034
- variant = "category",
5035
- category = 1,
5036
- status = "neutral",
5037
- size = "md",
5038
- interactive = false,
5039
- onClick,
5040
- className,
5041
- tooltip
5042
- }) => {
5043
- const currentSize = useResponsiveValue(size);
5044
- const sizeConfig = responsiveScales.components.iconBadge[currentSize];
5045
- const sizeClasses2 = {
5046
- 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"}`,
5047
- sm: "w-8 h-8 text-xs rounded-lg",
5048
- md: "w-10 h-10 text-sm rounded-xl",
5049
- lg: "w-12 h-12 text-base rounded-xl"
5050
- };
5051
- const baseClasses = cn(
5052
- "inline-flex items-center justify-center font-bold shadow-md",
5053
- sizeClasses2[currentSize],
5054
- interactive && [
5055
- "cursor-pointer",
5056
- getAnimationClasses({
5057
- ...animationPresets.dataBadge,
5058
- size: currentSize === "lg" ? "lg" : currentSize === "sm" || currentSize === "xs" ? "sm" : "md"
5059
- })
5060
- ],
5061
- className
5062
- );
5063
- 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";
5064
- const badge = /* @__PURE__ */ jsxs(
5065
- "div",
5066
- {
5067
- className: cn(baseClasses, gradientClasses, "animate-fade-in"),
5068
- onClick,
5069
- role: interactive ? "button" : void 0,
5070
- tabIndex: interactive ? 0 : void 0,
5071
- onKeyDown: interactive ? (e) => {
5072
- if (e.key === "Enter" || e.key === " ") {
5073
- e.preventDefault();
5074
- onClick == null ? void 0 : onClick();
5075
- }
5076
- } : void 0,
5077
- "data-component-name": "IconBadge",
5078
- children: [
5079
- icon && /* @__PURE__ */ jsx("span", { className: "text-primary-foreground drop-shadow", children: icon }),
5080
- children && !icon && /* @__PURE__ */ jsx("span", { className: "text-primary-foreground font-bold", children })
5081
- ]
5082
- }
5083
- );
5084
- if (tooltip) {
5085
- return /* @__PURE__ */ jsx(Tooltip, { content: tooltip, position: "top", size: "sm", children: badge });
5086
- }
5087
- return badge;
5088
- };
5089
5506
  const Chart = ({
5090
5507
  title,
5091
5508
  subtitle,
@@ -5848,85 +6265,6 @@ const ProgressBar = ({
5848
6265
  )
5849
6266
  ] });
5850
6267
  };
5851
- const valueColorMap = {
5852
- success: "text-green-600",
5853
- error: "text-red-600",
5854
- warning: "text-yellow-600",
5855
- info: "text-blue-600",
5856
- neutral: "text-muted-foreground",
5857
- default: "text-foreground"
5858
- };
5859
- function ListCard({
5860
- icon,
5861
- title,
5862
- subtitle,
5863
- metadata,
5864
- value,
5865
- badge,
5866
- onClick,
5867
- className,
5868
- children
5869
- }) {
5870
- return /* @__PURE__ */ jsxs(
5871
- "div",
5872
- {
5873
- className: cn(
5874
- "flex items-start gap-3 p-3 rounded-lg border border-border transition-colors",
5875
- onClick && "hover:bg-muted/50 cursor-pointer",
5876
- className
5877
- ),
5878
- onClick,
5879
- children: [
5880
- icon && /* @__PURE__ */ jsx(
5881
- IconBadge,
5882
- {
5883
- variant: icon.variant || "category",
5884
- category: icon.variant === "category" ? icon.category || 1 : void 0,
5885
- status: icon.variant === "status" ? icon.status || "neutral" : void 0,
5886
- size: icon.size || "sm",
5887
- icon: icon.icon,
5888
- tooltip: icon.tooltip
5889
- }
5890
- ),
5891
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
5892
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
5893
- /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
5894
- /* @__PURE__ */ jsx("h4", { className: "text-sm font-medium text-foreground truncate", children: title }),
5895
- subtitle && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: subtitle }),
5896
- metadata && metadata.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 mt-1", children: metadata.map((item, index) => /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: item }, index)) })
5897
- ] }),
5898
- (value || badge) && /* @__PURE__ */ jsxs("div", { className: "text-right", children: [
5899
- value && /* @__PURE__ */ jsx(
5900
- "p",
5901
- {
5902
- className: cn(
5903
- "text-sm font-semibold",
5904
- value.variant && valueColorMap[value.variant],
5905
- value.className
5906
- ),
5907
- children: value.text
5908
- }
5909
- ),
5910
- badge && /* @__PURE__ */ jsx(
5911
- DataBadge,
5912
- {
5913
- variant: badge.variant,
5914
- status: badge.variant === "status" ? badge.status : void 0,
5915
- category: badge.variant === "category" ? badge.category : void 0,
5916
- size: badge.size || "sm",
5917
- icon: badge.icon,
5918
- className: "mt-1",
5919
- children: badge.text
5920
- }
5921
- )
5922
- ] })
5923
- ] }),
5924
- children
5925
- ] })
5926
- ]
5927
- }
5928
- );
5929
- }
5930
6268
  const TruncatedText = ({
5931
6269
  text,
5932
6270
  className,
@@ -9247,6 +9585,13 @@ const defaultShowcaseNavigation = [
9247
9585
  path: "/admin/dashboard",
9248
9586
  category: 2
9249
9587
  },
9588
+ {
9589
+ value: "admin-dashboard-v2",
9590
+ label: "Admin Dashboard V2",
9591
+ icon: "Shield",
9592
+ path: "/admin/dashboard-v2",
9593
+ category: 2
9594
+ },
9250
9595
  {
9251
9596
  value: "admin-users",
9252
9597
  label: "User Management",
@@ -9517,7 +9862,6 @@ const Sidebar = ({ className }) => {
9517
9862
  );
9518
9863
  };
9519
9864
  const AppHeader = ({ className }) => {
9520
- const isTrialMode = false;
9521
9865
  return /* @__PURE__ */ jsx(
9522
9866
  "header",
9523
9867
  {
@@ -9530,7 +9874,7 @@ const AppHeader = ({ className }) => {
9530
9874
  children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxs("div", { className: "flex h-16 items-center justify-between", children: [
9531
9875
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
9532
9876
  /* @__PURE__ */ jsx("h1", { className: "text-xl font-bold text-foreground", children: "Frontend Template" }),
9533
- isTrialMode
9877
+ /* @__PURE__ */ jsx(DataBadge, { variant: "status", status: "info", display: "icon-text", children: "Trial Mode" })
9534
9878
  ] }),
9535
9879
  /* @__PURE__ */ jsx("div", { className: "flex-1 max-w-2xl mx-8", children: /* @__PURE__ */ jsx(
9536
9880
  GlobalSearch,
@@ -14314,18 +14658,41 @@ function createReactApp(config) {
14314
14658
  enableRouting = true,
14315
14659
  auth,
14316
14660
  navigation,
14317
- customProviders = []
14661
+ customProviders = [],
14662
+ persistQueryCache = false,
14663
+ queryCacheKey = "PATTERN_STACK_QUERY_CACHE"
14318
14664
  } = appConfig;
14319
14665
  const queryClient = new QueryClient({
14320
14666
  defaultOptions: {
14321
14667
  queries: {
14322
14668
  retry: 1,
14323
14669
  refetchOnWindowFocus: false,
14324
- staleTime: 5 * 60 * 1e3
14670
+ staleTime: 5 * 60 * 1e3,
14325
14671
  // 5 minutes
14672
+ // Keep data longer when persistence is enabled
14673
+ gcTime: persistQueryCache ? 1e3 * 60 * 60 * 24 : 1e3 * 60 * 5
14326
14674
  }
14327
14675
  }
14328
14676
  });
14677
+ if (persistQueryCache && typeof window !== "undefined") {
14678
+ import("@tanstack/react-query-persist-client").then(({ persistQueryClient }) => {
14679
+ import("@tanstack/query-sync-storage-persister").then(({ createSyncStoragePersister }) => {
14680
+ const persister = createSyncStoragePersister({
14681
+ storage: window.localStorage,
14682
+ key: queryCacheKey
14683
+ });
14684
+ persistQueryClient({
14685
+ queryClient,
14686
+ persister,
14687
+ maxAge: 1e3 * 60 * 60 * 24
14688
+ // 24 hours
14689
+ });
14690
+ console.info("[app] Query cache persistence enabled");
14691
+ });
14692
+ }).catch((err) => {
14693
+ console.warn("[app] Query persistence not available:", err.message);
14694
+ });
14695
+ }
14329
14696
  const routes = [];
14330
14697
  const DefaultHome = () => /* @__PURE__ */ jsx(DashboardTemplate, { title, description, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center min-h-[400px] text-center", children: [
14331
14698
  /* @__PURE__ */ jsxs("h1", { className: "text-4xl font-bold text-foreground mb-4", children: [
@@ -14344,7 +14711,7 @@ function createReactApp(config) {
14344
14711
  ] }) });
14345
14712
  const createProviderTree = (children) => {
14346
14713
  let tree = children;
14347
- customProviders.reverse().forEach((Provider) => {
14714
+ [...customProviders].reverse().forEach((Provider) => {
14348
14715
  tree = /* @__PURE__ */ jsx(Provider, { children: tree }, Provider.name);
14349
14716
  });
14350
14717
  if (enableRouting) {
@@ -14387,10 +14754,10 @@ function createReactApp(config) {
14387
14754
  }
14388
14755
  return component;
14389
14756
  };
14390
- const AppContent = () => /* @__PURE__ */ jsx(Routes, { children: /* @__PURE__ */ jsxs(Route, { path: "/", element: /* @__PURE__ */ jsx(AppLayout, {}), children: [
14757
+ const AppContent = () => /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-screen", children: "Loading..." }), children: /* @__PURE__ */ jsx(Routes, { children: /* @__PURE__ */ jsxs(Route, { path: "/", element: /* @__PURE__ */ jsx(AppLayout, {}), children: [
14391
14758
  /* @__PURE__ */ jsx(Route, { index: true, element: wrapWithProtection("/", /* @__PURE__ */ jsx(DefaultHome, {})) }),
14392
14759
  routes.map(({ path, component }, index) => /* @__PURE__ */ jsx(Route, { path, element: wrapWithProtection(path, component) }, index))
14393
- ] }) });
14760
+ ] }) }) });
14394
14761
  return createProviderTree(/* @__PURE__ */ jsx(AppContent, {}));
14395
14762
  },
14396
14763
  mount(elementId = "root") {
@@ -17586,6 +17953,10 @@ export {
17586
17953
  Breadcrumb,
17587
17954
  BulkSelectionBar,
17588
17955
  Button,
17956
+ COLUMN_WIDTHS,
17957
+ COMPACT_COLUMN_WIDTHS,
17958
+ COMPACT_FORMATTERS,
17959
+ COMPACT_LABELS,
17589
17960
  Card,
17590
17961
  CardContent,
17591
17962
  CardDescription,
@@ -17644,6 +18015,7 @@ export {
17644
18015
  FormField,
17645
18016
  FormGroup,
17646
18017
  GlobalSearch,
18018
+ IMPORTANCE_VISIBILITY,
17647
18019
  Icon,
17648
18020
  IconBadge,
17649
18021
  Input,
@@ -17699,6 +18071,7 @@ export {
17699
18071
  StatCard,
17700
18072
  StyleGuide,
17701
18073
  Switch,
18074
+ TABLE_BREAKPOINTS,
17702
18075
  Table,
17703
18076
  TableBody,
17704
18077
  TableCaption,
@@ -17721,6 +18094,7 @@ export {
17721
18094
  animationPresets,
17722
18095
  apiClient,
17723
18096
  breakpoints,
18097
+ calculateTableWidth,
17724
18098
  cn,
17725
18099
  createAPIDataTemplate,
17726
18100
  createApiClient,
@@ -17730,6 +18104,7 @@ export {
17730
18104
  defaultFieldRenderers,
17731
18105
  detectBulkOperationType,
17732
18106
  detectUIConfig,
18107
+ determineTableMode,
17733
18108
  entityToListCardProps,
17734
18109
  env,
17735
18110
  formatNumberWithTooltip,
@@ -17737,6 +18112,9 @@ export {
17737
18112
  generateCompactColumns,
17738
18113
  getAnimationClasses,
17739
18114
  getChartHeight,
18115
+ getColumnWidth,
18116
+ getCompactFormatter,
18117
+ getCompactLabel,
17740
18118
  getContainerHeightClass,
17741
18119
  getCurrentBreakpoint,
17742
18120
  getFieldType,
@@ -17755,6 +18133,7 @@ export {
17755
18133
  responsiveScales,
17756
18134
  setGlobalAuthService,
17757
18135
  tooltipContent,
18136
+ useAdaptiveTable,
17758
18137
  useApiMutation,
17759
18138
  useApiQuery,
17760
18139
  useAuth,