@pattern-stack/frontend-patterns 0.2.0-alpha.4 → 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/atoms/components/data/DataTable/DataTable.d.ts +1 -1
- package/dist/atoms/components/data/DataTable/DataTable.d.ts.map +1 -1
- package/dist/atoms/components/data/DataTable/DataTable.types.d.ts +28 -0
- package/dist/atoms/components/data/DataTable/DataTable.types.d.ts.map +1 -1
- package/dist/atoms/components/data/index.d.ts +1 -0
- package/dist/atoms/components/data/index.d.ts.map +1 -1
- package/dist/atoms/composed/ConnectionStatus/ConnectionStatus.d.ts +16 -0
- package/dist/atoms/composed/ConnectionStatus/ConnectionStatus.d.ts.map +1 -0
- package/dist/atoms/composed/ConnectionStatus/index.d.ts +3 -0
- package/dist/atoms/composed/ConnectionStatus/index.d.ts.map +1 -0
- package/dist/atoms/hooks/index.d.ts +2 -0
- package/dist/atoms/hooks/index.d.ts.map +1 -1
- package/dist/atoms/hooks/useEntityDetail.d.ts +43 -0
- package/dist/atoms/hooks/useEntityDetail.d.ts.map +1 -0
- package/dist/atoms/hooks/useOnlineStatus.d.ts +16 -0
- package/dist/atoms/hooks/useOnlineStatus.d.ts.map +1 -0
- package/dist/atoms/primitives/table.d.ts.map +1 -1
- package/dist/atoms/services/api/client.d.ts +12 -2
- package/dist/atoms/services/api/client.d.ts.map +1 -1
- package/dist/atoms/services/auth-service.d.ts +15 -0
- package/dist/atoms/services/auth-service.d.ts.map +1 -1
- package/dist/atoms/services/index.d.ts +2 -2
- package/dist/atoms/services/index.d.ts.map +1 -1
- package/dist/atoms/types/auth.d.ts +44 -2
- package/dist/atoms/types/auth.d.ts.map +1 -1
- package/dist/atoms/types/ui-config.d.ts +11 -0
- package/dist/atoms/types/ui-config.d.ts.map +1 -1
- package/dist/atoms/utils/entity-card-mapping.d.ts +105 -0
- package/dist/atoms/utils/entity-card-mapping.d.ts.map +1 -0
- package/dist/atoms/utils/index.d.ts +1 -0
- package/dist/atoms/utils/index.d.ts.map +1 -1
- package/dist/features/auth/components/ProtectedRoute.d.ts +3 -1
- package/dist/features/auth/components/ProtectedRoute.d.ts.map +1 -1
- package/dist/features/auth/hooks/useAuth.d.ts.map +1 -1
- package/dist/features/auth/providers/NoAuthProvider.d.ts +17 -0
- package/dist/features/auth/providers/NoAuthProvider.d.ts.map +1 -0
- package/dist/features/auth/providers/index.d.ts +1 -0
- package/dist/features/auth/providers/index.d.ts.map +1 -1
- package/dist/frontend-patterns.css +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +857 -91
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +854 -88
- package/dist/index.js.map +1 -1
- package/dist/molecules/layout/AppHeader/AppHeader.d.ts.map +1 -1
- package/dist/molecules/layout/FieldGrid/FieldGrid.d.ts +61 -0
- package/dist/molecules/layout/FieldGrid/FieldGrid.d.ts.map +1 -0
- package/dist/molecules/layout/FieldGrid/index.d.ts +2 -0
- package/dist/molecules/layout/FieldGrid/index.d.ts.map +1 -0
- package/dist/molecules/layout/index.d.ts +1 -0
- package/dist/molecules/layout/index.d.ts.map +1 -1
- package/dist/templates/factory.d.ts.map +1 -1
- package/package.json +4 -5
package/dist/index.js
CHANGED
|
@@ -77,8 +77,8 @@ const UI_CONFIG = {
|
|
|
77
77
|
TOAST_DURATION: 5e3
|
|
78
78
|
};
|
|
79
79
|
const env = {
|
|
80
|
-
API_BASE_URL: "
|
|
81
|
-
OPENAPI_URL: "
|
|
80
|
+
API_BASE_URL: "",
|
|
81
|
+
OPENAPI_URL: "/openapi.json",
|
|
82
82
|
NODE_ENV: "development",
|
|
83
83
|
IS_DEVELOPMENT: false,
|
|
84
84
|
IS_PRODUCTION: false,
|
|
@@ -2052,16 +2052,374 @@ function renderField(value, fieldName, fieldType, breakpoint, item, customRender
|
|
|
2052
2052
|
function cn(...inputs) {
|
|
2053
2053
|
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
2054
2054
|
}
|
|
2055
|
-
const
|
|
2055
|
+
const STATUS_COLOR_MAP = {
|
|
2056
|
+
// Success states
|
|
2057
|
+
active: "success",
|
|
2058
|
+
completed: "success",
|
|
2059
|
+
approved: "success",
|
|
2060
|
+
paid: "success",
|
|
2061
|
+
delivered: "success",
|
|
2062
|
+
resolved: "success",
|
|
2063
|
+
closed: "success",
|
|
2064
|
+
// Warning states
|
|
2065
|
+
pending: "warning",
|
|
2066
|
+
processing: "warning",
|
|
2067
|
+
review: "warning",
|
|
2068
|
+
draft: "warning",
|
|
2069
|
+
waiting: "warning",
|
|
2070
|
+
// Error states
|
|
2071
|
+
failed: "error",
|
|
2072
|
+
rejected: "error",
|
|
2073
|
+
cancelled: "error",
|
|
2074
|
+
overdue: "error",
|
|
2075
|
+
blocked: "error",
|
|
2076
|
+
// Info states
|
|
2077
|
+
new: "info",
|
|
2078
|
+
open: "info",
|
|
2079
|
+
in_progress: "info",
|
|
2080
|
+
scheduled: "info",
|
|
2081
|
+
// Neutral (default)
|
|
2082
|
+
inactive: "neutral",
|
|
2083
|
+
archived: "neutral",
|
|
2084
|
+
unknown: "neutral"
|
|
2085
|
+
};
|
|
2086
|
+
function getStatusColor(value) {
|
|
2087
|
+
if (typeof value !== "string") return "neutral";
|
|
2088
|
+
const normalized = value.toLowerCase().replace(/[_-]/g, "_");
|
|
2089
|
+
return STATUS_COLOR_MAP[normalized] ?? "neutral";
|
|
2090
|
+
}
|
|
2091
|
+
const CATEGORY_MAP = {
|
|
2092
|
+
account: 1,
|
|
2093
|
+
customer: 1,
|
|
2094
|
+
client: 1,
|
|
2095
|
+
user: 2,
|
|
2096
|
+
contact: 2,
|
|
2097
|
+
person: 2,
|
|
2098
|
+
order: 3,
|
|
2099
|
+
sale: 3,
|
|
2100
|
+
transaction: 3,
|
|
2101
|
+
product: 4,
|
|
2102
|
+
item: 4,
|
|
2103
|
+
inventory: 4,
|
|
2104
|
+
task: 5,
|
|
2105
|
+
activity: 5,
|
|
2106
|
+
event: 5,
|
|
2107
|
+
file: 6,
|
|
2108
|
+
document: 6,
|
|
2109
|
+
report: 6,
|
|
2110
|
+
tag: 7,
|
|
2111
|
+
category: 7,
|
|
2112
|
+
label: 7,
|
|
2113
|
+
default: 8
|
|
2114
|
+
};
|
|
2115
|
+
function getCategoryForEntity(entityType) {
|
|
2116
|
+
if (!entityType) return 8;
|
|
2117
|
+
const normalized = entityType.toLowerCase();
|
|
2118
|
+
for (const [key, value] of Object.entries(CATEGORY_MAP)) {
|
|
2119
|
+
if (normalized.includes(key)) return value;
|
|
2120
|
+
}
|
|
2121
|
+
return 8;
|
|
2122
|
+
}
|
|
2123
|
+
function getIconForEntity(entityType) {
|
|
2124
|
+
if (!entityType) return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Circle, { className: "w-4 h-4" });
|
|
2125
|
+
const normalized = entityType.toLowerCase();
|
|
2126
|
+
if (normalized.includes("account") || normalized.includes("customer") || normalized.includes("client")) {
|
|
2127
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Building2, { className: "w-4 h-4" });
|
|
2128
|
+
}
|
|
2129
|
+
if (normalized.includes("user") || normalized.includes("contact") || normalized.includes("person")) {
|
|
2130
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "w-4 h-4" });
|
|
2131
|
+
}
|
|
2132
|
+
if (normalized.includes("product") || normalized.includes("item")) {
|
|
2133
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Package, { className: "w-4 h-4" });
|
|
2134
|
+
}
|
|
2135
|
+
if (normalized.includes("file") || normalized.includes("document")) {
|
|
2136
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "w-4 h-4" });
|
|
2137
|
+
}
|
|
2138
|
+
if (normalized.includes("tag") || normalized.includes("category")) {
|
|
2139
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Tag, { className: "w-4 h-4" });
|
|
2140
|
+
}
|
|
2141
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Circle, { className: "w-4 h-4" });
|
|
2142
|
+
}
|
|
2143
|
+
function normalizeImportance(importance) {
|
|
2144
|
+
switch (importance) {
|
|
2145
|
+
case "critical":
|
|
2146
|
+
case "high":
|
|
2147
|
+
case "primary":
|
|
2148
|
+
return "primary";
|
|
2149
|
+
case "medium":
|
|
2150
|
+
case "secondary":
|
|
2151
|
+
return "secondary";
|
|
2152
|
+
default:
|
|
2153
|
+
return "tertiary";
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
function isPrimaryImportance(importance) {
|
|
2157
|
+
return normalizeImportance(importance) === "primary";
|
|
2158
|
+
}
|
|
2159
|
+
function formatValueForCard(value, type, format) {
|
|
2160
|
+
if (value === null || value === void 0) return "—";
|
|
2161
|
+
switch (type) {
|
|
2162
|
+
case "money": {
|
|
2163
|
+
const num = Number(value);
|
|
2164
|
+
const currency = (format == null ? void 0 : format.currency) ?? "USD";
|
|
2165
|
+
if (num >= 1e6) {
|
|
2166
|
+
return new Intl.NumberFormat("en-US", {
|
|
2167
|
+
style: "currency",
|
|
2168
|
+
currency,
|
|
2169
|
+
notation: "compact",
|
|
2170
|
+
maximumFractionDigits: 1
|
|
2171
|
+
}).format(num);
|
|
2172
|
+
}
|
|
2173
|
+
if (num >= 1e3) {
|
|
2174
|
+
return new Intl.NumberFormat("en-US", {
|
|
2175
|
+
style: "currency",
|
|
2176
|
+
currency,
|
|
2177
|
+
notation: "compact",
|
|
2178
|
+
maximumFractionDigits: 1
|
|
2179
|
+
}).format(num);
|
|
2180
|
+
}
|
|
2181
|
+
return new Intl.NumberFormat("en-US", {
|
|
2182
|
+
style: "currency",
|
|
2183
|
+
currency,
|
|
2184
|
+
maximumFractionDigits: 0
|
|
2185
|
+
}).format(num);
|
|
2186
|
+
}
|
|
2187
|
+
case "percent": {
|
|
2188
|
+
const num = Number(value);
|
|
2189
|
+
return `${num.toFixed(0)}%`;
|
|
2190
|
+
}
|
|
2191
|
+
case "number": {
|
|
2192
|
+
const num = Number(value);
|
|
2193
|
+
if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
|
|
2194
|
+
if (num >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
|
|
2195
|
+
return String(num);
|
|
2196
|
+
}
|
|
2197
|
+
case "date":
|
|
2198
|
+
case "datetime": {
|
|
2199
|
+
const date = new Date(String(value));
|
|
2200
|
+
if (isNaN(date.getTime())) return String(value);
|
|
2201
|
+
return date.toLocaleDateString("en-US", {
|
|
2202
|
+
month: "short",
|
|
2203
|
+
day: "numeric"
|
|
2204
|
+
});
|
|
2205
|
+
}
|
|
2206
|
+
case "boolean":
|
|
2207
|
+
return value ? "Yes" : "No";
|
|
2208
|
+
default:
|
|
2209
|
+
return String(value);
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
function autoDetectMapping(columns) {
|
|
2213
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2214
|
+
const primaryCols = columns.filter((c) => isPrimaryImportance(c.importance));
|
|
2215
|
+
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);
|
|
2216
|
+
const valueField = (_d = primaryCols.find((c) => c.type === "money")) == null ? void 0 : _d.field;
|
|
2217
|
+
const statusField = (_e = primaryCols.find(
|
|
2218
|
+
(c) => c.type === "status" || c.type === "badge"
|
|
2219
|
+
)) == null ? void 0 : _e.field;
|
|
2220
|
+
const usedForMain = new Set([titleField, valueField, statusField].filter(Boolean));
|
|
2221
|
+
const remainingPrimary = primaryCols.filter((c) => !usedForMain.has(c.field));
|
|
2222
|
+
const subtitleField = (_f = remainingPrimary[0]) == null ? void 0 : _f.field;
|
|
2223
|
+
const usedFields = new Set(
|
|
2224
|
+
[titleField, valueField, statusField, subtitleField].filter(Boolean)
|
|
2225
|
+
);
|
|
2226
|
+
const metadataFields = primaryCols.filter((c) => !usedFields.has(c.field)).slice(0, 3).map((c) => c.field);
|
|
2227
|
+
return {
|
|
2228
|
+
titleField: titleField ?? "id",
|
|
2229
|
+
subtitleField,
|
|
2230
|
+
valueField,
|
|
2231
|
+
statusField,
|
|
2232
|
+
metadataFields,
|
|
2233
|
+
iconConfig: void 0
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
function entityToListCardProps(item, columns, mapping, entityType) {
|
|
2237
|
+
const autoMapping = autoDetectMapping(columns);
|
|
2238
|
+
const effectiveMapping = { ...autoMapping, ...mapping };
|
|
2239
|
+
const {
|
|
2240
|
+
titleField,
|
|
2241
|
+
subtitleField,
|
|
2242
|
+
valueField,
|
|
2243
|
+
statusField,
|
|
2244
|
+
metadataFields,
|
|
2245
|
+
iconConfig
|
|
2246
|
+
} = effectiveMapping;
|
|
2247
|
+
const getColumn = (field) => field ? columns.find((c) => c.field === field) : void 0;
|
|
2248
|
+
const title = item[titleField] != null ? String(item[titleField]) : "—";
|
|
2249
|
+
const subtitle = subtitleField && item[subtitleField] != null ? String(item[subtitleField]) : void 0;
|
|
2250
|
+
let value;
|
|
2251
|
+
if (valueField && item[valueField] != null) {
|
|
2252
|
+
const valueCol = getColumn(valueField);
|
|
2253
|
+
const formatted = formatValueForCard(
|
|
2254
|
+
item[valueField],
|
|
2255
|
+
(valueCol == null ? void 0 : valueCol.type) ?? "text",
|
|
2256
|
+
valueCol == null ? void 0 : valueCol.format
|
|
2257
|
+
);
|
|
2258
|
+
value = {
|
|
2259
|
+
text: formatted,
|
|
2260
|
+
variant: "success"
|
|
2261
|
+
// Money typically green
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
2264
|
+
let badge;
|
|
2265
|
+
if (statusField && item[statusField] != null) {
|
|
2266
|
+
const statusValue = String(item[statusField]);
|
|
2267
|
+
badge = {
|
|
2268
|
+
text: statusValue,
|
|
2269
|
+
variant: "status",
|
|
2270
|
+
status: getStatusColor(statusValue),
|
|
2271
|
+
size: "sm"
|
|
2272
|
+
};
|
|
2273
|
+
}
|
|
2274
|
+
const metadata = metadataFields == null ? void 0 : metadataFields.map((field) => {
|
|
2275
|
+
const col = getColumn(field);
|
|
2276
|
+
if (!col || item[field] == null) return null;
|
|
2277
|
+
return formatValueForCard(item[field], col.type, col.format);
|
|
2278
|
+
}).filter((v) => v !== null);
|
|
2279
|
+
let icon;
|
|
2280
|
+
if (iconConfig) {
|
|
2281
|
+
const iconValue = iconConfig.field ? item[iconConfig.field] : void 0;
|
|
2282
|
+
if (iconConfig.variant === "status" && iconValue) {
|
|
2283
|
+
icon = {
|
|
2284
|
+
icon: iconConfig.icon ?? getIconForEntity(entityType),
|
|
2285
|
+
variant: "status",
|
|
2286
|
+
status: getStatusColor(iconValue),
|
|
2287
|
+
size: "sm"
|
|
2288
|
+
};
|
|
2289
|
+
} else {
|
|
2290
|
+
icon = {
|
|
2291
|
+
icon: iconConfig.icon ?? getIconForEntity(entityType),
|
|
2292
|
+
variant: "category",
|
|
2293
|
+
category: getCategoryForEntity(entityType),
|
|
2294
|
+
size: "sm"
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
} else {
|
|
2298
|
+
icon = {
|
|
2299
|
+
icon: getIconForEntity(entityType),
|
|
2300
|
+
variant: "category",
|
|
2301
|
+
category: getCategoryForEntity(entityType),
|
|
2302
|
+
size: "sm"
|
|
2303
|
+
};
|
|
2304
|
+
}
|
|
2305
|
+
return {
|
|
2306
|
+
icon,
|
|
2307
|
+
title,
|
|
2308
|
+
subtitle,
|
|
2309
|
+
metadata,
|
|
2310
|
+
value,
|
|
2311
|
+
badge
|
|
2312
|
+
};
|
|
2313
|
+
}
|
|
2314
|
+
const HEADER_ABBREVIATIONS = {
|
|
2315
|
+
customer: "Cust",
|
|
2316
|
+
account: "Acct",
|
|
2317
|
+
amount: "Amt",
|
|
2318
|
+
quantity: "Qty",
|
|
2319
|
+
priority: "Pri",
|
|
2320
|
+
status: "Status",
|
|
2321
|
+
created: "Created",
|
|
2322
|
+
updated: "Updated",
|
|
2323
|
+
description: "Desc",
|
|
2324
|
+
total: "Total",
|
|
2325
|
+
items: "Items",
|
|
2326
|
+
number: "#",
|
|
2327
|
+
date: "Date",
|
|
2328
|
+
time: "Time"
|
|
2329
|
+
};
|
|
2330
|
+
function abbreviateHeader(label) {
|
|
2331
|
+
const lower = label.toLowerCase();
|
|
2332
|
+
for (const [key, abbrev] of Object.entries(HEADER_ABBREVIATIONS)) {
|
|
2333
|
+
if (lower.includes(key)) return abbrev;
|
|
2334
|
+
}
|
|
2335
|
+
if (label.length > 8) return label.slice(0, 6) + "…";
|
|
2336
|
+
return label;
|
|
2337
|
+
}
|
|
2338
|
+
function generateCompactColumns(columns, options = {}) {
|
|
2339
|
+
const {
|
|
2340
|
+
maxColumns = 5,
|
|
2341
|
+
abbreviateHeaders = true,
|
|
2342
|
+
requiredFields = [],
|
|
2343
|
+
excludeFields = []
|
|
2344
|
+
} = options;
|
|
2345
|
+
const excludeSet = new Set(excludeFields);
|
|
2346
|
+
const requiredSet = new Set(requiredFields);
|
|
2347
|
+
const filteredColumns = columns.filter((col) => {
|
|
2348
|
+
if (excludeSet.has(col.field)) return false;
|
|
2349
|
+
return true;
|
|
2350
|
+
});
|
|
2351
|
+
const required = filteredColumns.filter((c) => requiredSet.has(c.field));
|
|
2352
|
+
const primary = filteredColumns.filter(
|
|
2353
|
+
(c) => !requiredSet.has(c.field) && normalizeImportance(c.importance) === "primary"
|
|
2354
|
+
);
|
|
2355
|
+
const secondary = filteredColumns.filter(
|
|
2356
|
+
(c) => !requiredSet.has(c.field) && normalizeImportance(c.importance) === "secondary"
|
|
2357
|
+
);
|
|
2358
|
+
const selectedColumns = [...required, ...primary, ...secondary].slice(
|
|
2359
|
+
0,
|
|
2360
|
+
maxColumns
|
|
2361
|
+
);
|
|
2362
|
+
return selectedColumns.map((col) => ({
|
|
2363
|
+
key: col.field,
|
|
2364
|
+
header: abbreviateHeaders ? abbreviateHeader(col.label) : col.label,
|
|
2365
|
+
sortable: col.sortable,
|
|
2366
|
+
type: col.type,
|
|
2367
|
+
format: col.format,
|
|
2368
|
+
// Compact widths
|
|
2369
|
+
width: getCompactWidth(col.type)
|
|
2370
|
+
}));
|
|
2371
|
+
}
|
|
2372
|
+
function getCompactWidth(type) {
|
|
2373
|
+
switch (type) {
|
|
2374
|
+
case "money":
|
|
2375
|
+
return "70px";
|
|
2376
|
+
case "number":
|
|
2377
|
+
case "percent":
|
|
2378
|
+
return "50px";
|
|
2379
|
+
case "status":
|
|
2380
|
+
case "badge":
|
|
2381
|
+
return "80px";
|
|
2382
|
+
case "date":
|
|
2383
|
+
return "70px";
|
|
2384
|
+
case "datetime":
|
|
2385
|
+
return "90px";
|
|
2386
|
+
case "boolean":
|
|
2387
|
+
return "50px";
|
|
2388
|
+
default:
|
|
2389
|
+
return "120px";
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
function createMobileCardRenderer(columns, mapping, entityType) {
|
|
2393
|
+
const { ListCard: ListCard2 } = require("../components/data/ListCard");
|
|
2394
|
+
return ({ data, onItemClick }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: data.map((item, index) => {
|
|
2395
|
+
const cardProps = entityToListCardProps(
|
|
2396
|
+
item,
|
|
2397
|
+
columns,
|
|
2398
|
+
mapping,
|
|
2399
|
+
entityType
|
|
2400
|
+
);
|
|
2401
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2402
|
+
ListCard2,
|
|
2403
|
+
{
|
|
2404
|
+
...cardProps,
|
|
2405
|
+
onClick: onItemClick ? () => onItemClick(item) : void 0
|
|
2406
|
+
},
|
|
2407
|
+
item.id ?? index
|
|
2408
|
+
);
|
|
2409
|
+
}) });
|
|
2410
|
+
}
|
|
2056
2411
|
let globalAuthService = null;
|
|
2057
2412
|
function setGlobalAuthService(authService) {
|
|
2058
2413
|
globalAuthService = authService;
|
|
2059
2414
|
}
|
|
2415
|
+
function getGlobalAuthService() {
|
|
2416
|
+
return globalAuthService;
|
|
2417
|
+
}
|
|
2060
2418
|
class ApiClient {
|
|
2061
2419
|
constructor(config = {}) {
|
|
2062
2420
|
__publicField(this, "instance");
|
|
2063
2421
|
this.instance = axios.create({
|
|
2064
|
-
baseURL: config.baseURL ||
|
|
2422
|
+
baseURL: config.baseURL || "",
|
|
2065
2423
|
timeout: config.timeout || 1e4,
|
|
2066
2424
|
headers: {
|
|
2067
2425
|
"Content-Type": "application/json",
|
|
@@ -2101,11 +2459,15 @@ class ApiClient {
|
|
|
2101
2459
|
this.instance.interceptors.response.use(
|
|
2102
2460
|
(response) => response,
|
|
2103
2461
|
async (error) => {
|
|
2104
|
-
var _a;
|
|
2105
|
-
|
|
2462
|
+
var _a, _b, _c, _d;
|
|
2463
|
+
const status = (_a = error.response) == null ? void 0 : _a.status;
|
|
2464
|
+
if (!error.response) {
|
|
2465
|
+
return Promise.reject(error);
|
|
2466
|
+
}
|
|
2467
|
+
if (status === 401 || status === 403) {
|
|
2106
2468
|
if (globalAuthService) {
|
|
2107
2469
|
const tokenData = globalAuthService.getTokenData();
|
|
2108
|
-
if ((tokenData == null ? void 0 : tokenData.refreshToken) && !error.config._retry) {
|
|
2470
|
+
if (status === 401 && (tokenData == null ? void 0 : tokenData.refreshToken) && !error.config._retry) {
|
|
2109
2471
|
error.config._retry = true;
|
|
2110
2472
|
try {
|
|
2111
2473
|
await globalAuthService.refreshToken();
|
|
@@ -2113,17 +2475,18 @@ class ApiClient {
|
|
|
2113
2475
|
error.config.headers.Authorization = `Bearer ${newTokenData == null ? void 0 : newTokenData.token}`;
|
|
2114
2476
|
return this.instance.request(error.config);
|
|
2115
2477
|
} catch {
|
|
2116
|
-
globalAuthService.clearAuth();
|
|
2117
|
-
window.location.reload();
|
|
2118
2478
|
}
|
|
2119
|
-
}
|
|
2120
|
-
|
|
2121
|
-
|
|
2479
|
+
}
|
|
2480
|
+
globalAuthService.clearAuth();
|
|
2481
|
+
const errorInfo = { status, message: (_c = (_b = error.response) == null ? void 0 : _b.data) == null ? void 0 : _c.detail };
|
|
2482
|
+
const handled = (_d = globalAuthService.onAuthError) == null ? void 0 : _d.call(globalAuthService, errorInfo);
|
|
2483
|
+
if (!handled) {
|
|
2484
|
+
window.location.href = "/login";
|
|
2122
2485
|
}
|
|
2123
2486
|
} else {
|
|
2124
2487
|
localStorage.removeItem("auth_token");
|
|
2125
2488
|
localStorage.removeItem("auth_user");
|
|
2126
|
-
window.location.
|
|
2489
|
+
window.location.href = "/login";
|
|
2127
2490
|
}
|
|
2128
2491
|
}
|
|
2129
2492
|
return Promise.reject(error);
|
|
@@ -2163,6 +2526,9 @@ class ApiClient {
|
|
|
2163
2526
|
return response.data;
|
|
2164
2527
|
}
|
|
2165
2528
|
}
|
|
2529
|
+
function createApiClient(config = {}) {
|
|
2530
|
+
return new ApiClient(config);
|
|
2531
|
+
}
|
|
2166
2532
|
const apiClient = new ApiClient();
|
|
2167
2533
|
function useToast() {
|
|
2168
2534
|
const [toasts, setToasts] = React.useState([]);
|
|
@@ -2433,7 +2799,7 @@ function useEntityData(entity, options = {}) {
|
|
|
2433
2799
|
const dataQuery = reactQuery.useQuery({
|
|
2434
2800
|
queryKey: [entity, "list", { limit, offset, filters }],
|
|
2435
2801
|
queryFn: () => apiClient.get(
|
|
2436
|
-
`/api/v1/${entity}
|
|
2802
|
+
`/api/v1/${entity}?${dataParams.toString()}`
|
|
2437
2803
|
),
|
|
2438
2804
|
enabled
|
|
2439
2805
|
});
|
|
@@ -2461,6 +2827,95 @@ function useEntityData(entity, options = {}) {
|
|
|
2461
2827
|
refetch
|
|
2462
2828
|
};
|
|
2463
2829
|
}
|
|
2830
|
+
function useEntityDetail(entity, id, options = {}) {
|
|
2831
|
+
var _a;
|
|
2832
|
+
const { view = "detail", sourceId, enabled = true } = options;
|
|
2833
|
+
const metadataParams = new URLSearchParams();
|
|
2834
|
+
metadataParams.set("view", view);
|
|
2835
|
+
if (sourceId) metadataParams.set("source_id", sourceId);
|
|
2836
|
+
const dataQuery = reactQuery.useQuery({
|
|
2837
|
+
queryKey: [entity, "detail", id],
|
|
2838
|
+
queryFn: () => apiClient.get(`/api/v1/${entity}/${id}`),
|
|
2839
|
+
enabled: enabled && !!id
|
|
2840
|
+
});
|
|
2841
|
+
const metadataQuery = reactQuery.useQuery({
|
|
2842
|
+
queryKey: [entity, "metadata", { view, sourceId }],
|
|
2843
|
+
queryFn: () => apiClient.get(
|
|
2844
|
+
`/api/v1/${entity}/fields/metadata?${metadataParams.toString()}`
|
|
2845
|
+
),
|
|
2846
|
+
enabled,
|
|
2847
|
+
staleTime: 5 * 60 * 1e3
|
|
2848
|
+
// 5 minutes
|
|
2849
|
+
});
|
|
2850
|
+
const refetch = () => {
|
|
2851
|
+
dataQuery.refetch();
|
|
2852
|
+
metadataQuery.refetch();
|
|
2853
|
+
};
|
|
2854
|
+
return {
|
|
2855
|
+
data: dataQuery.data ?? null,
|
|
2856
|
+
fields: ((_a = metadataQuery.data) == null ? void 0 : _a.columns) ?? [],
|
|
2857
|
+
isLoading: dataQuery.isLoading || metadataQuery.isLoading,
|
|
2858
|
+
isLoadingData: dataQuery.isLoading,
|
|
2859
|
+
isLoadingMetadata: metadataQuery.isLoading,
|
|
2860
|
+
error: dataQuery.error ?? metadataQuery.error ?? null,
|
|
2861
|
+
refetch
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2864
|
+
function useOnlineStatus(healthEndpoint) {
|
|
2865
|
+
const [isOnline, setIsOnline] = React.useState(
|
|
2866
|
+
typeof navigator !== "undefined" ? navigator.onLine : true
|
|
2867
|
+
);
|
|
2868
|
+
const [isBackendReachable, setIsBackendReachable] = React.useState(true);
|
|
2869
|
+
const [lastBackendContact, setLastBackendContact] = React.useState(
|
|
2870
|
+
null
|
|
2871
|
+
);
|
|
2872
|
+
React.useEffect(() => {
|
|
2873
|
+
const handleOnline = () => setIsOnline(true);
|
|
2874
|
+
const handleOffline = () => {
|
|
2875
|
+
setIsOnline(false);
|
|
2876
|
+
setIsBackendReachable(false);
|
|
2877
|
+
};
|
|
2878
|
+
window.addEventListener("online", handleOnline);
|
|
2879
|
+
window.addEventListener("offline", handleOffline);
|
|
2880
|
+
return () => {
|
|
2881
|
+
window.removeEventListener("online", handleOnline);
|
|
2882
|
+
window.removeEventListener("offline", handleOffline);
|
|
2883
|
+
};
|
|
2884
|
+
}, []);
|
|
2885
|
+
const checkBackend = React.useCallback(async () => {
|
|
2886
|
+
if (!isOnline) {
|
|
2887
|
+
setIsBackendReachable(false);
|
|
2888
|
+
return false;
|
|
2889
|
+
}
|
|
2890
|
+
try {
|
|
2891
|
+
const endpoint = healthEndpoint || "/api/v1/health";
|
|
2892
|
+
const response = await fetch(endpoint, {
|
|
2893
|
+
method: "GET",
|
|
2894
|
+
cache: "no-store"
|
|
2895
|
+
});
|
|
2896
|
+
const reachable = response.ok;
|
|
2897
|
+
setIsBackendReachable(reachable);
|
|
2898
|
+
if (reachable) {
|
|
2899
|
+
setLastBackendContact(Date.now());
|
|
2900
|
+
}
|
|
2901
|
+
return reachable;
|
|
2902
|
+
} catch {
|
|
2903
|
+
setIsBackendReachable(false);
|
|
2904
|
+
return false;
|
|
2905
|
+
}
|
|
2906
|
+
}, [isOnline, healthEndpoint]);
|
|
2907
|
+
React.useEffect(() => {
|
|
2908
|
+
if (isOnline) {
|
|
2909
|
+
checkBackend();
|
|
2910
|
+
}
|
|
2911
|
+
}, [isOnline, checkBackend]);
|
|
2912
|
+
return {
|
|
2913
|
+
isOnline,
|
|
2914
|
+
isBackendReachable,
|
|
2915
|
+
lastBackendContact,
|
|
2916
|
+
checkBackend
|
|
2917
|
+
};
|
|
2918
|
+
}
|
|
2464
2919
|
const buttonVariants = classVarianceAuthority.cva(
|
|
2465
2920
|
"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]",
|
|
2466
2921
|
{
|
|
@@ -4151,7 +4606,9 @@ const TableHead$1 = React__namespace.forwardRef(({ className, ...props }, ref) =
|
|
|
4151
4606
|
{
|
|
4152
4607
|
ref,
|
|
4153
4608
|
className: cn(
|
|
4154
|
-
"h-12 px-4 xs:px-2 2xs:px-1 text-left align-middle font-semibold text-gray-700 text-xs uppercase tracking-wider
|
|
4609
|
+
"h-12 px-4 xs:px-2 2xs:px-1 text-left align-middle font-semibold text-gray-700 text-xs uppercase tracking-wider",
|
|
4610
|
+
// Compact padding for checkbox columns
|
|
4611
|
+
"[&:has(input[type=checkbox])]:px-2 [&:has(input[type=checkbox])]:w-8",
|
|
4155
4612
|
className
|
|
4156
4613
|
),
|
|
4157
4614
|
...props
|
|
@@ -4163,7 +4620,9 @@ const TableCell$1 = React__namespace.forwardRef(({ className, ...props }, ref) =
|
|
|
4163
4620
|
{
|
|
4164
4621
|
ref,
|
|
4165
4622
|
className: cn(
|
|
4166
|
-
"p-4 xs:p-2 2xs:p-1 align-middle
|
|
4623
|
+
"p-4 xs:p-2 2xs:p-1 align-middle",
|
|
4624
|
+
// Compact padding for checkbox columns
|
|
4625
|
+
"[&:has(input[type=checkbox])]:px-2 [&:has(input[type=checkbox])]:w-8",
|
|
4167
4626
|
className
|
|
4168
4627
|
),
|
|
4169
4628
|
...props
|
|
@@ -4213,7 +4672,12 @@ function DataTable({
|
|
|
4213
4672
|
ui,
|
|
4214
4673
|
error = null,
|
|
4215
4674
|
errorTitle,
|
|
4216
|
-
errorRetry
|
|
4675
|
+
errorRetry,
|
|
4676
|
+
selectable = false,
|
|
4677
|
+
selectedIds,
|
|
4678
|
+
onSelectionChange,
|
|
4679
|
+
getRowId,
|
|
4680
|
+
selectAllMode = "page"
|
|
4217
4681
|
}) {
|
|
4218
4682
|
const [searchTerm, setSearchTerm] = React.useState("");
|
|
4219
4683
|
const [sortColumn, setSortColumn] = React.useState(null);
|
|
@@ -4276,6 +4740,85 @@ function DataTable({
|
|
|
4276
4740
|
return true;
|
|
4277
4741
|
});
|
|
4278
4742
|
}, [normalizedColumns, currentBreakpoint, responsive]);
|
|
4743
|
+
const defaultGetRowId = React.useCallback((row) => {
|
|
4744
|
+
if ("id" in row && typeof row.id === "string") return row.id;
|
|
4745
|
+
if ("id" in row && typeof row.id === "number") return String(row.id);
|
|
4746
|
+
throw new Error("DataTable: selectable requires getRowId prop or row.id field");
|
|
4747
|
+
}, []);
|
|
4748
|
+
const effectiveGetRowId = getRowId ?? defaultGetRowId;
|
|
4749
|
+
const selectableData = selectAllMode === "all" ? sortedData : paginatedData;
|
|
4750
|
+
const selectableIds = React.useMemo(
|
|
4751
|
+
() => {
|
|
4752
|
+
if (!selectable) return /* @__PURE__ */ new Set();
|
|
4753
|
+
return new Set(selectableData.map((row) => effectiveGetRowId(row)));
|
|
4754
|
+
},
|
|
4755
|
+
[selectable, selectableData, effectiveGetRowId]
|
|
4756
|
+
);
|
|
4757
|
+
const selectedInScope = React.useMemo(
|
|
4758
|
+
() => {
|
|
4759
|
+
if (!selectable || !selectedIds) return /* @__PURE__ */ new Set();
|
|
4760
|
+
return new Set([...selectedIds].filter((id) => selectableIds.has(id)));
|
|
4761
|
+
},
|
|
4762
|
+
[selectable, selectedIds, selectableIds]
|
|
4763
|
+
);
|
|
4764
|
+
const isAllSelected = selectableIds.size > 0 && selectedInScope.size === selectableIds.size;
|
|
4765
|
+
const isIndeterminate = selectedInScope.size > 0 && selectedInScope.size < selectableIds.size;
|
|
4766
|
+
const handleSelectAll = React.useCallback(() => {
|
|
4767
|
+
if (!onSelectionChange) return;
|
|
4768
|
+
if (isAllSelected) {
|
|
4769
|
+
const newSelection = new Set([...selectedIds ?? []].filter((id) => !selectableIds.has(id)));
|
|
4770
|
+
onSelectionChange(newSelection);
|
|
4771
|
+
} else {
|
|
4772
|
+
const newSelection = /* @__PURE__ */ new Set([...selectedIds ?? [], ...selectableIds]);
|
|
4773
|
+
onSelectionChange(newSelection);
|
|
4774
|
+
}
|
|
4775
|
+
}, [isAllSelected, selectedIds, selectableIds, onSelectionChange]);
|
|
4776
|
+
const handleSelectRow = React.useCallback((rowId) => {
|
|
4777
|
+
if (!onSelectionChange) return;
|
|
4778
|
+
const newSelection = new Set(selectedIds);
|
|
4779
|
+
if (newSelection.has(rowId)) {
|
|
4780
|
+
newSelection.delete(rowId);
|
|
4781
|
+
} else {
|
|
4782
|
+
newSelection.add(rowId);
|
|
4783
|
+
}
|
|
4784
|
+
onSelectionChange(newSelection);
|
|
4785
|
+
}, [selectedIds, onSelectionChange]);
|
|
4786
|
+
const selectionColumn = React.useMemo(() => {
|
|
4787
|
+
return {
|
|
4788
|
+
key: "__selection__",
|
|
4789
|
+
header: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4790
|
+
Checkbox$1,
|
|
4791
|
+
{
|
|
4792
|
+
checked: isAllSelected,
|
|
4793
|
+
indeterminate: isIndeterminate,
|
|
4794
|
+
onCheckedChange: handleSelectAll,
|
|
4795
|
+
"aria-label": isAllSelected ? "Deselect all rows" : "Select all rows",
|
|
4796
|
+
size: "sm"
|
|
4797
|
+
}
|
|
4798
|
+
),
|
|
4799
|
+
cell: (row) => {
|
|
4800
|
+
const rowId = effectiveGetRowId(row);
|
|
4801
|
+
const isSelected = (selectedIds == null ? void 0 : selectedIds.has(rowId)) ?? false;
|
|
4802
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4803
|
+
Checkbox$1,
|
|
4804
|
+
{
|
|
4805
|
+
checked: isSelected,
|
|
4806
|
+
onCheckedChange: () => handleSelectRow(rowId),
|
|
4807
|
+
"aria-label": isSelected ? `Deselect row ${rowId}` : `Select row ${rowId}`,
|
|
4808
|
+
size: "sm",
|
|
4809
|
+
onClick: (e) => e.stopPropagation()
|
|
4810
|
+
}
|
|
4811
|
+
);
|
|
4812
|
+
},
|
|
4813
|
+
width: "32px",
|
|
4814
|
+
sortable: false,
|
|
4815
|
+
filterable: false
|
|
4816
|
+
};
|
|
4817
|
+
}, [isAllSelected, isIndeterminate, selectedIds, effectiveGetRowId, handleSelectAll, handleSelectRow]);
|
|
4818
|
+
const effectiveColumns = React.useMemo(() => {
|
|
4819
|
+
if (!selectable) return visibleColumns;
|
|
4820
|
+
return [selectionColumn, ...visibleColumns];
|
|
4821
|
+
}, [selectable, selectionColumn, visibleColumns]);
|
|
4279
4822
|
const handleSort = (columnKey) => {
|
|
4280
4823
|
if (sortColumn === columnKey) {
|
|
4281
4824
|
if (sortDirection === "asc") {
|
|
@@ -4338,8 +4881,8 @@ function DataTable({
|
|
|
4338
4881
|
children: [
|
|
4339
4882
|
showSearch && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex-1 max-w-sm", children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton$1, { className: "h-10 w-full" }) }) }),
|
|
4340
4883
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded border overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs(Table$1, { children: [
|
|
4341
|
-
/* @__PURE__ */ jsxRuntime.jsx(TableHeader$1, { children: /* @__PURE__ */ jsxRuntime.jsx(TableRow$1, { children:
|
|
4342
|
-
/* @__PURE__ */ jsxRuntime.jsx(TableBody$1, { children: Array.from({ length: loadingItemCount }, (_, index) => /* @__PURE__ */ jsxRuntime.jsx(TableRow$1, { children:
|
|
4884
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableHeader$1, { children: /* @__PURE__ */ jsxRuntime.jsx(TableRow$1, { children: effectiveColumns.map((column) => /* @__PURE__ */ jsxRuntime.jsx(TableHead$1, { style: { width: column.width }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton$1, { className: "h-4 w-20" }) }) }, column.key)) }) }),
|
|
4885
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableBody$1, { children: Array.from({ length: loadingItemCount }, (_, index) => /* @__PURE__ */ jsxRuntime.jsx(TableRow$1, { children: effectiveColumns.map((column) => /* @__PURE__ */ jsxRuntime.jsx(TableCell$1, { children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton$1, { className: "h-4 w-full max-w-32" }) }, column.key)) }, index)) })
|
|
4343
4886
|
] }) }),
|
|
4344
4887
|
showPagination && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
4345
4888
|
/* @__PURE__ */ jsxRuntime.jsx(Skeleton$1, { className: "h-4 w-48" }),
|
|
@@ -4401,7 +4944,7 @@ function DataTable({
|
|
|
4401
4944
|
data: paginatedData,
|
|
4402
4945
|
onItemClick: onRowClick
|
|
4403
4946
|
}) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded border overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs(Table$1, { children: [
|
|
4404
|
-
/* @__PURE__ */ jsxRuntime.jsx(TableHeader$1, { children: /* @__PURE__ */ jsxRuntime.jsx(TableRow$1, { children:
|
|
4947
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableHeader$1, { children: /* @__PURE__ */ jsxRuntime.jsx(TableRow$1, { children: effectiveColumns.map((column) => {
|
|
4405
4948
|
const width = column.width;
|
|
4406
4949
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4407
4950
|
TableHead$1,
|
|
@@ -4424,7 +4967,7 @@ function DataTable({
|
|
|
4424
4967
|
/* @__PURE__ */ jsxRuntime.jsx(TableBody$1, { children: paginatedData.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(TableRow$1, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4425
4968
|
TableCell$1,
|
|
4426
4969
|
{
|
|
4427
|
-
colSpan:
|
|
4970
|
+
colSpan: effectiveColumns.length,
|
|
4428
4971
|
className: "text-center py-8 text-muted-foreground",
|
|
4429
4972
|
children: emptyMessage
|
|
4430
4973
|
}
|
|
@@ -4436,7 +4979,7 @@ function DataTable({
|
|
|
4436
4979
|
(hover || onRowClick) && "hover:bg-muted/50"
|
|
4437
4980
|
),
|
|
4438
4981
|
onClick: () => onRowClick == null ? void 0 : onRowClick(item),
|
|
4439
|
-
children:
|
|
4982
|
+
children: effectiveColumns.map((column) => /* @__PURE__ */ jsxRuntime.jsx(TableCell$1, { children: renderCell(column, item) }, column.key))
|
|
4440
4983
|
},
|
|
4441
4984
|
index
|
|
4442
4985
|
)) })
|
|
@@ -5629,6 +6172,75 @@ const ActivityFeed = ({
|
|
|
5629
6172
|
] }) })
|
|
5630
6173
|
] });
|
|
5631
6174
|
};
|
|
6175
|
+
const statusConfig = {
|
|
6176
|
+
online: {
|
|
6177
|
+
icon: lucideReact.Cloud,
|
|
6178
|
+
label: "Online",
|
|
6179
|
+
color: "text-green-600 dark:text-green-400",
|
|
6180
|
+
bgColor: "bg-green-100 dark:bg-green-900/30"
|
|
6181
|
+
},
|
|
6182
|
+
degraded: {
|
|
6183
|
+
icon: lucideReact.CloudOff,
|
|
6184
|
+
label: "Server Offline",
|
|
6185
|
+
color: "text-yellow-600 dark:text-yellow-400",
|
|
6186
|
+
bgColor: "bg-yellow-100 dark:bg-yellow-900/30"
|
|
6187
|
+
},
|
|
6188
|
+
offline: {
|
|
6189
|
+
icon: lucideReact.WifiOff,
|
|
6190
|
+
label: "Offline",
|
|
6191
|
+
color: "text-red-600 dark:text-red-400",
|
|
6192
|
+
bgColor: "bg-red-100 dark:bg-red-900/30"
|
|
6193
|
+
}
|
|
6194
|
+
};
|
|
6195
|
+
function ConnectionStatus({
|
|
6196
|
+
healthEndpoint,
|
|
6197
|
+
variant = "badge",
|
|
6198
|
+
className,
|
|
6199
|
+
showWhenOnline = false
|
|
6200
|
+
}) {
|
|
6201
|
+
const { isOnline, isBackendReachable } = useOnlineStatus(healthEndpoint);
|
|
6202
|
+
const status = !isOnline ? "offline" : !isBackendReachable ? "degraded" : "online";
|
|
6203
|
+
if (status === "online" && !showWhenOnline) {
|
|
6204
|
+
return null;
|
|
6205
|
+
}
|
|
6206
|
+
const config = statusConfig[status];
|
|
6207
|
+
const Icon2 = config.icon;
|
|
6208
|
+
if (variant === "badge") {
|
|
6209
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6210
|
+
"div",
|
|
6211
|
+
{
|
|
6212
|
+
className: cn(
|
|
6213
|
+
"inline-flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-medium",
|
|
6214
|
+
config.bgColor,
|
|
6215
|
+
config.color,
|
|
6216
|
+
className
|
|
6217
|
+
),
|
|
6218
|
+
children: [
|
|
6219
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon2, { className: "h-3 w-3" }),
|
|
6220
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: config.label })
|
|
6221
|
+
]
|
|
6222
|
+
}
|
|
6223
|
+
);
|
|
6224
|
+
}
|
|
6225
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6226
|
+
"div",
|
|
6227
|
+
{
|
|
6228
|
+
className: cn(
|
|
6229
|
+
"flex items-center gap-2 px-3 py-2 rounded-lg",
|
|
6230
|
+
config.bgColor,
|
|
6231
|
+
className
|
|
6232
|
+
),
|
|
6233
|
+
children: [
|
|
6234
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon2, { className: cn("h-4 w-4", config.color) }),
|
|
6235
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
|
|
6236
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-sm font-medium", config.color), children: config.label }),
|
|
6237
|
+
status === "degraded" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "Working with cached data" }),
|
|
6238
|
+
status === "offline" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "Check your connection" })
|
|
6239
|
+
] })
|
|
6240
|
+
]
|
|
6241
|
+
}
|
|
6242
|
+
);
|
|
6243
|
+
}
|
|
5632
6244
|
const FormField = ({
|
|
5633
6245
|
label,
|
|
5634
6246
|
description,
|
|
@@ -8970,7 +9582,10 @@ const AppHeader = ({ className }) => {
|
|
|
8970
9582
|
placeholder: "Search models, tests, jobs, and components..."
|
|
8971
9583
|
}
|
|
8972
9584
|
) }),
|
|
8973
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9585
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-4", children: [
|
|
9586
|
+
/* @__PURE__ */ jsxRuntime.jsx(ConnectionStatus, {}),
|
|
9587
|
+
/* @__PURE__ */ jsxRuntime.jsx(UserMenu, { category: 8 })
|
|
9588
|
+
] })
|
|
8974
9589
|
] }) })
|
|
8975
9590
|
}
|
|
8976
9591
|
);
|
|
@@ -9261,6 +9876,55 @@ const ListToolbar = ({
|
|
|
9261
9876
|
}
|
|
9262
9877
|
);
|
|
9263
9878
|
};
|
|
9879
|
+
const FieldGrid = ({
|
|
9880
|
+
data,
|
|
9881
|
+
fields,
|
|
9882
|
+
sections,
|
|
9883
|
+
columns = 2,
|
|
9884
|
+
showLabels = true,
|
|
9885
|
+
compact = false,
|
|
9886
|
+
showEmpty = true,
|
|
9887
|
+
className
|
|
9888
|
+
}) => {
|
|
9889
|
+
const gridCols = {
|
|
9890
|
+
1: "grid-cols-1",
|
|
9891
|
+
2: "grid-cols-1 md:grid-cols-2",
|
|
9892
|
+
3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
|
9893
|
+
};
|
|
9894
|
+
const renderValue = (field) => {
|
|
9895
|
+
const value = data[field.field];
|
|
9896
|
+
if (value === null || value === void 0 || value === "") {
|
|
9897
|
+
if (!showEmpty) return null;
|
|
9898
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "-" });
|
|
9899
|
+
}
|
|
9900
|
+
const format = field.format;
|
|
9901
|
+
return renderField(value, field.field, field.type, "lg", data, void 0, format);
|
|
9902
|
+
};
|
|
9903
|
+
const renderField_ = (field) => {
|
|
9904
|
+
const value = data[field.field];
|
|
9905
|
+
if (!showEmpty && (value === null || value === void 0 || value === "")) {
|
|
9906
|
+
return null;
|
|
9907
|
+
}
|
|
9908
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-1", compact ? "py-1" : "py-2"), children: [
|
|
9909
|
+
showLabels && /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-sm font-medium text-muted-foreground", children: field.label }),
|
|
9910
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { className: "text-sm text-foreground", children: renderValue(field) })
|
|
9911
|
+
] }, field.field);
|
|
9912
|
+
};
|
|
9913
|
+
const renderFields = (fieldList) => /* @__PURE__ */ jsxRuntime.jsx("dl", { className: cn("grid gap-x-6", gridCols[columns]), children: fieldList.map(renderField_) });
|
|
9914
|
+
if (sections && sections.length > 0) {
|
|
9915
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("space-y-6", className), children: sections.map((section) => /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: cn(compact ? "p-4" : "p-6"), children: [
|
|
9916
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4", children: [
|
|
9917
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-foreground", children: section.title }),
|
|
9918
|
+
section.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1", children: section.description })
|
|
9919
|
+
] }),
|
|
9920
|
+
renderFields(section.fields)
|
|
9921
|
+
] }, section.title)) });
|
|
9922
|
+
}
|
|
9923
|
+
if (fields && fields.length > 0) {
|
|
9924
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn(compact ? "p-4" : "p-6", className), children: renderFields(fields) });
|
|
9925
|
+
}
|
|
9926
|
+
return null;
|
|
9927
|
+
};
|
|
9264
9928
|
const Pagination = ({
|
|
9265
9929
|
currentPage,
|
|
9266
9930
|
totalPages,
|
|
@@ -9940,9 +10604,11 @@ function ProtectedRoute({
|
|
|
9940
10604
|
children,
|
|
9941
10605
|
requirePermission,
|
|
9942
10606
|
requireRole,
|
|
9943
|
-
fallback
|
|
10607
|
+
fallback,
|
|
10608
|
+
loginPath = "/login"
|
|
9944
10609
|
}) {
|
|
9945
10610
|
const { isAuthenticated, isLoading, hasPermission, hasRole } = useAuthContext();
|
|
10611
|
+
const location = reactRouterDom.useLocation();
|
|
9946
10612
|
if (isLoading) {
|
|
9947
10613
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
9948
10614
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto" }),
|
|
@@ -9950,7 +10616,7 @@ function ProtectedRoute({
|
|
|
9950
10616
|
] }) });
|
|
9951
10617
|
}
|
|
9952
10618
|
if (!isAuthenticated) {
|
|
9953
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10619
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: loginPath, state: { from: location }, replace: true });
|
|
9954
10620
|
}
|
|
9955
10621
|
if (requirePermission && !hasPermission(requirePermission)) {
|
|
9956
10622
|
return fallback || /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
@@ -9977,17 +10643,39 @@ function LogoutButton() {
|
|
|
9977
10643
|
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", size: "sm", onClick: logout, children: "Logout" })
|
|
9978
10644
|
] });
|
|
9979
10645
|
}
|
|
10646
|
+
const AUTH_TOKEN_KEY = "auth_token";
|
|
10647
|
+
const DEFAULT_ENDPOINTS = {
|
|
10648
|
+
login: "/auth/login",
|
|
10649
|
+
register: "/auth/register",
|
|
10650
|
+
refresh: "/auth/refresh",
|
|
10651
|
+
me: "/auth/me",
|
|
10652
|
+
logout: "/auth/logout"
|
|
10653
|
+
};
|
|
9980
10654
|
class AuthService {
|
|
9981
10655
|
constructor(config) {
|
|
9982
10656
|
__publicField(this, "config");
|
|
10657
|
+
__publicField(this, "apiClient");
|
|
9983
10658
|
__publicField(this, "refreshPromise", null);
|
|
10659
|
+
__publicField(this, "endpoints");
|
|
9984
10660
|
this.config = {
|
|
10661
|
+
mode: "pattern-stack",
|
|
9985
10662
|
tokenStorage: "localStorage",
|
|
9986
10663
|
tokenRefreshBuffer: 5,
|
|
9987
10664
|
// 5 minutes before expiry
|
|
9988
10665
|
autoRefresh: true,
|
|
9989
10666
|
...config
|
|
9990
10667
|
};
|
|
10668
|
+
this.endpoints = {
|
|
10669
|
+
...DEFAULT_ENDPOINTS,
|
|
10670
|
+
...config.endpoints
|
|
10671
|
+
};
|
|
10672
|
+
this.apiClient = createApiClient({ baseURL: this.config.apiUrl || "" });
|
|
10673
|
+
}
|
|
10674
|
+
/**
|
|
10675
|
+
* Get the auth mode
|
|
10676
|
+
*/
|
|
10677
|
+
get mode() {
|
|
10678
|
+
return this.config.mode || "pattern-stack";
|
|
9991
10679
|
}
|
|
9992
10680
|
getStorageKey(key) {
|
|
9993
10681
|
return `auth_${key}`;
|
|
@@ -10071,21 +10759,33 @@ class AuthService {
|
|
|
10071
10759
|
this.removeItem("user");
|
|
10072
10760
|
}
|
|
10073
10761
|
async login(credentials) {
|
|
10074
|
-
const response = await apiClient.post(`${this.config.apiUrl}${this.config.endpoints.login}`, credentials);
|
|
10075
10762
|
let token;
|
|
10076
10763
|
let refreshToken;
|
|
10077
10764
|
let expiresIn;
|
|
10078
|
-
|
|
10079
|
-
|
|
10080
|
-
|
|
10081
|
-
|
|
10765
|
+
let user;
|
|
10766
|
+
if (this.mode === "pattern-stack") {
|
|
10767
|
+
const response = await this.apiClient.post(
|
|
10768
|
+
this.endpoints.login,
|
|
10769
|
+
credentials
|
|
10770
|
+
);
|
|
10771
|
+
token = response.access_token;
|
|
10772
|
+
refreshToken = response.refresh_token;
|
|
10773
|
+
user = response.user;
|
|
10774
|
+
} else if (this.mode === "custom" && this.config.responseMapper) {
|
|
10775
|
+
const rawResponse = await this.apiClient.post(
|
|
10776
|
+
this.endpoints.login,
|
|
10777
|
+
credentials
|
|
10778
|
+
);
|
|
10779
|
+
const mapped = this.config.responseMapper(rawResponse);
|
|
10780
|
+
token = mapped.token;
|
|
10781
|
+
refreshToken = mapped.refreshToken;
|
|
10782
|
+
expiresIn = mapped.expiresIn;
|
|
10783
|
+
user = mapped.user;
|
|
10082
10784
|
} else {
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
|
|
10086
|
-
expiresIn = oldResponse.expiresIn;
|
|
10785
|
+
throw new Error(
|
|
10786
|
+
`Invalid auth mode: ${this.mode}. Use 'pattern-stack' or 'custom' with responseMapper.`
|
|
10787
|
+
);
|
|
10087
10788
|
}
|
|
10088
|
-
const user = response.user;
|
|
10089
10789
|
const expiresAt = expiresIn ? Date.now() + expiresIn * 1e3 : void 0;
|
|
10090
10790
|
this.setTokenData({ token, refreshToken, expiresAt });
|
|
10091
10791
|
this.setStoredUser(user);
|
|
@@ -10111,21 +10811,27 @@ class AuthService {
|
|
|
10111
10811
|
}
|
|
10112
10812
|
async performTokenRefresh(currentRefreshToken) {
|
|
10113
10813
|
try {
|
|
10114
|
-
const response = await apiClient.post(`${this.config.apiUrl}${this.config.endpoints.refresh}`, {
|
|
10115
|
-
refresh_token: currentRefreshToken
|
|
10116
|
-
});
|
|
10117
10814
|
let token;
|
|
10118
10815
|
let newRefreshToken;
|
|
10119
10816
|
let expiresIn;
|
|
10120
|
-
if ("
|
|
10121
|
-
const
|
|
10122
|
-
|
|
10817
|
+
if (this.mode === "pattern-stack") {
|
|
10818
|
+
const response = await this.apiClient.post(
|
|
10819
|
+
this.endpoints.refresh,
|
|
10820
|
+
{ refresh_token: currentRefreshToken }
|
|
10821
|
+
);
|
|
10822
|
+
token = response.access_token;
|
|
10123
10823
|
newRefreshToken = currentRefreshToken;
|
|
10824
|
+
} else if (this.mode === "custom" && this.config.responseMapper) {
|
|
10825
|
+
const rawResponse = await this.apiClient.post(
|
|
10826
|
+
this.endpoints.refresh,
|
|
10827
|
+
{ refresh_token: currentRefreshToken }
|
|
10828
|
+
);
|
|
10829
|
+
const mapped = this.config.responseMapper(rawResponse);
|
|
10830
|
+
token = mapped.token;
|
|
10831
|
+
newRefreshToken = mapped.refreshToken || currentRefreshToken;
|
|
10832
|
+
expiresIn = mapped.expiresIn;
|
|
10124
10833
|
} else {
|
|
10125
|
-
|
|
10126
|
-
token = oldResponse.token;
|
|
10127
|
-
newRefreshToken = oldResponse.refreshToken;
|
|
10128
|
-
expiresIn = oldResponse.expiresIn;
|
|
10834
|
+
throw new Error(`Invalid auth mode for token refresh: ${this.mode}`);
|
|
10129
10835
|
}
|
|
10130
10836
|
const expiresAt = expiresIn ? Date.now() + expiresIn * 1e3 : void 0;
|
|
10131
10837
|
this.setTokenData({
|
|
@@ -10167,36 +10873,39 @@ class AuthService {
|
|
|
10167
10873
|
const tokenData = this.getTokenData();
|
|
10168
10874
|
if (!(tokenData == null ? void 0 : tokenData.token)) return null;
|
|
10169
10875
|
try {
|
|
10170
|
-
return await apiClient.get(
|
|
10171
|
-
`${this.config.apiUrl}${this.config.endpoints.me}`
|
|
10172
|
-
);
|
|
10876
|
+
return await this.apiClient.get(this.endpoints.me);
|
|
10173
10877
|
} catch {
|
|
10174
10878
|
return null;
|
|
10175
10879
|
}
|
|
10176
10880
|
}
|
|
10177
10881
|
async logout() {
|
|
10178
10882
|
const tokenData = this.getTokenData();
|
|
10179
|
-
if (this.
|
|
10883
|
+
if (this.endpoints.logout && (tokenData == null ? void 0 : tokenData.token)) {
|
|
10180
10884
|
try {
|
|
10181
|
-
await apiClient.post(
|
|
10182
|
-
`${this.config.apiUrl}${this.config.endpoints.logout}`
|
|
10183
|
-
);
|
|
10885
|
+
await this.apiClient.post(this.endpoints.logout);
|
|
10184
10886
|
} catch {
|
|
10185
10887
|
}
|
|
10186
10888
|
}
|
|
10187
10889
|
this.clearAuth();
|
|
10188
10890
|
}
|
|
10891
|
+
/**
|
|
10892
|
+
* Get the configured onAuthError handler
|
|
10893
|
+
*/
|
|
10894
|
+
get onAuthError() {
|
|
10895
|
+
return this.config.onAuthError;
|
|
10896
|
+
}
|
|
10189
10897
|
}
|
|
10190
10898
|
function AuthProvider({
|
|
10191
10899
|
children,
|
|
10192
10900
|
config
|
|
10193
10901
|
}) {
|
|
10902
|
+
var _a;
|
|
10194
10903
|
const [user, setUser] = React.useState(null);
|
|
10195
10904
|
const [isLoading, setIsLoading] = React.useState(true);
|
|
10196
10905
|
const [permissions, setPermissions] = React.useState([]);
|
|
10197
10906
|
const authService = React.useMemo(() => {
|
|
10198
10907
|
const defaultConfig = {
|
|
10199
|
-
apiUrl:
|
|
10908
|
+
apiUrl: (config == null ? void 0 : config.apiUrl) ?? "",
|
|
10200
10909
|
endpoints: {
|
|
10201
10910
|
login: "/auth/login",
|
|
10202
10911
|
register: "/auth/register",
|
|
@@ -10211,7 +10920,7 @@ function AuthProvider({
|
|
|
10211
10920
|
enabled: false
|
|
10212
10921
|
}
|
|
10213
10922
|
};
|
|
10214
|
-
const mergedConfig = config ? { ...defaultConfig, ...config } : defaultConfig;
|
|
10923
|
+
const mergedConfig = config ? { ...defaultConfig, ...config, apiUrl: config.apiUrl ?? defaultConfig.apiUrl } : defaultConfig;
|
|
10215
10924
|
const service = new AuthService(mergedConfig);
|
|
10216
10925
|
setGlobalAuthService(service);
|
|
10217
10926
|
return service;
|
|
@@ -10229,10 +10938,12 @@ function AuthProvider({
|
|
|
10229
10938
|
try {
|
|
10230
10939
|
await authService.refreshToken();
|
|
10231
10940
|
setUser(storedUser);
|
|
10232
|
-
|
|
10233
|
-
|
|
10234
|
-
|
|
10235
|
-
|
|
10941
|
+
if (!(config == null ? void 0 : config.offlineFirst)) {
|
|
10942
|
+
try {
|
|
10943
|
+
const freshUser = await authService.getCurrentUser();
|
|
10944
|
+
if (freshUser) setUser(freshUser);
|
|
10945
|
+
} catch {
|
|
10946
|
+
}
|
|
10236
10947
|
}
|
|
10237
10948
|
} catch {
|
|
10238
10949
|
authService.clearAuth();
|
|
@@ -10242,10 +10953,12 @@ function AuthProvider({
|
|
|
10242
10953
|
}
|
|
10243
10954
|
} else {
|
|
10244
10955
|
setUser(storedUser);
|
|
10245
|
-
|
|
10246
|
-
|
|
10247
|
-
|
|
10248
|
-
|
|
10956
|
+
if (!(config == null ? void 0 : config.offlineFirst)) {
|
|
10957
|
+
try {
|
|
10958
|
+
const freshUser = await authService.getCurrentUser();
|
|
10959
|
+
if (freshUser) setUser(freshUser);
|
|
10960
|
+
} catch {
|
|
10961
|
+
}
|
|
10249
10962
|
}
|
|
10250
10963
|
}
|
|
10251
10964
|
}
|
|
@@ -10258,48 +10971,56 @@ function AuthProvider({
|
|
|
10258
10971
|
};
|
|
10259
10972
|
initAuth();
|
|
10260
10973
|
}, [authService]);
|
|
10261
|
-
const login = async (credentials) => {
|
|
10262
|
-
var _a;
|
|
10974
|
+
const login = React.useCallback(async (credentials) => {
|
|
10263
10975
|
setIsLoading(true);
|
|
10264
10976
|
try {
|
|
10265
|
-
const
|
|
10266
|
-
|
|
10267
|
-
|
|
10268
|
-
|
|
10269
|
-
|
|
10270
|
-
|
|
10271
|
-
|
|
10272
|
-
|
|
10273
|
-
|
|
10977
|
+
const loggedInUser = await authService.login(credentials);
|
|
10978
|
+
reactDom.flushSync(() => {
|
|
10979
|
+
var _a2;
|
|
10980
|
+
setUser(loggedInUser);
|
|
10981
|
+
if (((_a2 = config == null ? void 0 : config.permissions) == null ? void 0 : _a2.enabled) && "permissions" in loggedInUser) {
|
|
10982
|
+
const userPermissions = loggedInUser.permissions;
|
|
10983
|
+
setPermissions(
|
|
10984
|
+
Array.isArray(userPermissions) ? userPermissions : []
|
|
10985
|
+
);
|
|
10986
|
+
}
|
|
10987
|
+
setIsLoading(false);
|
|
10988
|
+
});
|
|
10989
|
+
} catch (error) {
|
|
10274
10990
|
setIsLoading(false);
|
|
10991
|
+
throw error;
|
|
10275
10992
|
}
|
|
10276
|
-
};
|
|
10277
|
-
const logout = async () => {
|
|
10993
|
+
}, [authService, (_a = config == null ? void 0 : config.permissions) == null ? void 0 : _a.enabled]);
|
|
10994
|
+
const logout = React.useCallback(async () => {
|
|
10278
10995
|
setIsLoading(true);
|
|
10279
10996
|
try {
|
|
10280
10997
|
await authService.logout();
|
|
10281
|
-
|
|
10282
|
-
|
|
10998
|
+
reactDom.flushSync(() => {
|
|
10999
|
+
setUser(null);
|
|
11000
|
+
setPermissions([]);
|
|
11001
|
+
setIsLoading(false);
|
|
11002
|
+
});
|
|
10283
11003
|
} catch (error) {
|
|
10284
11004
|
console.error("Logout error:", error);
|
|
10285
11005
|
authService.clearAuth();
|
|
10286
|
-
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
11006
|
+
reactDom.flushSync(() => {
|
|
11007
|
+
setUser(null);
|
|
11008
|
+
setPermissions([]);
|
|
11009
|
+
setIsLoading(false);
|
|
11010
|
+
});
|
|
10290
11011
|
}
|
|
10291
|
-
};
|
|
11012
|
+
}, [authService]);
|
|
10292
11013
|
const refreshToken = async () => {
|
|
10293
11014
|
await authService.refreshToken();
|
|
10294
11015
|
};
|
|
10295
11016
|
const hasPermission = (permission) => {
|
|
10296
|
-
var
|
|
10297
|
-
if (!((
|
|
11017
|
+
var _a2;
|
|
11018
|
+
if (!((_a2 = config == null ? void 0 : config.permissions) == null ? void 0 : _a2.enabled)) return true;
|
|
10298
11019
|
return permissions.includes(permission);
|
|
10299
11020
|
};
|
|
10300
11021
|
const hasRole = (role) => {
|
|
10301
|
-
var
|
|
10302
|
-
if (!((
|
|
11022
|
+
var _a2;
|
|
11023
|
+
if (!((_a2 = config == null ? void 0 : config.permissions) == null ? void 0 : _a2.enabled)) return true;
|
|
10303
11024
|
if (user && "role" in user) {
|
|
10304
11025
|
return user.role === role;
|
|
10305
11026
|
}
|
|
@@ -10508,6 +11229,32 @@ function MockAuthProvider({
|
|
|
10508
11229
|
};
|
|
10509
11230
|
return /* @__PURE__ */ jsxRuntime.jsx(AuthContext.Provider, { value, children });
|
|
10510
11231
|
}
|
|
11232
|
+
function NoAuthProvider({ children }) {
|
|
11233
|
+
const noOpLogin = async () => {
|
|
11234
|
+
console.warn("NoAuthProvider: login() called but auth is disabled");
|
|
11235
|
+
};
|
|
11236
|
+
const noOpLogout = async () => {
|
|
11237
|
+
console.warn("NoAuthProvider: logout() called but auth is disabled");
|
|
11238
|
+
};
|
|
11239
|
+
const noOpRefresh = async () => {
|
|
11240
|
+
console.warn("NoAuthProvider: refreshToken() called but auth is disabled");
|
|
11241
|
+
};
|
|
11242
|
+
const value = {
|
|
11243
|
+
user: null,
|
|
11244
|
+
isAuthenticated: true,
|
|
11245
|
+
// Always authenticated in no-auth mode
|
|
11246
|
+
isLoading: false,
|
|
11247
|
+
permissions: [],
|
|
11248
|
+
login: noOpLogin,
|
|
11249
|
+
logout: noOpLogout,
|
|
11250
|
+
refreshToken: noOpRefresh,
|
|
11251
|
+
hasPermission: () => true,
|
|
11252
|
+
// All permissions granted
|
|
11253
|
+
hasRole: () => true
|
|
11254
|
+
// All roles granted
|
|
11255
|
+
};
|
|
11256
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AuthContext.Provider, { value, children });
|
|
11257
|
+
}
|
|
10511
11258
|
const MockAuthContext = React.createContext(
|
|
10512
11259
|
void 0
|
|
10513
11260
|
);
|
|
@@ -13647,7 +14394,13 @@ function createReactApp(config) {
|
|
|
13647
14394
|
tree = /* @__PURE__ */ jsxRuntime.jsx(NavigationProvider, { initialNavigation: navigation, children: /* @__PURE__ */ jsxRuntime.jsx(SidebarProvider, { children: tree }) });
|
|
13648
14395
|
}
|
|
13649
14396
|
if (enableAuth) {
|
|
13650
|
-
|
|
14397
|
+
const authMode = (auth == null ? void 0 : auth.mode) || "pattern-stack";
|
|
14398
|
+
if (authMode === "none") {
|
|
14399
|
+
setGlobalAuthService(null);
|
|
14400
|
+
tree = /* @__PURE__ */ jsxRuntime.jsx(NoAuthProvider, { children: tree });
|
|
14401
|
+
} else {
|
|
14402
|
+
tree = /* @__PURE__ */ jsxRuntime.jsx(AuthProvider, { config: auth, children: tree });
|
|
14403
|
+
}
|
|
13651
14404
|
}
|
|
13652
14405
|
if (enableQuery) {
|
|
13653
14406
|
tree = /* @__PURE__ */ jsxRuntime.jsxs(reactQuery.QueryClientProvider, { client: queryClient, children: [
|
|
@@ -13665,7 +14418,8 @@ function createReactApp(config) {
|
|
|
13665
14418
|
routes.push({ path, component });
|
|
13666
14419
|
},
|
|
13667
14420
|
render() {
|
|
13668
|
-
const
|
|
14421
|
+
const authMode = (auth == null ? void 0 : auth.mode) || "pattern-stack";
|
|
14422
|
+
const shouldProtect = enableAuth && authMode !== "none" && (auth == null ? void 0 : auth.requireAuth) !== false;
|
|
13669
14423
|
const publicPaths = (auth == null ? void 0 : auth.publicPaths) ?? ["/login", "/register", "/forgot-password"];
|
|
13670
14424
|
const isPublicPath = (path) => {
|
|
13671
14425
|
return publicPaths.some((p) => path === p || path.startsWith(p + "/"));
|
|
@@ -16852,12 +17606,14 @@ function capitalize(str) {
|
|
|
16852
17606
|
exports.APIDataTemplate = APIDataTemplate;
|
|
16853
17607
|
exports.API_ENDPOINTS = API_ENDPOINTS;
|
|
16854
17608
|
exports.APP_NAME = APP_NAME;
|
|
17609
|
+
exports.AUTH_TOKEN_KEY = AUTH_TOKEN_KEY;
|
|
16855
17610
|
exports.Accordion = Accordion;
|
|
16856
17611
|
exports.ActivityFeed = ActivityFeed;
|
|
16857
17612
|
exports.AdminCRUDTemplate = AdminCRUDTemplate;
|
|
16858
17613
|
exports.AdminDashboardTemplate = AdminDashboardTemplate;
|
|
16859
17614
|
exports.AdminDetailTemplate = AdminDetailTemplate;
|
|
16860
17615
|
exports.Alert = Alert;
|
|
17616
|
+
exports.ApiClient = ApiClient;
|
|
16861
17617
|
exports.AppHeader = AppHeader;
|
|
16862
17618
|
exports.AppLayout = AppLayout;
|
|
16863
17619
|
exports.AuthDivider = AuthDivider;
|
|
@@ -16884,6 +17640,7 @@ exports.Checkbox = Checkbox;
|
|
|
16884
17640
|
exports.ColorSwatch = ColorSwatch;
|
|
16885
17641
|
exports.ComponentShowcasePage = ComponentShowcasePage;
|
|
16886
17642
|
exports.ComponentShowcaseTemplate = ComponentShowcaseTemplate;
|
|
17643
|
+
exports.ConnectionStatus = ConnectionStatus;
|
|
16887
17644
|
exports.DASHBOARD_CHART_HEIGHTS = DASHBOARD_CHART_HEIGHTS;
|
|
16888
17645
|
exports.DASHBOARD_CONTAINER_HEIGHTS = DASHBOARD_CONTAINER_HEIGHTS;
|
|
16889
17646
|
exports.DASHBOARD_HEIGHT_CLASSES = DASHBOARD_HEIGHT_CLASSES;
|
|
@@ -16924,6 +17681,7 @@ exports.EmptyState = EmptyState;
|
|
|
16924
17681
|
exports.EnhancedDataTemplate = EnhancedDataTemplate;
|
|
16925
17682
|
exports.EntityIcon = EntityIcon;
|
|
16926
17683
|
exports.ErrorBoundary = ErrorBoundary2;
|
|
17684
|
+
exports.FieldGrid = FieldGrid;
|
|
16927
17685
|
exports.FileUpload = FileUpload;
|
|
16928
17686
|
exports.FormField = FormField;
|
|
16929
17687
|
exports.FormGroup = FormGroup;
|
|
@@ -16943,6 +17701,7 @@ exports.MockAuthProvider = MockAuthProvider;
|
|
|
16943
17701
|
exports.Modal = Modal;
|
|
16944
17702
|
exports.NavMenu = NavMenu;
|
|
16945
17703
|
exports.NavigationProvider = NavigationProvider;
|
|
17704
|
+
exports.NoAuthProvider = NoAuthProvider;
|
|
16946
17705
|
exports.PageTemplate = PageTemplate;
|
|
16947
17706
|
exports.PageTitle = PageTitle;
|
|
16948
17707
|
exports.Pagination = Pagination;
|
|
@@ -17006,19 +17765,24 @@ exports.apiClient = apiClient;
|
|
|
17006
17765
|
exports.breakpoints = breakpoints;
|
|
17007
17766
|
exports.cn = cn;
|
|
17008
17767
|
exports.createAPIDataTemplate = createAPIDataTemplate;
|
|
17768
|
+
exports.createApiClient = createApiClient;
|
|
17769
|
+
exports.createMobileCardRenderer = createMobileCardRenderer;
|
|
17009
17770
|
exports.createReactApp = createReactApp;
|
|
17010
17771
|
exports.createSimpleApp = createSimpleApp;
|
|
17011
17772
|
exports.defaultFieldRenderers = defaultFieldRenderers;
|
|
17012
17773
|
exports.detectBulkOperationType = detectBulkOperationType;
|
|
17013
17774
|
exports.detectUIConfig = detectUIConfig;
|
|
17775
|
+
exports.entityToListCardProps = entityToListCardProps;
|
|
17014
17776
|
exports.env = env;
|
|
17015
17777
|
exports.formatNumberWithTooltip = formatNumberWithTooltip;
|
|
17016
17778
|
exports.generateBulkOperationName = generateBulkOperationName;
|
|
17779
|
+
exports.generateCompactColumns = generateCompactColumns;
|
|
17017
17780
|
exports.getAnimationClasses = getAnimationClasses;
|
|
17018
17781
|
exports.getChartHeight = getChartHeight;
|
|
17019
17782
|
exports.getContainerHeightClass = getContainerHeightClass;
|
|
17020
17783
|
exports.getCurrentBreakpoint = getCurrentBreakpoint;
|
|
17021
17784
|
exports.getFieldType = getFieldType;
|
|
17785
|
+
exports.getGlobalAuthService = getGlobalAuthService;
|
|
17022
17786
|
exports.getIcon = getIcon;
|
|
17023
17787
|
exports.getNavigationItems = getNavigationItems;
|
|
17024
17788
|
exports.getResponsiveClasses = getResponsiveClasses;
|
|
@@ -17042,6 +17806,7 @@ exports.useCategoryColors = useCategoryColors;
|
|
|
17042
17806
|
exports.useCreateExample = useCreateExample;
|
|
17043
17807
|
exports.useDeleteExample = useDeleteExample;
|
|
17044
17808
|
exports.useEntityData = useEntityData;
|
|
17809
|
+
exports.useEntityDetail = useEntityDetail;
|
|
17045
17810
|
exports.useErrorBoundary = useErrorBoundary;
|
|
17046
17811
|
exports.useFieldMetadata = useFieldMetadata;
|
|
17047
17812
|
exports.useGetExample = useGetExample;
|
|
@@ -17053,6 +17818,7 @@ exports.useMaxMediaQuery = useMaxMediaQuery;
|
|
|
17053
17818
|
exports.useMediaQuery = useMediaQuery;
|
|
17054
17819
|
exports.useMockAuth = useMockAuth;
|
|
17055
17820
|
exports.useNavigation = useNavigation;
|
|
17821
|
+
exports.useOnlineStatus = useOnlineStatus;
|
|
17056
17822
|
exports.useOverflowDetection = useOverflowDetection;
|
|
17057
17823
|
exports.usePermissions = usePermissions;
|
|
17058
17824
|
exports.useResponsiveClasses = useResponsiveClasses;
|