@pattern-stack/frontend-patterns 0.0.4 → 0.0.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/composed/SalesPanel/SalesPanel.d.ts +19 -0
- package/dist/atoms/composed/SalesPanel/SalesPanel.d.ts.map +1 -0
- package/dist/atoms/composed/SalesPanel/index.d.ts +2 -0
- package/dist/atoms/composed/SalesPanel/index.d.ts.map +1 -0
- package/dist/atoms/composed/SalesPanel/mockSalesData.d.ts +63 -0
- package/dist/atoms/composed/SalesPanel/mockSalesData.d.ts.map +1 -0
- package/dist/atoms/composed/index.d.ts +1 -0
- package/dist/atoms/composed/index.d.ts.map +1 -1
- package/dist/atoms/types/entity-config.d.ts +117 -0
- package/dist/atoms/types/entity-config.d.ts.map +1 -0
- package/dist/atoms/types/index.d.ts +2 -0
- package/dist/atoms/types/index.d.ts.map +1 -1
- package/dist/atoms/types/navigation.d.ts +30 -0
- package/dist/atoms/types/navigation.d.ts.map +1 -0
- package/dist/atoms/ui/ErrorBoundary.d.ts +1 -1
- package/dist/atoms/ui/button.d.ts +1 -1
- package/dist/atoms/utils/icon-resolver.d.ts +72 -0
- package/dist/atoms/utils/icon-resolver.d.ts.map +1 -0
- package/dist/atoms/utils/metric-engine.d.ts +30 -0
- package/dist/atoms/utils/metric-engine.d.ts.map +1 -0
- package/dist/atoms/utils/utils.d.ts +2 -0
- package/dist/atoms/utils/utils.d.ts.map +1 -1
- package/dist/features/auth/components/ProtectedRoute.d.ts +1 -1
- package/dist/frontend-patterns.css +1 -1
- package/dist/index.es.js +402 -14
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +402 -14
- package/dist/index.js.map +1 -1
- package/dist/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.d.ts +16 -0
- package/dist/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.d.ts.map +1 -0
- package/dist/molecules/layout/DashboardWithSidePanel/index.d.ts +2 -0
- package/dist/molecules/layout/DashboardWithSidePanel/index.d.ts.map +1 -0
- package/dist/molecules/layout/NavigationContext.d.ts +15 -0
- package/dist/molecules/layout/NavigationContext.d.ts.map +1 -0
- package/dist/molecules/layout/Sidebar.d.ts.map +1 -1
- package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts +2 -0
- package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts.map +1 -1
- package/dist/molecules/layout/index.d.ts +3 -0
- package/dist/molecules/layout/index.d.ts.map +1 -1
- package/dist/templates/factory.d.ts +2 -1
- package/dist/templates/factory.d.ts.map +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/package.json +7 -3
- package/src/App.tsx +11 -1
- package/src/__tests__/atoms/composed/databadge.test.tsx +106 -0
- package/src/__tests__/atoms/composed/statcard.test.tsx +133 -0
- package/src/__tests__/atoms/utils/icon-resolver.test.tsx +140 -0
- package/src/atoms/composed/SalesPanel/SalesPanel.tsx +116 -0
- package/src/atoms/composed/SalesPanel/index.ts +1 -0
- package/src/atoms/composed/SalesPanel/mockSalesData.ts +151 -0
- package/src/atoms/composed/index.ts +1 -0
- package/src/atoms/types/entity-config.ts +127 -0
- package/src/atoms/types/index.ts +3 -1
- package/src/atoms/types/navigation.ts +43 -0
- package/src/atoms/utils/icon-resolver.tsx +54 -0
- package/src/atoms/utils/metric-engine.ts +236 -0
- package/src/atoms/utils/utils.ts +4 -2
- package/src/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.tsx +42 -0
- package/src/molecules/layout/DashboardWithSidePanel/index.ts +1 -0
- package/src/molecules/layout/NavigationContext.tsx +63 -0
- package/src/molecules/layout/Sidebar.tsx +10 -23
- package/src/molecules/layout/SidebarButton/SidebarButton.tsx +32 -10
- package/src/molecules/layout/index.ts +4 -1
- package/src/organisms/entity/CategoryBreakdownPanel.tsx +427 -0
- package/src/organisms/entity/EntityListPanel.tsx +339 -0
- package/src/organisms/entity/MetricsOverviewPanel.tsx +236 -0
- package/src/organisms/entity/TrendAnalysisPanel.tsx +337 -0
- package/src/organisms/entity/index.ts +4 -0
- package/src/organisms/index.ts +5 -1
- package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +77 -75
- package/src/pages/AdminShowcase/SalesPerformanceDashboard.tsx +158 -0
- package/src/pages/AdminShowcase/index.tsx +2 -1
- package/src/pages/EntityShowcase/EntityManagementShowcase.tsx +137 -0
- package/src/pages/EntityShowcase/EntityPerformanceShowcase.tsx +117 -0
- package/src/pages/EntityShowcase/index.ts +2 -0
- package/src/pages/EntityTemplateExample.tsx +229 -0
- package/src/pages/TestEntityTemplate.tsx +40 -0
- package/src/pages/index.ts +2 -1
- package/src/templates/entity/EntityManagementTemplate.tsx +430 -0
- package/src/templates/entity/EntityPerformanceDashboardTemplate.tsx +277 -0
- package/src/templates/entity/configs/financial-config.ts +141 -0
- package/src/templates/entity/configs/index.ts +1 -0
- package/src/templates/entity/index.ts +3 -0
- package/src/templates/factory.tsx +14 -7
- package/src/templates/financial/FinancialDashboardTemplate.tsx +326 -0
- package/src/templates/index.ts +4 -0
package/dist/index.js
CHANGED
|
@@ -7,19 +7,18 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
7
7
|
const clsx = require("clsx");
|
|
8
8
|
const tailwindMerge = require("tailwind-merge");
|
|
9
9
|
const React = require("react");
|
|
10
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
11
|
+
const lucideReact = require("lucide-react");
|
|
10
12
|
const classVarianceAuthority = require("class-variance-authority");
|
|
11
13
|
const axios = require("axios");
|
|
12
14
|
const reactQuery = require("@tanstack/react-query");
|
|
13
|
-
const jsxRuntime = require("react/jsx-runtime");
|
|
14
15
|
const reactSlot = require("@radix-ui/react-slot");
|
|
15
16
|
const LabelPrimitive = require("@radix-ui/react-label");
|
|
16
17
|
const AvatarPrimitive = require("@radix-ui/react-avatar");
|
|
17
18
|
const DropdownMenuPrimitive = require("@radix-ui/react-dropdown-menu");
|
|
18
|
-
const lucideReact = require("lucide-react");
|
|
19
19
|
const reactDom = require("react-dom");
|
|
20
20
|
const reactRouterDom = require("react-router-dom");
|
|
21
21
|
const client = require("react-dom/client");
|
|
22
|
-
require("@tanstack/react-query-devtools");
|
|
23
22
|
function _interopNamespaceDefault(e) {
|
|
24
23
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
25
24
|
if (e) {
|
|
@@ -265,6 +264,240 @@ const tooltipContent = {
|
|
|
265
264
|
profile: "Profile",
|
|
266
265
|
account: "Account settings"
|
|
267
266
|
};
|
|
267
|
+
class MetricCalculationEngine {
|
|
268
|
+
static calculateMetric(config, data, previousData) {
|
|
269
|
+
const currentValue = this.aggregateValue(config, data);
|
|
270
|
+
const previousValue = previousData ? this.aggregateValue(config, previousData) : void 0;
|
|
271
|
+
const trend = this.calculateTrend(currentValue, previousValue);
|
|
272
|
+
const formattedValue = this.formatValue(currentValue, config.format, config.type);
|
|
273
|
+
const target = typeof config.target === "function" ? config.target(data) : config.target;
|
|
274
|
+
return {
|
|
275
|
+
current: currentValue,
|
|
276
|
+
previous: previousValue,
|
|
277
|
+
trend,
|
|
278
|
+
target,
|
|
279
|
+
formattedValue
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
static aggregateValue(config, data) {
|
|
283
|
+
if (!data.length) return 0;
|
|
284
|
+
const values = data.map((item) => {
|
|
285
|
+
const value = this.extractValue(item, config.key);
|
|
286
|
+
return typeof value === "number" ? value : 0;
|
|
287
|
+
}).filter((value) => !isNaN(value));
|
|
288
|
+
if (!values.length) return 0;
|
|
289
|
+
switch (config.aggregation || "sum") {
|
|
290
|
+
case "sum":
|
|
291
|
+
return values.reduce((sum, value) => sum + value, 0);
|
|
292
|
+
case "avg":
|
|
293
|
+
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
294
|
+
case "count":
|
|
295
|
+
return values.length;
|
|
296
|
+
case "min":
|
|
297
|
+
return Math.min(...values);
|
|
298
|
+
case "max":
|
|
299
|
+
return Math.max(...values);
|
|
300
|
+
default:
|
|
301
|
+
return values.reduce((sum, value) => sum + value, 0);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
static extractValue(item, key) {
|
|
305
|
+
return key.split(".").reduce((obj, k) => obj == null ? void 0 : obj[k], item);
|
|
306
|
+
}
|
|
307
|
+
static calculateTrend(current, previous) {
|
|
308
|
+
if (previous === void 0 || previous === 0) return "neutral";
|
|
309
|
+
const change = (current - previous) / Math.abs(previous) * 100;
|
|
310
|
+
if (change > 1) return "up";
|
|
311
|
+
if (change < -1) return "down";
|
|
312
|
+
return "neutral";
|
|
313
|
+
}
|
|
314
|
+
static formatValue(value, format, type) {
|
|
315
|
+
let formatted = value;
|
|
316
|
+
if ((format == null ? void 0 : format.decimals) !== void 0) {
|
|
317
|
+
formatted = Number(value.toFixed(format.decimals));
|
|
318
|
+
}
|
|
319
|
+
let result = formatted.toString();
|
|
320
|
+
if ((format == null ? void 0 : format.thousands) !== false && Math.abs(formatted) >= 1e3) {
|
|
321
|
+
result = formatted.toLocaleString();
|
|
322
|
+
}
|
|
323
|
+
switch (type) {
|
|
324
|
+
case "currency":
|
|
325
|
+
result = new Intl.NumberFormat("en-US", {
|
|
326
|
+
style: "currency",
|
|
327
|
+
currency: "USD",
|
|
328
|
+
minimumFractionDigits: (format == null ? void 0 : format.decimals) ?? 2,
|
|
329
|
+
maximumFractionDigits: (format == null ? void 0 : format.decimals) ?? 2
|
|
330
|
+
}).format(formatted);
|
|
331
|
+
break;
|
|
332
|
+
case "percentage":
|
|
333
|
+
result = `${formatted.toFixed((format == null ? void 0 : format.decimals) ?? 1)}%`;
|
|
334
|
+
break;
|
|
335
|
+
case "duration":
|
|
336
|
+
result = this.formatDuration(formatted);
|
|
337
|
+
break;
|
|
338
|
+
case "ratio":
|
|
339
|
+
result = `${formatted.toFixed((format == null ? void 0 : format.decimals) ?? 2)}:1`;
|
|
340
|
+
break;
|
|
341
|
+
default:
|
|
342
|
+
if (format == null ? void 0 : format.prefix) result = format.prefix + result;
|
|
343
|
+
if (format == null ? void 0 : format.suffix) result = result + format.suffix;
|
|
344
|
+
}
|
|
345
|
+
return result;
|
|
346
|
+
}
|
|
347
|
+
static formatDuration(minutes) {
|
|
348
|
+
const hours = Math.floor(minutes / 60);
|
|
349
|
+
const mins = Math.floor(minutes % 60);
|
|
350
|
+
if (hours > 0) {
|
|
351
|
+
return `${hours}h ${mins}m`;
|
|
352
|
+
}
|
|
353
|
+
return `${mins}m`;
|
|
354
|
+
}
|
|
355
|
+
static calculateTrendData(config, data, dateField = "date", periods = 12) {
|
|
356
|
+
const groupedData = this.groupByPeriod(data, dateField);
|
|
357
|
+
const sortedDates = Object.keys(groupedData).sort();
|
|
358
|
+
return sortedDates.slice(-periods).map((date) => ({
|
|
359
|
+
date,
|
|
360
|
+
value: this.aggregateValue(config, groupedData[date]),
|
|
361
|
+
label: this.formatDateLabel(date)
|
|
362
|
+
}));
|
|
363
|
+
}
|
|
364
|
+
static groupByPeriod(data, dateField) {
|
|
365
|
+
return data.reduce((groups, item) => {
|
|
366
|
+
const date = this.extractValue(item, dateField);
|
|
367
|
+
if (!date) return groups;
|
|
368
|
+
const period = new Date(date).toISOString().split("T")[0];
|
|
369
|
+
if (!groups[period]) groups[period] = [];
|
|
370
|
+
groups[period].push(item);
|
|
371
|
+
return groups;
|
|
372
|
+
}, {});
|
|
373
|
+
}
|
|
374
|
+
static formatDateLabel(dateString) {
|
|
375
|
+
const date = new Date(dateString);
|
|
376
|
+
return date.toLocaleDateString("en-US", {
|
|
377
|
+
month: "short",
|
|
378
|
+
day: "numeric"
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
static calculateCategoryBreakdown(data, categoryField, valueField, maxCategories = 8) {
|
|
382
|
+
const groups = data.reduce((acc, item) => {
|
|
383
|
+
const category = String(this.extractValue(item, categoryField) || "Unknown");
|
|
384
|
+
const value = this.extractValue(item, valueField) || 0;
|
|
385
|
+
if (!acc[category]) acc[category] = 0;
|
|
386
|
+
acc[category] += typeof value === "number" ? value : 0;
|
|
387
|
+
return acc;
|
|
388
|
+
}, {});
|
|
389
|
+
const total = Object.values(groups).reduce((sum, value) => sum + value, 0);
|
|
390
|
+
let categories = Object.entries(groups).map(([category, value]) => ({
|
|
391
|
+
category,
|
|
392
|
+
value,
|
|
393
|
+
percentage: total > 0 ? value / total * 100 : 0
|
|
394
|
+
})).sort((a, b) => b.value - a.value);
|
|
395
|
+
if (categories.length > maxCategories) {
|
|
396
|
+
const topCategories = categories.slice(0, maxCategories - 1);
|
|
397
|
+
const otherValue = categories.slice(maxCategories - 1).reduce((sum, cat) => sum + cat.value, 0);
|
|
398
|
+
topCategories.push({
|
|
399
|
+
category: "Other",
|
|
400
|
+
value: otherValue,
|
|
401
|
+
percentage: total > 0 ? otherValue / total * 100 : 0
|
|
402
|
+
});
|
|
403
|
+
categories = topCategories;
|
|
404
|
+
}
|
|
405
|
+
return categories;
|
|
406
|
+
}
|
|
407
|
+
static detectInsights(metrics, thresholds = {}) {
|
|
408
|
+
const insights = [];
|
|
409
|
+
for (const [key, metric] of Object.entries(metrics)) {
|
|
410
|
+
const threshold = thresholds[key];
|
|
411
|
+
if (metric.target && metric.current < metric.target * 0.8) {
|
|
412
|
+
insights.push({
|
|
413
|
+
type: "negative",
|
|
414
|
+
message: `${key} is significantly below target`,
|
|
415
|
+
value: metric.current
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
if (metric.trend === "up" && metric.previous && metric.current > metric.previous * 1.2) {
|
|
419
|
+
insights.push({
|
|
420
|
+
type: "positive",
|
|
421
|
+
message: `${key} showing strong growth`,
|
|
422
|
+
value: metric.current
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
if (threshold && metric.current >= threshold.critical) {
|
|
426
|
+
insights.push({
|
|
427
|
+
type: "negative",
|
|
428
|
+
message: `${key} has reached critical threshold`,
|
|
429
|
+
value: metric.current
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return insights.slice(0, 5);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const iconMap = {
|
|
437
|
+
Palette: lucideReact.Palette,
|
|
438
|
+
Menu: lucideReact.Menu,
|
|
439
|
+
X: lucideReact.X,
|
|
440
|
+
Shield: lucideReact.Shield,
|
|
441
|
+
Users: lucideReact.Users,
|
|
442
|
+
BarChart3: lucideReact.BarChart3,
|
|
443
|
+
Database: lucideReact.Database,
|
|
444
|
+
TrendingUp: lucideReact.TrendingUp,
|
|
445
|
+
Layout: lucideReact.Layout,
|
|
446
|
+
Home: lucideReact.Home,
|
|
447
|
+
Settings: lucideReact.Settings,
|
|
448
|
+
Bell: lucideReact.Bell,
|
|
449
|
+
Search: lucideReact.Search,
|
|
450
|
+
Plus: lucideReact.Plus,
|
|
451
|
+
Edit: lucideReact.Edit,
|
|
452
|
+
Trash2: lucideReact.Trash2,
|
|
453
|
+
Eye: lucideReact.Eye,
|
|
454
|
+
Download: lucideReact.Download,
|
|
455
|
+
Upload: lucideReact.Upload,
|
|
456
|
+
Share: lucideReact.Share,
|
|
457
|
+
Lock: lucideReact.Lock,
|
|
458
|
+
Unlock: lucideReact.Unlock,
|
|
459
|
+
Mail: lucideReact.Mail,
|
|
460
|
+
Phone: lucideReact.Phone,
|
|
461
|
+
Calendar: lucideReact.Calendar,
|
|
462
|
+
Clock: lucideReact.Clock,
|
|
463
|
+
MapPin: lucideReact.MapPin,
|
|
464
|
+
Star: lucideReact.Star,
|
|
465
|
+
Heart: lucideReact.Heart,
|
|
466
|
+
Bookmark: lucideReact.Bookmark,
|
|
467
|
+
Tag: lucideReact.Tag,
|
|
468
|
+
Flag: lucideReact.Flag,
|
|
469
|
+
File: lucideReact.File,
|
|
470
|
+
Folder: lucideReact.Folder,
|
|
471
|
+
Image: lucideReact.Image,
|
|
472
|
+
Video: lucideReact.Video,
|
|
473
|
+
Music: lucideReact.Music,
|
|
474
|
+
ChevronRight: lucideReact.ChevronRight,
|
|
475
|
+
ChevronDown: lucideReact.ChevronDown,
|
|
476
|
+
ChevronLeft: lucideReact.ChevronLeft,
|
|
477
|
+
ChevronUp: lucideReact.ChevronUp,
|
|
478
|
+
ArrowRight: lucideReact.ArrowRight,
|
|
479
|
+
ArrowLeft: lucideReact.ArrowLeft,
|
|
480
|
+
ArrowUp: lucideReact.ArrowUp,
|
|
481
|
+
ArrowDown: lucideReact.ArrowDown,
|
|
482
|
+
Check: lucideReact.Check,
|
|
483
|
+
AlertCircle: lucideReact.AlertCircle,
|
|
484
|
+
Info: lucideReact.Info,
|
|
485
|
+
HelpCircle: lucideReact.HelpCircle
|
|
486
|
+
};
|
|
487
|
+
const Icon = ({ name, className = "w-5 h-5", size, ...props }) => {
|
|
488
|
+
const IconComponent = iconMap[name];
|
|
489
|
+
if (!IconComponent) {
|
|
490
|
+
console.warn(`Icon "${name}" not found. Using default Menu icon.`);
|
|
491
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className, size, ...props });
|
|
492
|
+
}
|
|
493
|
+
return /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className, size, ...props });
|
|
494
|
+
};
|
|
495
|
+
const getIcon = (name) => {
|
|
496
|
+
return iconMap[name] || lucideReact.Menu;
|
|
497
|
+
};
|
|
498
|
+
const isValidIcon = (name) => {
|
|
499
|
+
return name in iconMap;
|
|
500
|
+
};
|
|
268
501
|
function cn(...inputs) {
|
|
269
502
|
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
270
503
|
}
|
|
@@ -1286,6 +1519,83 @@ const DataBadge = ({
|
|
|
1286
1519
|
}
|
|
1287
1520
|
return badge;
|
|
1288
1521
|
};
|
|
1522
|
+
const getStageStatus = (stage) => {
|
|
1523
|
+
switch (stage) {
|
|
1524
|
+
case "Closed Won":
|
|
1525
|
+
return "success";
|
|
1526
|
+
case "Negotiation":
|
|
1527
|
+
return "warning";
|
|
1528
|
+
case "Proposal Sent":
|
|
1529
|
+
return "info";
|
|
1530
|
+
case "Qualified":
|
|
1531
|
+
return "info";
|
|
1532
|
+
case "Discovery":
|
|
1533
|
+
return "neutral";
|
|
1534
|
+
default:
|
|
1535
|
+
return "neutral";
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1538
|
+
const SalesPanel = ({
|
|
1539
|
+
sales,
|
|
1540
|
+
onSaleClick,
|
|
1541
|
+
onClose
|
|
1542
|
+
}) => {
|
|
1543
|
+
const topDeals = sales.slice(0, 5);
|
|
1544
|
+
const totalValue = sales.reduce((sum, sale) => sum + sale.amount, 0);
|
|
1545
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed right-0 top-16 h-[calc(100vh-4rem)] w-72 bg-background border-l border-border shadow-lg flex flex-col z-30", children: [
|
|
1546
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 border-b border-border", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1547
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "font-medium flex items-center gap-2", children: [
|
|
1548
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Target, { className: "w-4 h-4" }),
|
|
1549
|
+
"Pipeline"
|
|
1550
|
+
] }),
|
|
1551
|
+
onClose && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, children: "×" })
|
|
1552
|
+
] }) }),
|
|
1553
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 border-b border-border", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1554
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
|
|
1555
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: "Total Value" }),
|
|
1556
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-green-600", children: [
|
|
1557
|
+
"$",
|
|
1558
|
+
totalValue.toLocaleString()
|
|
1559
|
+
] })
|
|
1560
|
+
] }),
|
|
1561
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
|
|
1562
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: "Active Deals" }),
|
|
1563
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: sales.length })
|
|
1564
|
+
] })
|
|
1565
|
+
] }) }),
|
|
1566
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-auto p-3", children: [
|
|
1567
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-medium mb-3", children: "Recent Opportunities" }),
|
|
1568
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: topDeals.map((sale) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1569
|
+
"div",
|
|
1570
|
+
{
|
|
1571
|
+
className: "p-2 border border-border rounded-md hover:bg-muted/50 cursor-pointer transition-colors",
|
|
1572
|
+
onClick: () => onSaleClick == null ? void 0 : onSaleClick(sale),
|
|
1573
|
+
children: [
|
|
1574
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-start mb-1", children: [
|
|
1575
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1576
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium truncate", children: sale.customer }),
|
|
1577
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground truncate", children: sale.product })
|
|
1578
|
+
] }),
|
|
1579
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right ml-2", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm font-semibold text-green-600", children: [
|
|
1580
|
+
"$",
|
|
1581
|
+
sale.amount.toLocaleString()
|
|
1582
|
+
] }) })
|
|
1583
|
+
] }),
|
|
1584
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
1585
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataBadge, { variant: "status", status: getStageStatus(sale.stage), children: sale.stage }),
|
|
1586
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: sale.salesperson })
|
|
1587
|
+
] })
|
|
1588
|
+
]
|
|
1589
|
+
},
|
|
1590
|
+
sale.id
|
|
1591
|
+
)) })
|
|
1592
|
+
] }),
|
|
1593
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 border-t border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", size: "sm", className: "w-full", children: [
|
|
1594
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "w-4 h-4 mr-2" }),
|
|
1595
|
+
"View All Deals"
|
|
1596
|
+
] }) })
|
|
1597
|
+
] });
|
|
1598
|
+
};
|
|
1289
1599
|
const StatCard = ({
|
|
1290
1600
|
title,
|
|
1291
1601
|
value,
|
|
@@ -5550,6 +5860,42 @@ const useSidebar = () => {
|
|
|
5550
5860
|
}
|
|
5551
5861
|
return context;
|
|
5552
5862
|
};
|
|
5863
|
+
const NavigationContext = React.createContext(void 0);
|
|
5864
|
+
const defaultShowcaseNavigation = [
|
|
5865
|
+
{ value: "showcase", label: "Showcase", icon: "Palette", path: "/showcase", category: 5 },
|
|
5866
|
+
{ value: "admin-dashboard", label: "Admin Dashboard", icon: "Shield", path: "/admin/dashboard", category: 2 },
|
|
5867
|
+
{ value: "admin-users", label: "User Management", icon: "Users", path: "/admin/users", category: 3 },
|
|
5868
|
+
{ value: "admin-sales", label: "Sales Dashboard", icon: "TrendingUp", path: "/admin/sales", category: 4 },
|
|
5869
|
+
{ value: "entity-performance", label: "Performance Dashboard", icon: "BarChart3", path: "/entity/performance", category: 6 },
|
|
5870
|
+
{ value: "entity-management", label: "Entity Management", icon: "Database", path: "/entity/management", category: 7 },
|
|
5871
|
+
{ value: "entity-template", label: "Template Example", icon: "Layout", path: "/entity/template-example", category: 1 }
|
|
5872
|
+
];
|
|
5873
|
+
const NavigationProvider = ({ children, initialNavigation }) => {
|
|
5874
|
+
const [navigation, setNavigation] = React.useState(
|
|
5875
|
+
initialNavigation || {
|
|
5876
|
+
items: defaultShowcaseNavigation,
|
|
5877
|
+
showDefaultNavigation: true,
|
|
5878
|
+
defaultExpanded: true
|
|
5879
|
+
}
|
|
5880
|
+
);
|
|
5881
|
+
return /* @__PURE__ */ jsxRuntime.jsx(NavigationContext.Provider, { value: { navigation, setNavigation }, children });
|
|
5882
|
+
};
|
|
5883
|
+
const useNavigation = () => {
|
|
5884
|
+
const context = React.useContext(NavigationContext);
|
|
5885
|
+
if (context === void 0) {
|
|
5886
|
+
throw new Error("useNavigation must be used within a NavigationProvider");
|
|
5887
|
+
}
|
|
5888
|
+
return context;
|
|
5889
|
+
};
|
|
5890
|
+
const getNavigationItems = (config) => {
|
|
5891
|
+
if (config.items.length > 0) {
|
|
5892
|
+
return config.items;
|
|
5893
|
+
}
|
|
5894
|
+
if (config.showDefaultNavigation !== false) {
|
|
5895
|
+
return defaultShowcaseNavigation;
|
|
5896
|
+
}
|
|
5897
|
+
return [];
|
|
5898
|
+
};
|
|
5553
5899
|
const SidebarButton = ({
|
|
5554
5900
|
icon,
|
|
5555
5901
|
label,
|
|
@@ -5557,13 +5903,16 @@ const SidebarButton = ({
|
|
|
5557
5903
|
category = 1,
|
|
5558
5904
|
expanded = false,
|
|
5559
5905
|
onClick,
|
|
5560
|
-
className
|
|
5906
|
+
className,
|
|
5907
|
+
badge,
|
|
5908
|
+
disabled = false
|
|
5561
5909
|
}) => {
|
|
5562
5910
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5563
5911
|
Button,
|
|
5564
5912
|
{
|
|
5565
5913
|
variant: active ? "secondary" : "ghost",
|
|
5566
5914
|
onClick,
|
|
5915
|
+
disabled,
|
|
5567
5916
|
tooltip: !expanded ? label : void 0,
|
|
5568
5917
|
className: cn(
|
|
5569
5918
|
"relative w-full justify-start gap-3 h-12",
|
|
@@ -5597,12 +5946,19 @@ const SidebarButton = ({
|
|
|
5597
5946
|
"text-sm font-medium flex-1 text-left",
|
|
5598
5947
|
active ? `text-category-${category}` : "text-foreground"
|
|
5599
5948
|
), children: label }),
|
|
5600
|
-
|
|
5949
|
+
badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
|
|
5950
|
+
"px-2 py-0.5 text-xs font-medium rounded-full flex-shrink-0",
|
|
5951
|
+
"bg-primary/10 text-primary"
|
|
5952
|
+
), children: badge }) : active && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(
|
|
5601
5953
|
"w-2 h-2 rounded-full flex-shrink-0",
|
|
5602
5954
|
`bg-category-${category}`
|
|
5603
5955
|
) })
|
|
5604
5956
|
] }),
|
|
5605
|
-
!expanded && active && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-1 -right-1", children: /* @__PURE__ */ jsxRuntime.jsx("
|
|
5957
|
+
!expanded && (badge || active) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-1 -right-1", children: badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
|
|
5958
|
+
"px-1.5 py-0.5 text-xs font-bold rounded-full",
|
|
5959
|
+
"bg-primary text-primary-foreground",
|
|
5960
|
+
"ring-2 ring-background"
|
|
5961
|
+
), children: badge }) : active && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(
|
|
5606
5962
|
"w-2.5 h-2.5 rounded-full",
|
|
5607
5963
|
`bg-category-${category}`,
|
|
5608
5964
|
"ring-2 ring-background"
|
|
@@ -5613,14 +5969,11 @@ const SidebarButton = ({
|
|
|
5613
5969
|
};
|
|
5614
5970
|
const Sidebar = ({ className }) => {
|
|
5615
5971
|
const { isExpanded, toggleSidebar } = useSidebar();
|
|
5972
|
+
const { navigation } = useNavigation();
|
|
5616
5973
|
const location = reactRouterDom.useLocation();
|
|
5617
5974
|
const navigate = reactRouterDom.useNavigate();
|
|
5618
5975
|
const [searchParams] = reactRouterDom.useSearchParams();
|
|
5619
|
-
const items =
|
|
5620
|
-
{ value: "showcase", label: "Showcase", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Palette, { className: "w-5 h-5" }), path: "/showcase", category: 5 },
|
|
5621
|
-
{ value: "admin-dashboard", label: "Admin Dashboard", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Shield, { className: "w-5 h-5" }), path: "/admin/dashboard", category: 2 },
|
|
5622
|
-
{ value: "admin-users", label: "User Management", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Users, { className: "w-5 h-5" }), path: "/admin/users", category: 3 }
|
|
5623
|
-
];
|
|
5976
|
+
const items = getNavigationItems(navigation);
|
|
5624
5977
|
const handleNavigation = (path) => {
|
|
5625
5978
|
if (path.includes("?")) {
|
|
5626
5979
|
const [basePath, query] = path.split("?");
|
|
@@ -5676,12 +6029,14 @@ const Sidebar = ({ className }) => {
|
|
|
5676
6029
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5677
6030
|
SidebarButton,
|
|
5678
6031
|
{
|
|
5679
|
-
icon: item.icon,
|
|
6032
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, className: "w-5 h-5" }),
|
|
5680
6033
|
label: item.label,
|
|
5681
6034
|
active: isActive,
|
|
5682
6035
|
category: item.category,
|
|
5683
6036
|
expanded: isExpanded,
|
|
5684
|
-
onClick: () => handleNavigation(item.path)
|
|
6037
|
+
onClick: () => handleNavigation(item.path),
|
|
6038
|
+
badge: item.badge,
|
|
6039
|
+
disabled: item.disabled
|
|
5685
6040
|
},
|
|
5686
6041
|
item.value
|
|
5687
6042
|
);
|
|
@@ -5747,6 +6102,28 @@ const AppLayout = () => {
|
|
|
5747
6102
|
)
|
|
5748
6103
|
] });
|
|
5749
6104
|
};
|
|
6105
|
+
const DashboardWithSidePanel = ({
|
|
6106
|
+
children,
|
|
6107
|
+
sidePanel,
|
|
6108
|
+
showSidePanel = false,
|
|
6109
|
+
sidePanelWidth = 72,
|
|
6110
|
+
className
|
|
6111
|
+
}) => {
|
|
6112
|
+
const marginClass = `pr-${sidePanelWidth}`;
|
|
6113
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative h-full", className), children: [
|
|
6114
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6115
|
+
"div",
|
|
6116
|
+
{
|
|
6117
|
+
className: cn(
|
|
6118
|
+
"transition-all duration-300",
|
|
6119
|
+
showSidePanel ? marginClass : ""
|
|
6120
|
+
),
|
|
6121
|
+
children
|
|
6122
|
+
}
|
|
6123
|
+
),
|
|
6124
|
+
showSidePanel && sidePanel
|
|
6125
|
+
] });
|
|
6126
|
+
};
|
|
5750
6127
|
const SectionHeader = ({
|
|
5751
6128
|
title,
|
|
5752
6129
|
description,
|
|
@@ -7769,6 +8146,7 @@ function createReactApp(config) {
|
|
|
7769
8146
|
enableQuery = true,
|
|
7770
8147
|
enableRouting = true,
|
|
7771
8148
|
auth,
|
|
8149
|
+
navigation,
|
|
7772
8150
|
customProviders = []
|
|
7773
8151
|
} = appConfig;
|
|
7774
8152
|
const queryClient = new reactQuery.QueryClient({
|
|
@@ -7803,7 +8181,7 @@ function createReactApp(config) {
|
|
|
7803
8181
|
tree = /* @__PURE__ */ jsxRuntime.jsx(Provider, { children: tree }, Provider.name);
|
|
7804
8182
|
});
|
|
7805
8183
|
if (enableRouting) {
|
|
7806
|
-
tree = /* @__PURE__ */ jsxRuntime.jsx(SidebarProvider, { children: tree });
|
|
8184
|
+
tree = /* @__PURE__ */ jsxRuntime.jsx(NavigationProvider, { initialNavigation: navigation, children: /* @__PURE__ */ jsxRuntime.jsx(SidebarProvider, { children: tree }) });
|
|
7807
8185
|
}
|
|
7808
8186
|
if (enableAuth) {
|
|
7809
8187
|
tree = /* @__PURE__ */ jsxRuntime.jsx(AuthProvider, { config: auth, children: tree });
|
|
@@ -10069,6 +10447,7 @@ exports.DarkModeToggle = DarkModeToggle;
|
|
|
10069
10447
|
exports.DashboardCard = DashboardCard;
|
|
10070
10448
|
exports.DashboardGrid = DashboardGrid;
|
|
10071
10449
|
exports.DashboardTemplate = DashboardTemplate;
|
|
10450
|
+
exports.DashboardWithSidePanel = DashboardWithSidePanel;
|
|
10072
10451
|
exports.DataBadge = DataBadge;
|
|
10073
10452
|
exports.DataDetailTemplate = DataDetailTemplate;
|
|
10074
10453
|
exports.DataTable = DataTable;
|
|
@@ -10096,14 +10475,17 @@ exports.FileUpload = FileUpload;
|
|
|
10096
10475
|
exports.FormField = FormField;
|
|
10097
10476
|
exports.FormGroup = FormGroup;
|
|
10098
10477
|
exports.GlobalSearch = GlobalSearch;
|
|
10478
|
+
exports.Icon = Icon;
|
|
10099
10479
|
exports.IconBadge = IconBadge;
|
|
10100
10480
|
exports.Input = Input;
|
|
10101
10481
|
exports.Label = Label;
|
|
10102
10482
|
exports.Loading = Loading;
|
|
10103
10483
|
exports.LoginForm = LoginForm;
|
|
10104
10484
|
exports.LogoutButton = LogoutButton;
|
|
10485
|
+
exports.MetricCalculationEngine = MetricCalculationEngine;
|
|
10105
10486
|
exports.Modal = Modal;
|
|
10106
10487
|
exports.NavMenu = NavMenu;
|
|
10488
|
+
exports.NavigationProvider = NavigationProvider;
|
|
10107
10489
|
exports.PageTemplate = PageTemplate;
|
|
10108
10490
|
exports.Pagination = Pagination;
|
|
10109
10491
|
exports.PaletteSwitcher = PaletteSwitcher;
|
|
@@ -10111,6 +10493,7 @@ exports.ProgressBar = ProgressBar;
|
|
|
10111
10493
|
exports.ProtectedRoute = ProtectedRoute;
|
|
10112
10494
|
exports.RESPONSIVE_CHART_HEIGHTS = RESPONSIVE_CHART_HEIGHTS;
|
|
10113
10495
|
exports.ROUTES = ROUTES;
|
|
10496
|
+
exports.SalesPanel = SalesPanel;
|
|
10114
10497
|
exports.SearchInput = SearchInput;
|
|
10115
10498
|
exports.SectionHeader = SectionHeader;
|
|
10116
10499
|
exports.Select = Select;
|
|
@@ -10119,6 +10502,7 @@ exports.SelectItem = SelectItem;
|
|
|
10119
10502
|
exports.SelectTrigger = SelectTrigger;
|
|
10120
10503
|
exports.SelectValue = SelectValue;
|
|
10121
10504
|
exports.ShowcaseSection = ShowcaseSection;
|
|
10505
|
+
exports.Sidebar = Sidebar;
|
|
10122
10506
|
exports.SidebarButton = SidebarButton;
|
|
10123
10507
|
exports.SidebarProvider = SidebarProvider;
|
|
10124
10508
|
exports.Skeleton = Skeleton;
|
|
@@ -10154,7 +10538,10 @@ exports.formatNumberWithTooltip = formatNumberWithTooltip;
|
|
|
10154
10538
|
exports.getAnimationClasses = getAnimationClasses;
|
|
10155
10539
|
exports.getChartHeight = getChartHeight;
|
|
10156
10540
|
exports.getContainerHeightClass = getContainerHeightClass;
|
|
10541
|
+
exports.getIcon = getIcon;
|
|
10542
|
+
exports.getNavigationItems = getNavigationItems;
|
|
10157
10543
|
exports.interactionVariants = interactionVariants;
|
|
10544
|
+
exports.isValidIcon = isValidIcon;
|
|
10158
10545
|
exports.legacyPatterns = legacyPatterns;
|
|
10159
10546
|
exports.setGlobalAuthService = setGlobalAuthService;
|
|
10160
10547
|
exports.tooltipContent = tooltipContent;
|
|
@@ -10164,6 +10551,7 @@ exports.useAuth = useAuth;
|
|
|
10164
10551
|
exports.useCreateExample = useCreateExample;
|
|
10165
10552
|
exports.useDeleteExample = useDeleteExample;
|
|
10166
10553
|
exports.useGetExample = useGetExample;
|
|
10554
|
+
exports.useNavigation = useNavigation;
|
|
10167
10555
|
exports.usePermissions = usePermissions;
|
|
10168
10556
|
exports.useSidebar = useSidebar;
|
|
10169
10557
|
exports.useTextOverflow = useTextOverflow;
|