@pattern-stack/frontend-patterns 0.2.0-alpha.5 → 0.2.0-alpha.6

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
@@ -9,7 +9,7 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
9
9
  import * as React from "react";
10
10
  import React__default, { useRef, useState, useEffect, useMemo, forwardRef, useCallback, useContext, createContext, useId, Component } from "react";
11
11
  import * as SheetPrimitive from "@radix-ui/react-dialog";
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, Loader2, EyeOff, InfoIcon, Circle, ChevronsUpDown, AreaChart, LineChart, TrendingDown, Minus, AlertTriangle, Activity, CheckCircle, FileX, Sun, Moon, LogOut, Square, Waves, Zap, TreePine, Sparkles, Sunset, Building2, DollarSign, ShoppingCart, Target, ExternalLink, MoreHorizontal, Filter, Edit2, Undo2, RefreshCw, FileText, History, Save, Copy, Grid3X3, Layers, XCircle, Briefcase } from "lucide-react";
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";
14
14
  import { cva } from "class-variance-authority";
15
15
  import * as AvatarPrimitive from "@radix-ui/react-avatar";
@@ -2009,6 +2009,362 @@ function renderField(value, fieldName, fieldType, breakpoint, item, customRender
2009
2009
  function cn(...inputs) {
2010
2010
  return twMerge(clsx(inputs));
2011
2011
  }
2012
+ const STATUS_COLOR_MAP = {
2013
+ // Success states
2014
+ active: "success",
2015
+ completed: "success",
2016
+ approved: "success",
2017
+ paid: "success",
2018
+ delivered: "success",
2019
+ resolved: "success",
2020
+ closed: "success",
2021
+ // Warning states
2022
+ pending: "warning",
2023
+ processing: "warning",
2024
+ review: "warning",
2025
+ draft: "warning",
2026
+ waiting: "warning",
2027
+ // Error states
2028
+ failed: "error",
2029
+ rejected: "error",
2030
+ cancelled: "error",
2031
+ overdue: "error",
2032
+ blocked: "error",
2033
+ // Info states
2034
+ new: "info",
2035
+ open: "info",
2036
+ in_progress: "info",
2037
+ scheduled: "info",
2038
+ // Neutral (default)
2039
+ inactive: "neutral",
2040
+ archived: "neutral",
2041
+ unknown: "neutral"
2042
+ };
2043
+ function getStatusColor(value) {
2044
+ if (typeof value !== "string") return "neutral";
2045
+ const normalized = value.toLowerCase().replace(/[_-]/g, "_");
2046
+ return STATUS_COLOR_MAP[normalized] ?? "neutral";
2047
+ }
2048
+ const CATEGORY_MAP = {
2049
+ account: 1,
2050
+ customer: 1,
2051
+ client: 1,
2052
+ user: 2,
2053
+ contact: 2,
2054
+ person: 2,
2055
+ order: 3,
2056
+ sale: 3,
2057
+ transaction: 3,
2058
+ product: 4,
2059
+ item: 4,
2060
+ inventory: 4,
2061
+ task: 5,
2062
+ activity: 5,
2063
+ event: 5,
2064
+ file: 6,
2065
+ document: 6,
2066
+ report: 6,
2067
+ tag: 7,
2068
+ category: 7,
2069
+ label: 7,
2070
+ default: 8
2071
+ };
2072
+ function getCategoryForEntity(entityType) {
2073
+ if (!entityType) return 8;
2074
+ const normalized = entityType.toLowerCase();
2075
+ for (const [key, value] of Object.entries(CATEGORY_MAP)) {
2076
+ if (normalized.includes(key)) return value;
2077
+ }
2078
+ return 8;
2079
+ }
2080
+ function getIconForEntity(entityType) {
2081
+ if (!entityType) return /* @__PURE__ */ jsx(Circle, { className: "w-4 h-4" });
2082
+ const normalized = entityType.toLowerCase();
2083
+ if (normalized.includes("account") || normalized.includes("customer") || normalized.includes("client")) {
2084
+ return /* @__PURE__ */ jsx(Building2, { className: "w-4 h-4" });
2085
+ }
2086
+ if (normalized.includes("user") || normalized.includes("contact") || normalized.includes("person")) {
2087
+ return /* @__PURE__ */ jsx(User, { className: "w-4 h-4" });
2088
+ }
2089
+ if (normalized.includes("product") || normalized.includes("item")) {
2090
+ return /* @__PURE__ */ jsx(Package, { className: "w-4 h-4" });
2091
+ }
2092
+ if (normalized.includes("file") || normalized.includes("document")) {
2093
+ return /* @__PURE__ */ jsx(FileText, { className: "w-4 h-4" });
2094
+ }
2095
+ if (normalized.includes("tag") || normalized.includes("category")) {
2096
+ return /* @__PURE__ */ jsx(Tag, { className: "w-4 h-4" });
2097
+ }
2098
+ return /* @__PURE__ */ jsx(Circle, { className: "w-4 h-4" });
2099
+ }
2100
+ function normalizeImportance(importance) {
2101
+ switch (importance) {
2102
+ case "critical":
2103
+ case "high":
2104
+ case "primary":
2105
+ return "primary";
2106
+ case "medium":
2107
+ case "secondary":
2108
+ return "secondary";
2109
+ default:
2110
+ return "tertiary";
2111
+ }
2112
+ }
2113
+ function isPrimaryImportance(importance) {
2114
+ return normalizeImportance(importance) === "primary";
2115
+ }
2116
+ function formatValueForCard(value, type, format) {
2117
+ if (value === null || value === void 0) return "—";
2118
+ switch (type) {
2119
+ case "money": {
2120
+ const num = Number(value);
2121
+ const currency = (format == null ? void 0 : format.currency) ?? "USD";
2122
+ if (num >= 1e6) {
2123
+ return new Intl.NumberFormat("en-US", {
2124
+ style: "currency",
2125
+ currency,
2126
+ notation: "compact",
2127
+ maximumFractionDigits: 1
2128
+ }).format(num);
2129
+ }
2130
+ if (num >= 1e3) {
2131
+ return new Intl.NumberFormat("en-US", {
2132
+ style: "currency",
2133
+ currency,
2134
+ notation: "compact",
2135
+ maximumFractionDigits: 1
2136
+ }).format(num);
2137
+ }
2138
+ return new Intl.NumberFormat("en-US", {
2139
+ style: "currency",
2140
+ currency,
2141
+ maximumFractionDigits: 0
2142
+ }).format(num);
2143
+ }
2144
+ case "percent": {
2145
+ const num = Number(value);
2146
+ return `${num.toFixed(0)}%`;
2147
+ }
2148
+ case "number": {
2149
+ const num = Number(value);
2150
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
2151
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
2152
+ return String(num);
2153
+ }
2154
+ case "date":
2155
+ case "datetime": {
2156
+ const date = new Date(String(value));
2157
+ if (isNaN(date.getTime())) return String(value);
2158
+ return date.toLocaleDateString("en-US", {
2159
+ month: "short",
2160
+ day: "numeric"
2161
+ });
2162
+ }
2163
+ case "boolean":
2164
+ return value ? "Yes" : "No";
2165
+ default:
2166
+ return String(value);
2167
+ }
2168
+ }
2169
+ function autoDetectMapping(columns) {
2170
+ var _a, _b, _c, _d, _e, _f;
2171
+ const primaryCols = columns.filter((c) => isPrimaryImportance(c.importance));
2172
+ 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);
2173
+ const valueField = (_d = primaryCols.find((c) => c.type === "money")) == null ? void 0 : _d.field;
2174
+ const statusField = (_e = primaryCols.find(
2175
+ (c) => c.type === "status" || c.type === "badge"
2176
+ )) == null ? void 0 : _e.field;
2177
+ const usedForMain = new Set([titleField, valueField, statusField].filter(Boolean));
2178
+ const remainingPrimary = primaryCols.filter((c) => !usedForMain.has(c.field));
2179
+ const subtitleField = (_f = remainingPrimary[0]) == null ? void 0 : _f.field;
2180
+ const usedFields = new Set(
2181
+ [titleField, valueField, statusField, subtitleField].filter(Boolean)
2182
+ );
2183
+ const metadataFields = primaryCols.filter((c) => !usedFields.has(c.field)).slice(0, 3).map((c) => c.field);
2184
+ return {
2185
+ titleField: titleField ?? "id",
2186
+ subtitleField,
2187
+ valueField,
2188
+ statusField,
2189
+ metadataFields,
2190
+ iconConfig: void 0
2191
+ };
2192
+ }
2193
+ function entityToListCardProps(item, columns, mapping, entityType) {
2194
+ const autoMapping = autoDetectMapping(columns);
2195
+ const effectiveMapping = { ...autoMapping, ...mapping };
2196
+ const {
2197
+ titleField,
2198
+ subtitleField,
2199
+ valueField,
2200
+ statusField,
2201
+ metadataFields,
2202
+ iconConfig
2203
+ } = effectiveMapping;
2204
+ const getColumn = (field) => field ? columns.find((c) => c.field === field) : void 0;
2205
+ const title = item[titleField] != null ? String(item[titleField]) : "—";
2206
+ const subtitle = subtitleField && item[subtitleField] != null ? String(item[subtitleField]) : void 0;
2207
+ let value;
2208
+ if (valueField && item[valueField] != null) {
2209
+ const valueCol = getColumn(valueField);
2210
+ const formatted = formatValueForCard(
2211
+ item[valueField],
2212
+ (valueCol == null ? void 0 : valueCol.type) ?? "text",
2213
+ valueCol == null ? void 0 : valueCol.format
2214
+ );
2215
+ value = {
2216
+ text: formatted,
2217
+ variant: "success"
2218
+ // Money typically green
2219
+ };
2220
+ }
2221
+ let badge;
2222
+ if (statusField && item[statusField] != null) {
2223
+ const statusValue = String(item[statusField]);
2224
+ badge = {
2225
+ text: statusValue,
2226
+ variant: "status",
2227
+ status: getStatusColor(statusValue),
2228
+ size: "sm"
2229
+ };
2230
+ }
2231
+ const metadata = metadataFields == null ? void 0 : metadataFields.map((field) => {
2232
+ const col = getColumn(field);
2233
+ if (!col || item[field] == null) return null;
2234
+ return formatValueForCard(item[field], col.type, col.format);
2235
+ }).filter((v) => v !== null);
2236
+ let icon;
2237
+ if (iconConfig) {
2238
+ const iconValue = iconConfig.field ? item[iconConfig.field] : void 0;
2239
+ if (iconConfig.variant === "status" && iconValue) {
2240
+ icon = {
2241
+ icon: iconConfig.icon ?? getIconForEntity(entityType),
2242
+ variant: "status",
2243
+ status: getStatusColor(iconValue),
2244
+ size: "sm"
2245
+ };
2246
+ } else {
2247
+ icon = {
2248
+ icon: iconConfig.icon ?? getIconForEntity(entityType),
2249
+ variant: "category",
2250
+ category: getCategoryForEntity(entityType),
2251
+ size: "sm"
2252
+ };
2253
+ }
2254
+ } else {
2255
+ icon = {
2256
+ icon: getIconForEntity(entityType),
2257
+ variant: "category",
2258
+ category: getCategoryForEntity(entityType),
2259
+ size: "sm"
2260
+ };
2261
+ }
2262
+ return {
2263
+ icon,
2264
+ title,
2265
+ subtitle,
2266
+ metadata,
2267
+ value,
2268
+ badge
2269
+ };
2270
+ }
2271
+ const HEADER_ABBREVIATIONS = {
2272
+ customer: "Cust",
2273
+ account: "Acct",
2274
+ amount: "Amt",
2275
+ quantity: "Qty",
2276
+ priority: "Pri",
2277
+ status: "Status",
2278
+ created: "Created",
2279
+ updated: "Updated",
2280
+ description: "Desc",
2281
+ total: "Total",
2282
+ items: "Items",
2283
+ number: "#",
2284
+ date: "Date",
2285
+ time: "Time"
2286
+ };
2287
+ function abbreviateHeader(label) {
2288
+ const lower = label.toLowerCase();
2289
+ for (const [key, abbrev] of Object.entries(HEADER_ABBREVIATIONS)) {
2290
+ if (lower.includes(key)) return abbrev;
2291
+ }
2292
+ if (label.length > 8) return label.slice(0, 6) + "…";
2293
+ return label;
2294
+ }
2295
+ function generateCompactColumns(columns, options = {}) {
2296
+ const {
2297
+ maxColumns = 5,
2298
+ abbreviateHeaders = true,
2299
+ requiredFields = [],
2300
+ excludeFields = []
2301
+ } = options;
2302
+ const excludeSet = new Set(excludeFields);
2303
+ const requiredSet = new Set(requiredFields);
2304
+ const filteredColumns = columns.filter((col) => {
2305
+ if (excludeSet.has(col.field)) return false;
2306
+ return true;
2307
+ });
2308
+ const required = filteredColumns.filter((c) => requiredSet.has(c.field));
2309
+ const primary = filteredColumns.filter(
2310
+ (c) => !requiredSet.has(c.field) && normalizeImportance(c.importance) === "primary"
2311
+ );
2312
+ const secondary = filteredColumns.filter(
2313
+ (c) => !requiredSet.has(c.field) && normalizeImportance(c.importance) === "secondary"
2314
+ );
2315
+ const selectedColumns = [...required, ...primary, ...secondary].slice(
2316
+ 0,
2317
+ maxColumns
2318
+ );
2319
+ return selectedColumns.map((col) => ({
2320
+ key: col.field,
2321
+ header: abbreviateHeaders ? abbreviateHeader(col.label) : col.label,
2322
+ sortable: col.sortable,
2323
+ type: col.type,
2324
+ format: col.format,
2325
+ // Compact widths
2326
+ width: getCompactWidth(col.type)
2327
+ }));
2328
+ }
2329
+ function getCompactWidth(type) {
2330
+ switch (type) {
2331
+ case "money":
2332
+ return "70px";
2333
+ case "number":
2334
+ case "percent":
2335
+ return "50px";
2336
+ case "status":
2337
+ case "badge":
2338
+ return "80px";
2339
+ case "date":
2340
+ return "70px";
2341
+ case "datetime":
2342
+ return "90px";
2343
+ case "boolean":
2344
+ return "50px";
2345
+ default:
2346
+ return "120px";
2347
+ }
2348
+ }
2349
+ function createMobileCardRenderer(columns, mapping, entityType) {
2350
+ const { ListCard: ListCard2 } = require("../components/data/ListCard");
2351
+ return ({ data, onItemClick }) => /* @__PURE__ */ jsx("div", { className: "space-y-2", children: data.map((item, index) => {
2352
+ const cardProps = entityToListCardProps(
2353
+ item,
2354
+ columns,
2355
+ mapping,
2356
+ entityType
2357
+ );
2358
+ return /* @__PURE__ */ jsx(
2359
+ ListCard2,
2360
+ {
2361
+ ...cardProps,
2362
+ onClick: onItemClick ? () => onItemClick(item) : void 0
2363
+ },
2364
+ item.id ?? index
2365
+ );
2366
+ }) });
2367
+ }
2012
2368
  let globalAuthService = null;
2013
2369
  function setGlobalAuthService(authService) {
2014
2370
  globalAuthService = authService;
@@ -2062,6 +2418,9 @@ class ApiClient {
2062
2418
  async (error) => {
2063
2419
  var _a, _b, _c, _d;
2064
2420
  const status = (_a = error.response) == null ? void 0 : _a.status;
2421
+ if (!error.response) {
2422
+ return Promise.reject(error);
2423
+ }
2065
2424
  if (status === 401 || status === 403) {
2066
2425
  if (globalAuthService) {
2067
2426
  const tokenData = globalAuthService.getTokenData();
@@ -2079,12 +2438,12 @@ class ApiClient {
2079
2438
  const errorInfo = { status, message: (_c = (_b = error.response) == null ? void 0 : _b.data) == null ? void 0 : _c.detail };
2080
2439
  const handled = (_d = globalAuthService.onAuthError) == null ? void 0 : _d.call(globalAuthService, errorInfo);
2081
2440
  if (!handled) {
2082
- window.location.reload();
2441
+ window.location.href = "/login";
2083
2442
  }
2084
2443
  } else {
2085
2444
  localStorage.removeItem("auth_token");
2086
2445
  localStorage.removeItem("auth_user");
2087
- window.location.reload();
2446
+ window.location.href = "/login";
2088
2447
  }
2089
2448
  }
2090
2449
  return Promise.reject(error);
@@ -2459,6 +2818,61 @@ function useEntityDetail(entity, id, options = {}) {
2459
2818
  refetch
2460
2819
  };
2461
2820
  }
2821
+ function useOnlineStatus(healthEndpoint) {
2822
+ const [isOnline, setIsOnline] = useState(
2823
+ typeof navigator !== "undefined" ? navigator.onLine : true
2824
+ );
2825
+ const [isBackendReachable, setIsBackendReachable] = useState(true);
2826
+ const [lastBackendContact, setLastBackendContact] = useState(
2827
+ null
2828
+ );
2829
+ useEffect(() => {
2830
+ const handleOnline = () => setIsOnline(true);
2831
+ const handleOffline = () => {
2832
+ setIsOnline(false);
2833
+ setIsBackendReachable(false);
2834
+ };
2835
+ window.addEventListener("online", handleOnline);
2836
+ window.addEventListener("offline", handleOffline);
2837
+ return () => {
2838
+ window.removeEventListener("online", handleOnline);
2839
+ window.removeEventListener("offline", handleOffline);
2840
+ };
2841
+ }, []);
2842
+ const checkBackend = useCallback(async () => {
2843
+ if (!isOnline) {
2844
+ setIsBackendReachable(false);
2845
+ return false;
2846
+ }
2847
+ try {
2848
+ const endpoint = healthEndpoint || "/api/v1/health";
2849
+ const response = await fetch(endpoint, {
2850
+ method: "GET",
2851
+ cache: "no-store"
2852
+ });
2853
+ const reachable = response.ok;
2854
+ setIsBackendReachable(reachable);
2855
+ if (reachable) {
2856
+ setLastBackendContact(Date.now());
2857
+ }
2858
+ return reachable;
2859
+ } catch {
2860
+ setIsBackendReachable(false);
2861
+ return false;
2862
+ }
2863
+ }, [isOnline, healthEndpoint]);
2864
+ useEffect(() => {
2865
+ if (isOnline) {
2866
+ checkBackend();
2867
+ }
2868
+ }, [isOnline, checkBackend]);
2869
+ return {
2870
+ isOnline,
2871
+ isBackendReachable,
2872
+ lastBackendContact,
2873
+ checkBackend
2874
+ };
2875
+ }
2462
2876
  const buttonVariants = cva(
2463
2877
  "inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/20 focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-50 active:scale-[0.98]",
2464
2878
  {
@@ -5715,6 +6129,75 @@ const ActivityFeed = ({
5715
6129
  ] }) })
5716
6130
  ] });
5717
6131
  };
6132
+ const statusConfig = {
6133
+ online: {
6134
+ icon: Cloud,
6135
+ label: "Online",
6136
+ color: "text-green-600 dark:text-green-400",
6137
+ bgColor: "bg-green-100 dark:bg-green-900/30"
6138
+ },
6139
+ degraded: {
6140
+ icon: CloudOff,
6141
+ label: "Server Offline",
6142
+ color: "text-yellow-600 dark:text-yellow-400",
6143
+ bgColor: "bg-yellow-100 dark:bg-yellow-900/30"
6144
+ },
6145
+ offline: {
6146
+ icon: WifiOff,
6147
+ label: "Offline",
6148
+ color: "text-red-600 dark:text-red-400",
6149
+ bgColor: "bg-red-100 dark:bg-red-900/30"
6150
+ }
6151
+ };
6152
+ function ConnectionStatus({
6153
+ healthEndpoint,
6154
+ variant = "badge",
6155
+ className,
6156
+ showWhenOnline = false
6157
+ }) {
6158
+ const { isOnline, isBackendReachable } = useOnlineStatus(healthEndpoint);
6159
+ const status = !isOnline ? "offline" : !isBackendReachable ? "degraded" : "online";
6160
+ if (status === "online" && !showWhenOnline) {
6161
+ return null;
6162
+ }
6163
+ const config = statusConfig[status];
6164
+ const Icon2 = config.icon;
6165
+ if (variant === "badge") {
6166
+ return /* @__PURE__ */ jsxs(
6167
+ "div",
6168
+ {
6169
+ className: cn(
6170
+ "inline-flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-medium",
6171
+ config.bgColor,
6172
+ config.color,
6173
+ className
6174
+ ),
6175
+ children: [
6176
+ /* @__PURE__ */ jsx(Icon2, { className: "h-3 w-3" }),
6177
+ /* @__PURE__ */ jsx("span", { children: config.label })
6178
+ ]
6179
+ }
6180
+ );
6181
+ }
6182
+ return /* @__PURE__ */ jsxs(
6183
+ "div",
6184
+ {
6185
+ className: cn(
6186
+ "flex items-center gap-2 px-3 py-2 rounded-lg",
6187
+ config.bgColor,
6188
+ className
6189
+ ),
6190
+ children: [
6191
+ /* @__PURE__ */ jsx(Icon2, { className: cn("h-4 w-4", config.color) }),
6192
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
6193
+ /* @__PURE__ */ jsx("span", { className: cn("text-sm font-medium", config.color), children: config.label }),
6194
+ status === "degraded" && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Working with cached data" }),
6195
+ status === "offline" && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Check your connection" })
6196
+ ] })
6197
+ ]
6198
+ }
6199
+ );
6200
+ }
5718
6201
  const FormField = ({
5719
6202
  label,
5720
6203
  description,
@@ -9056,7 +9539,10 @@ const AppHeader = ({ className }) => {
9056
9539
  placeholder: "Search models, tests, jobs, and components..."
9057
9540
  }
9058
9541
  ) }),
9059
- /* @__PURE__ */ jsx("div", { className: "flex items-center space-x-4", children: /* @__PURE__ */ jsx(UserMenu, { category: 8 }) })
9542
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-4", children: [
9543
+ /* @__PURE__ */ jsx(ConnectionStatus, {}),
9544
+ /* @__PURE__ */ jsx(UserMenu, { category: 8 })
9545
+ ] })
9060
9546
  ] }) })
9061
9547
  }
9062
9548
  );
@@ -10409,10 +10895,12 @@ function AuthProvider({
10409
10895
  try {
10410
10896
  await authService.refreshToken();
10411
10897
  setUser(storedUser);
10412
- try {
10413
- const freshUser = await authService.getCurrentUser();
10414
- if (freshUser) setUser(freshUser);
10415
- } catch {
10898
+ if (!(config == null ? void 0 : config.offlineFirst)) {
10899
+ try {
10900
+ const freshUser = await authService.getCurrentUser();
10901
+ if (freshUser) setUser(freshUser);
10902
+ } catch {
10903
+ }
10416
10904
  }
10417
10905
  } catch {
10418
10906
  authService.clearAuth();
@@ -10422,10 +10910,12 @@ function AuthProvider({
10422
10910
  }
10423
10911
  } else {
10424
10912
  setUser(storedUser);
10425
- try {
10426
- const freshUser = await authService.getCurrentUser();
10427
- if (freshUser) setUser(freshUser);
10428
- } catch {
10913
+ if (!(config == null ? void 0 : config.offlineFirst)) {
10914
+ try {
10915
+ const freshUser = await authService.getCurrentUser();
10916
+ if (freshUser) setUser(freshUser);
10917
+ } catch {
10918
+ }
10429
10919
  }
10430
10920
  }
10431
10921
  }
@@ -17108,6 +17598,7 @@ export {
17108
17598
  ColorSwatch,
17109
17599
  ComponentShowcasePage,
17110
17600
  ComponentShowcaseTemplate,
17601
+ ConnectionStatus,
17111
17602
  DASHBOARD_CHART_HEIGHTS,
17112
17603
  DASHBOARD_CONTAINER_HEIGHTS,
17113
17604
  DASHBOARD_HEIGHT_CLASSES,
@@ -17233,14 +17724,17 @@ export {
17233
17724
  cn,
17234
17725
  createAPIDataTemplate,
17235
17726
  createApiClient,
17727
+ createMobileCardRenderer,
17236
17728
  createReactApp,
17237
17729
  createSimpleApp,
17238
17730
  defaultFieldRenderers,
17239
17731
  detectBulkOperationType,
17240
17732
  detectUIConfig,
17733
+ entityToListCardProps,
17241
17734
  env,
17242
17735
  formatNumberWithTooltip,
17243
17736
  generateBulkOperationName,
17737
+ generateCompactColumns,
17244
17738
  getAnimationClasses,
17245
17739
  getChartHeight,
17246
17740
  getContainerHeightClass,
@@ -17282,6 +17776,7 @@ export {
17282
17776
  useMediaQuery,
17283
17777
  useMockAuth,
17284
17778
  useNavigation,
17779
+ useOnlineStatus,
17285
17780
  useOverflowDetection,
17286
17781
  usePermissions,
17287
17782
  useResponsiveClasses,