@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.es.js
CHANGED
|
@@ -6,19 +6,18 @@ import { clsx } from "clsx";
|
|
|
6
6
|
import { twMerge } from "tailwind-merge";
|
|
7
7
|
import * as React from "react";
|
|
8
8
|
import React__default, { useRef, useState, useEffect, Component, createContext, useContext, useMemo, useId, useCallback } from "react";
|
|
9
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
10
|
+
import { Menu, HelpCircle, Info, AlertCircle, Check, ArrowDown, ArrowUp, ArrowLeft, ArrowRight, ChevronUp, ChevronLeft, ChevronDown, ChevronRight, Music, Video, Image, Folder, File, Flag, Tag, Bookmark, Heart, Star, MapPin, Clock, Calendar, Phone, Mail, Unlock, Lock, Share, Upload, Download, Eye, Trash2, Edit, Plus, Search, Bell, Settings, Home, Layout, TrendingUp, Database, BarChart3, Users, Shield, X, Palette, Circle, Loader2, Target, ExternalLink, TrendingDown, Minus, FileX, Sparkles, User, Sun, Moon, UserCircle, LogOut, ChevronsUpDown, AreaChart, LineChart, CheckCircle, AlertTriangle, Square, Waves, Zap, TreePine, Sunset, Building2, DollarSign, ShoppingCart, MoreHorizontal, Filter, Activity, RefreshCw, FileText, History, Save, Copy, Grid3X3, Layers, XCircle } from "lucide-react";
|
|
9
11
|
import { cva } from "class-variance-authority";
|
|
10
12
|
import axios from "axios";
|
|
11
13
|
import { useQuery, useQueryClient, useMutation, QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
12
|
-
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
13
14
|
import { Slot } from "@radix-ui/react-slot";
|
|
14
15
|
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
15
16
|
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
16
17
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
17
|
-
import { ChevronRight, Check, Circle, Loader2, ArrowRight, TrendingUp, TrendingDown, Minus, FileX, AlertCircle, Search, Database, Sparkles, Mail, Lock, Shield, User, Sun, Moon, UserCircle, Settings, LogOut, X, ChevronsUpDown, ChevronUp, ChevronDown, ChevronLeft, Clock, Calendar, BarChart3, AreaChart, LineChart, Upload, Image, File, CheckCircle, Info, AlertTriangle, Home, Palette, Square, Waves, Zap, TreePine, Sunset, Building2, Users, DollarSign, ShoppingCart, Menu, MoreHorizontal, Plus, Filter, Download, Activity, RefreshCw, Eye, Edit, Trash2, FileText, History, ArrowLeft, Save, Copy, ExternalLink, Grid3X3, Layers, XCircle } from "lucide-react";
|
|
18
18
|
import { createPortal } from "react-dom";
|
|
19
19
|
import { useLocation, useNavigate, useSearchParams, Outlet, BrowserRouter, Routes, Route } from "react-router-dom";
|
|
20
20
|
import { createRoot } from "react-dom/client";
|
|
21
|
-
import "@tanstack/react-query-devtools";
|
|
22
21
|
const APP_NAME = "Design System Showcase";
|
|
23
22
|
const API_ENDPOINTS = {
|
|
24
23
|
HEALTH: "/health"
|
|
@@ -244,6 +243,240 @@ const tooltipContent = {
|
|
|
244
243
|
profile: "Profile",
|
|
245
244
|
account: "Account settings"
|
|
246
245
|
};
|
|
246
|
+
class MetricCalculationEngine {
|
|
247
|
+
static calculateMetric(config, data, previousData) {
|
|
248
|
+
const currentValue = this.aggregateValue(config, data);
|
|
249
|
+
const previousValue = previousData ? this.aggregateValue(config, previousData) : void 0;
|
|
250
|
+
const trend = this.calculateTrend(currentValue, previousValue);
|
|
251
|
+
const formattedValue = this.formatValue(currentValue, config.format, config.type);
|
|
252
|
+
const target = typeof config.target === "function" ? config.target(data) : config.target;
|
|
253
|
+
return {
|
|
254
|
+
current: currentValue,
|
|
255
|
+
previous: previousValue,
|
|
256
|
+
trend,
|
|
257
|
+
target,
|
|
258
|
+
formattedValue
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
static aggregateValue(config, data) {
|
|
262
|
+
if (!data.length) return 0;
|
|
263
|
+
const values = data.map((item) => {
|
|
264
|
+
const value = this.extractValue(item, config.key);
|
|
265
|
+
return typeof value === "number" ? value : 0;
|
|
266
|
+
}).filter((value) => !isNaN(value));
|
|
267
|
+
if (!values.length) return 0;
|
|
268
|
+
switch (config.aggregation || "sum") {
|
|
269
|
+
case "sum":
|
|
270
|
+
return values.reduce((sum, value) => sum + value, 0);
|
|
271
|
+
case "avg":
|
|
272
|
+
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
273
|
+
case "count":
|
|
274
|
+
return values.length;
|
|
275
|
+
case "min":
|
|
276
|
+
return Math.min(...values);
|
|
277
|
+
case "max":
|
|
278
|
+
return Math.max(...values);
|
|
279
|
+
default:
|
|
280
|
+
return values.reduce((sum, value) => sum + value, 0);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
static extractValue(item, key) {
|
|
284
|
+
return key.split(".").reduce((obj, k) => obj == null ? void 0 : obj[k], item);
|
|
285
|
+
}
|
|
286
|
+
static calculateTrend(current, previous) {
|
|
287
|
+
if (previous === void 0 || previous === 0) return "neutral";
|
|
288
|
+
const change = (current - previous) / Math.abs(previous) * 100;
|
|
289
|
+
if (change > 1) return "up";
|
|
290
|
+
if (change < -1) return "down";
|
|
291
|
+
return "neutral";
|
|
292
|
+
}
|
|
293
|
+
static formatValue(value, format, type) {
|
|
294
|
+
let formatted = value;
|
|
295
|
+
if ((format == null ? void 0 : format.decimals) !== void 0) {
|
|
296
|
+
formatted = Number(value.toFixed(format.decimals));
|
|
297
|
+
}
|
|
298
|
+
let result = formatted.toString();
|
|
299
|
+
if ((format == null ? void 0 : format.thousands) !== false && Math.abs(formatted) >= 1e3) {
|
|
300
|
+
result = formatted.toLocaleString();
|
|
301
|
+
}
|
|
302
|
+
switch (type) {
|
|
303
|
+
case "currency":
|
|
304
|
+
result = new Intl.NumberFormat("en-US", {
|
|
305
|
+
style: "currency",
|
|
306
|
+
currency: "USD",
|
|
307
|
+
minimumFractionDigits: (format == null ? void 0 : format.decimals) ?? 2,
|
|
308
|
+
maximumFractionDigits: (format == null ? void 0 : format.decimals) ?? 2
|
|
309
|
+
}).format(formatted);
|
|
310
|
+
break;
|
|
311
|
+
case "percentage":
|
|
312
|
+
result = `${formatted.toFixed((format == null ? void 0 : format.decimals) ?? 1)}%`;
|
|
313
|
+
break;
|
|
314
|
+
case "duration":
|
|
315
|
+
result = this.formatDuration(formatted);
|
|
316
|
+
break;
|
|
317
|
+
case "ratio":
|
|
318
|
+
result = `${formatted.toFixed((format == null ? void 0 : format.decimals) ?? 2)}:1`;
|
|
319
|
+
break;
|
|
320
|
+
default:
|
|
321
|
+
if (format == null ? void 0 : format.prefix) result = format.prefix + result;
|
|
322
|
+
if (format == null ? void 0 : format.suffix) result = result + format.suffix;
|
|
323
|
+
}
|
|
324
|
+
return result;
|
|
325
|
+
}
|
|
326
|
+
static formatDuration(minutes) {
|
|
327
|
+
const hours = Math.floor(minutes / 60);
|
|
328
|
+
const mins = Math.floor(minutes % 60);
|
|
329
|
+
if (hours > 0) {
|
|
330
|
+
return `${hours}h ${mins}m`;
|
|
331
|
+
}
|
|
332
|
+
return `${mins}m`;
|
|
333
|
+
}
|
|
334
|
+
static calculateTrendData(config, data, dateField = "date", periods = 12) {
|
|
335
|
+
const groupedData = this.groupByPeriod(data, dateField);
|
|
336
|
+
const sortedDates = Object.keys(groupedData).sort();
|
|
337
|
+
return sortedDates.slice(-periods).map((date) => ({
|
|
338
|
+
date,
|
|
339
|
+
value: this.aggregateValue(config, groupedData[date]),
|
|
340
|
+
label: this.formatDateLabel(date)
|
|
341
|
+
}));
|
|
342
|
+
}
|
|
343
|
+
static groupByPeriod(data, dateField) {
|
|
344
|
+
return data.reduce((groups, item) => {
|
|
345
|
+
const date = this.extractValue(item, dateField);
|
|
346
|
+
if (!date) return groups;
|
|
347
|
+
const period = new Date(date).toISOString().split("T")[0];
|
|
348
|
+
if (!groups[period]) groups[period] = [];
|
|
349
|
+
groups[period].push(item);
|
|
350
|
+
return groups;
|
|
351
|
+
}, {});
|
|
352
|
+
}
|
|
353
|
+
static formatDateLabel(dateString) {
|
|
354
|
+
const date = new Date(dateString);
|
|
355
|
+
return date.toLocaleDateString("en-US", {
|
|
356
|
+
month: "short",
|
|
357
|
+
day: "numeric"
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
static calculateCategoryBreakdown(data, categoryField, valueField, maxCategories = 8) {
|
|
361
|
+
const groups = data.reduce((acc, item) => {
|
|
362
|
+
const category = String(this.extractValue(item, categoryField) || "Unknown");
|
|
363
|
+
const value = this.extractValue(item, valueField) || 0;
|
|
364
|
+
if (!acc[category]) acc[category] = 0;
|
|
365
|
+
acc[category] += typeof value === "number" ? value : 0;
|
|
366
|
+
return acc;
|
|
367
|
+
}, {});
|
|
368
|
+
const total = Object.values(groups).reduce((sum, value) => sum + value, 0);
|
|
369
|
+
let categories = Object.entries(groups).map(([category, value]) => ({
|
|
370
|
+
category,
|
|
371
|
+
value,
|
|
372
|
+
percentage: total > 0 ? value / total * 100 : 0
|
|
373
|
+
})).sort((a, b) => b.value - a.value);
|
|
374
|
+
if (categories.length > maxCategories) {
|
|
375
|
+
const topCategories = categories.slice(0, maxCategories - 1);
|
|
376
|
+
const otherValue = categories.slice(maxCategories - 1).reduce((sum, cat) => sum + cat.value, 0);
|
|
377
|
+
topCategories.push({
|
|
378
|
+
category: "Other",
|
|
379
|
+
value: otherValue,
|
|
380
|
+
percentage: total > 0 ? otherValue / total * 100 : 0
|
|
381
|
+
});
|
|
382
|
+
categories = topCategories;
|
|
383
|
+
}
|
|
384
|
+
return categories;
|
|
385
|
+
}
|
|
386
|
+
static detectInsights(metrics, thresholds = {}) {
|
|
387
|
+
const insights = [];
|
|
388
|
+
for (const [key, metric] of Object.entries(metrics)) {
|
|
389
|
+
const threshold = thresholds[key];
|
|
390
|
+
if (metric.target && metric.current < metric.target * 0.8) {
|
|
391
|
+
insights.push({
|
|
392
|
+
type: "negative",
|
|
393
|
+
message: `${key} is significantly below target`,
|
|
394
|
+
value: metric.current
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
if (metric.trend === "up" && metric.previous && metric.current > metric.previous * 1.2) {
|
|
398
|
+
insights.push({
|
|
399
|
+
type: "positive",
|
|
400
|
+
message: `${key} showing strong growth`,
|
|
401
|
+
value: metric.current
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
if (threshold && metric.current >= threshold.critical) {
|
|
405
|
+
insights.push({
|
|
406
|
+
type: "negative",
|
|
407
|
+
message: `${key} has reached critical threshold`,
|
|
408
|
+
value: metric.current
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return insights.slice(0, 5);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const iconMap = {
|
|
416
|
+
Palette,
|
|
417
|
+
Menu,
|
|
418
|
+
X,
|
|
419
|
+
Shield,
|
|
420
|
+
Users,
|
|
421
|
+
BarChart3,
|
|
422
|
+
Database,
|
|
423
|
+
TrendingUp,
|
|
424
|
+
Layout,
|
|
425
|
+
Home,
|
|
426
|
+
Settings,
|
|
427
|
+
Bell,
|
|
428
|
+
Search,
|
|
429
|
+
Plus,
|
|
430
|
+
Edit,
|
|
431
|
+
Trash2,
|
|
432
|
+
Eye,
|
|
433
|
+
Download,
|
|
434
|
+
Upload,
|
|
435
|
+
Share,
|
|
436
|
+
Lock,
|
|
437
|
+
Unlock,
|
|
438
|
+
Mail,
|
|
439
|
+
Phone,
|
|
440
|
+
Calendar,
|
|
441
|
+
Clock,
|
|
442
|
+
MapPin,
|
|
443
|
+
Star,
|
|
444
|
+
Heart,
|
|
445
|
+
Bookmark,
|
|
446
|
+
Tag,
|
|
447
|
+
Flag,
|
|
448
|
+
File,
|
|
449
|
+
Folder,
|
|
450
|
+
Image,
|
|
451
|
+
Video,
|
|
452
|
+
Music,
|
|
453
|
+
ChevronRight,
|
|
454
|
+
ChevronDown,
|
|
455
|
+
ChevronLeft,
|
|
456
|
+
ChevronUp,
|
|
457
|
+
ArrowRight,
|
|
458
|
+
ArrowLeft,
|
|
459
|
+
ArrowUp,
|
|
460
|
+
ArrowDown,
|
|
461
|
+
Check,
|
|
462
|
+
AlertCircle,
|
|
463
|
+
Info,
|
|
464
|
+
HelpCircle
|
|
465
|
+
};
|
|
466
|
+
const Icon = ({ name, className = "w-5 h-5", size, ...props }) => {
|
|
467
|
+
const IconComponent = iconMap[name];
|
|
468
|
+
if (!IconComponent) {
|
|
469
|
+
console.warn(`Icon "${name}" not found. Using default Menu icon.`);
|
|
470
|
+
return /* @__PURE__ */ jsx(Menu, { className, size, ...props });
|
|
471
|
+
}
|
|
472
|
+
return /* @__PURE__ */ jsx(IconComponent, { className, size, ...props });
|
|
473
|
+
};
|
|
474
|
+
const getIcon = (name) => {
|
|
475
|
+
return iconMap[name] || Menu;
|
|
476
|
+
};
|
|
477
|
+
const isValidIcon = (name) => {
|
|
478
|
+
return name in iconMap;
|
|
479
|
+
};
|
|
247
480
|
function cn(...inputs) {
|
|
248
481
|
return twMerge(clsx(inputs));
|
|
249
482
|
}
|
|
@@ -1265,6 +1498,83 @@ const DataBadge = ({
|
|
|
1265
1498
|
}
|
|
1266
1499
|
return badge;
|
|
1267
1500
|
};
|
|
1501
|
+
const getStageStatus = (stage) => {
|
|
1502
|
+
switch (stage) {
|
|
1503
|
+
case "Closed Won":
|
|
1504
|
+
return "success";
|
|
1505
|
+
case "Negotiation":
|
|
1506
|
+
return "warning";
|
|
1507
|
+
case "Proposal Sent":
|
|
1508
|
+
return "info";
|
|
1509
|
+
case "Qualified":
|
|
1510
|
+
return "info";
|
|
1511
|
+
case "Discovery":
|
|
1512
|
+
return "neutral";
|
|
1513
|
+
default:
|
|
1514
|
+
return "neutral";
|
|
1515
|
+
}
|
|
1516
|
+
};
|
|
1517
|
+
const SalesPanel = ({
|
|
1518
|
+
sales,
|
|
1519
|
+
onSaleClick,
|
|
1520
|
+
onClose
|
|
1521
|
+
}) => {
|
|
1522
|
+
const topDeals = sales.slice(0, 5);
|
|
1523
|
+
const totalValue = sales.reduce((sum, sale) => sum + sale.amount, 0);
|
|
1524
|
+
return /* @__PURE__ */ 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: [
|
|
1525
|
+
/* @__PURE__ */ jsx("div", { className: "p-3 border-b border-border", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1526
|
+
/* @__PURE__ */ jsxs("h3", { className: "font-medium flex items-center gap-2", children: [
|
|
1527
|
+
/* @__PURE__ */ jsx(Target, { className: "w-4 h-4" }),
|
|
1528
|
+
"Pipeline"
|
|
1529
|
+
] }),
|
|
1530
|
+
onClose && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, children: "×" })
|
|
1531
|
+
] }) }),
|
|
1532
|
+
/* @__PURE__ */ jsx("div", { className: "p-3 border-b border-border", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1533
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
1534
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Total Value" }),
|
|
1535
|
+
/* @__PURE__ */ jsxs("span", { className: "font-semibold text-green-600", children: [
|
|
1536
|
+
"$",
|
|
1537
|
+
totalValue.toLocaleString()
|
|
1538
|
+
] })
|
|
1539
|
+
] }),
|
|
1540
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
1541
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Active Deals" }),
|
|
1542
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold", children: sales.length })
|
|
1543
|
+
] })
|
|
1544
|
+
] }) }),
|
|
1545
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-auto p-3", children: [
|
|
1546
|
+
/* @__PURE__ */ jsx("h4", { className: "text-sm font-medium mb-3", children: "Recent Opportunities" }),
|
|
1547
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: topDeals.map((sale) => /* @__PURE__ */ jsxs(
|
|
1548
|
+
"div",
|
|
1549
|
+
{
|
|
1550
|
+
className: "p-2 border border-border rounded-md hover:bg-muted/50 cursor-pointer transition-colors",
|
|
1551
|
+
onClick: () => onSaleClick == null ? void 0 : onSaleClick(sale),
|
|
1552
|
+
children: [
|
|
1553
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-start mb-1", children: [
|
|
1554
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1555
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: sale.customer }),
|
|
1556
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground truncate", children: sale.product })
|
|
1557
|
+
] }),
|
|
1558
|
+
/* @__PURE__ */ jsx("div", { className: "text-right ml-2", children: /* @__PURE__ */ jsxs("p", { className: "text-sm font-semibold text-green-600", children: [
|
|
1559
|
+
"$",
|
|
1560
|
+
sale.amount.toLocaleString()
|
|
1561
|
+
] }) })
|
|
1562
|
+
] }),
|
|
1563
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
1564
|
+
/* @__PURE__ */ jsx(DataBadge, { variant: "status", status: getStageStatus(sale.stage), children: sale.stage }),
|
|
1565
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: sale.salesperson })
|
|
1566
|
+
] })
|
|
1567
|
+
]
|
|
1568
|
+
},
|
|
1569
|
+
sale.id
|
|
1570
|
+
)) })
|
|
1571
|
+
] }),
|
|
1572
|
+
/* @__PURE__ */ jsx("div", { className: "p-3 border-t border-border", children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "w-full", children: [
|
|
1573
|
+
/* @__PURE__ */ jsx(ExternalLink, { className: "w-4 h-4 mr-2" }),
|
|
1574
|
+
"View All Deals"
|
|
1575
|
+
] }) })
|
|
1576
|
+
] });
|
|
1577
|
+
};
|
|
1268
1578
|
const StatCard = ({
|
|
1269
1579
|
title,
|
|
1270
1580
|
value,
|
|
@@ -5529,6 +5839,42 @@ const useSidebar = () => {
|
|
|
5529
5839
|
}
|
|
5530
5840
|
return context;
|
|
5531
5841
|
};
|
|
5842
|
+
const NavigationContext = createContext(void 0);
|
|
5843
|
+
const defaultShowcaseNavigation = [
|
|
5844
|
+
{ value: "showcase", label: "Showcase", icon: "Palette", path: "/showcase", category: 5 },
|
|
5845
|
+
{ value: "admin-dashboard", label: "Admin Dashboard", icon: "Shield", path: "/admin/dashboard", category: 2 },
|
|
5846
|
+
{ value: "admin-users", label: "User Management", icon: "Users", path: "/admin/users", category: 3 },
|
|
5847
|
+
{ value: "admin-sales", label: "Sales Dashboard", icon: "TrendingUp", path: "/admin/sales", category: 4 },
|
|
5848
|
+
{ value: "entity-performance", label: "Performance Dashboard", icon: "BarChart3", path: "/entity/performance", category: 6 },
|
|
5849
|
+
{ value: "entity-management", label: "Entity Management", icon: "Database", path: "/entity/management", category: 7 },
|
|
5850
|
+
{ value: "entity-template", label: "Template Example", icon: "Layout", path: "/entity/template-example", category: 1 }
|
|
5851
|
+
];
|
|
5852
|
+
const NavigationProvider = ({ children, initialNavigation }) => {
|
|
5853
|
+
const [navigation, setNavigation] = React__default.useState(
|
|
5854
|
+
initialNavigation || {
|
|
5855
|
+
items: defaultShowcaseNavigation,
|
|
5856
|
+
showDefaultNavigation: true,
|
|
5857
|
+
defaultExpanded: true
|
|
5858
|
+
}
|
|
5859
|
+
);
|
|
5860
|
+
return /* @__PURE__ */ jsx(NavigationContext.Provider, { value: { navigation, setNavigation }, children });
|
|
5861
|
+
};
|
|
5862
|
+
const useNavigation = () => {
|
|
5863
|
+
const context = useContext(NavigationContext);
|
|
5864
|
+
if (context === void 0) {
|
|
5865
|
+
throw new Error("useNavigation must be used within a NavigationProvider");
|
|
5866
|
+
}
|
|
5867
|
+
return context;
|
|
5868
|
+
};
|
|
5869
|
+
const getNavigationItems = (config) => {
|
|
5870
|
+
if (config.items.length > 0) {
|
|
5871
|
+
return config.items;
|
|
5872
|
+
}
|
|
5873
|
+
if (config.showDefaultNavigation !== false) {
|
|
5874
|
+
return defaultShowcaseNavigation;
|
|
5875
|
+
}
|
|
5876
|
+
return [];
|
|
5877
|
+
};
|
|
5532
5878
|
const SidebarButton = ({
|
|
5533
5879
|
icon,
|
|
5534
5880
|
label,
|
|
@@ -5536,13 +5882,16 @@ const SidebarButton = ({
|
|
|
5536
5882
|
category = 1,
|
|
5537
5883
|
expanded = false,
|
|
5538
5884
|
onClick,
|
|
5539
|
-
className
|
|
5885
|
+
className,
|
|
5886
|
+
badge,
|
|
5887
|
+
disabled = false
|
|
5540
5888
|
}) => {
|
|
5541
5889
|
return /* @__PURE__ */ jsxs(
|
|
5542
5890
|
Button,
|
|
5543
5891
|
{
|
|
5544
5892
|
variant: active ? "secondary" : "ghost",
|
|
5545
5893
|
onClick,
|
|
5894
|
+
disabled,
|
|
5546
5895
|
tooltip: !expanded ? label : void 0,
|
|
5547
5896
|
className: cn(
|
|
5548
5897
|
"relative w-full justify-start gap-3 h-12",
|
|
@@ -5576,12 +5925,19 @@ const SidebarButton = ({
|
|
|
5576
5925
|
"text-sm font-medium flex-1 text-left",
|
|
5577
5926
|
active ? `text-category-${category}` : "text-foreground"
|
|
5578
5927
|
), children: label }),
|
|
5579
|
-
|
|
5928
|
+
badge ? /* @__PURE__ */ jsx("span", { className: cn(
|
|
5929
|
+
"px-2 py-0.5 text-xs font-medium rounded-full flex-shrink-0",
|
|
5930
|
+
"bg-primary/10 text-primary"
|
|
5931
|
+
), children: badge }) : active && /* @__PURE__ */ jsx("div", { className: cn(
|
|
5580
5932
|
"w-2 h-2 rounded-full flex-shrink-0",
|
|
5581
5933
|
`bg-category-${category}`
|
|
5582
5934
|
) })
|
|
5583
5935
|
] }),
|
|
5584
|
-
!expanded && active && /* @__PURE__ */ jsx("div", { className: "absolute -top-1 -right-1", children: /* @__PURE__ */ jsx("
|
|
5936
|
+
!expanded && (badge || active) && /* @__PURE__ */ jsx("div", { className: "absolute -top-1 -right-1", children: badge ? /* @__PURE__ */ jsx("span", { className: cn(
|
|
5937
|
+
"px-1.5 py-0.5 text-xs font-bold rounded-full",
|
|
5938
|
+
"bg-primary text-primary-foreground",
|
|
5939
|
+
"ring-2 ring-background"
|
|
5940
|
+
), children: badge }) : active && /* @__PURE__ */ jsx("div", { className: cn(
|
|
5585
5941
|
"w-2.5 h-2.5 rounded-full",
|
|
5586
5942
|
`bg-category-${category}`,
|
|
5587
5943
|
"ring-2 ring-background"
|
|
@@ -5592,14 +5948,11 @@ const SidebarButton = ({
|
|
|
5592
5948
|
};
|
|
5593
5949
|
const Sidebar = ({ className }) => {
|
|
5594
5950
|
const { isExpanded, toggleSidebar } = useSidebar();
|
|
5951
|
+
const { navigation } = useNavigation();
|
|
5595
5952
|
const location = useLocation();
|
|
5596
5953
|
const navigate = useNavigate();
|
|
5597
5954
|
const [searchParams] = useSearchParams();
|
|
5598
|
-
const items =
|
|
5599
|
-
{ value: "showcase", label: "Showcase", icon: /* @__PURE__ */ jsx(Palette, { className: "w-5 h-5" }), path: "/showcase", category: 5 },
|
|
5600
|
-
{ value: "admin-dashboard", label: "Admin Dashboard", icon: /* @__PURE__ */ jsx(Shield, { className: "w-5 h-5" }), path: "/admin/dashboard", category: 2 },
|
|
5601
|
-
{ value: "admin-users", label: "User Management", icon: /* @__PURE__ */ jsx(Users, { className: "w-5 h-5" }), path: "/admin/users", category: 3 }
|
|
5602
|
-
];
|
|
5955
|
+
const items = getNavigationItems(navigation);
|
|
5603
5956
|
const handleNavigation = (path) => {
|
|
5604
5957
|
if (path.includes("?")) {
|
|
5605
5958
|
const [basePath, query] = path.split("?");
|
|
@@ -5655,12 +6008,14 @@ const Sidebar = ({ className }) => {
|
|
|
5655
6008
|
return /* @__PURE__ */ jsx(
|
|
5656
6009
|
SidebarButton,
|
|
5657
6010
|
{
|
|
5658
|
-
icon: item.icon,
|
|
6011
|
+
icon: /* @__PURE__ */ jsx(Icon, { name: item.icon, className: "w-5 h-5" }),
|
|
5659
6012
|
label: item.label,
|
|
5660
6013
|
active: isActive,
|
|
5661
6014
|
category: item.category,
|
|
5662
6015
|
expanded: isExpanded,
|
|
5663
|
-
onClick: () => handleNavigation(item.path)
|
|
6016
|
+
onClick: () => handleNavigation(item.path),
|
|
6017
|
+
badge: item.badge,
|
|
6018
|
+
disabled: item.disabled
|
|
5664
6019
|
},
|
|
5665
6020
|
item.value
|
|
5666
6021
|
);
|
|
@@ -5726,6 +6081,28 @@ const AppLayout = () => {
|
|
|
5726
6081
|
)
|
|
5727
6082
|
] });
|
|
5728
6083
|
};
|
|
6084
|
+
const DashboardWithSidePanel = ({
|
|
6085
|
+
children,
|
|
6086
|
+
sidePanel,
|
|
6087
|
+
showSidePanel = false,
|
|
6088
|
+
sidePanelWidth = 72,
|
|
6089
|
+
className
|
|
6090
|
+
}) => {
|
|
6091
|
+
const marginClass = `pr-${sidePanelWidth}`;
|
|
6092
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("relative h-full", className), children: [
|
|
6093
|
+
/* @__PURE__ */ jsx(
|
|
6094
|
+
"div",
|
|
6095
|
+
{
|
|
6096
|
+
className: cn(
|
|
6097
|
+
"transition-all duration-300",
|
|
6098
|
+
showSidePanel ? marginClass : ""
|
|
6099
|
+
),
|
|
6100
|
+
children
|
|
6101
|
+
}
|
|
6102
|
+
),
|
|
6103
|
+
showSidePanel && sidePanel
|
|
6104
|
+
] });
|
|
6105
|
+
};
|
|
5729
6106
|
const SectionHeader = ({
|
|
5730
6107
|
title,
|
|
5731
6108
|
description,
|
|
@@ -7748,6 +8125,7 @@ function createReactApp(config) {
|
|
|
7748
8125
|
enableQuery = true,
|
|
7749
8126
|
enableRouting = true,
|
|
7750
8127
|
auth,
|
|
8128
|
+
navigation,
|
|
7751
8129
|
customProviders = []
|
|
7752
8130
|
} = appConfig;
|
|
7753
8131
|
const queryClient = new QueryClient({
|
|
@@ -7782,7 +8160,7 @@ function createReactApp(config) {
|
|
|
7782
8160
|
tree = /* @__PURE__ */ jsx(Provider, { children: tree }, Provider.name);
|
|
7783
8161
|
});
|
|
7784
8162
|
if (enableRouting) {
|
|
7785
|
-
tree = /* @__PURE__ */ jsx(SidebarProvider, { children: tree });
|
|
8163
|
+
tree = /* @__PURE__ */ jsx(NavigationProvider, { initialNavigation: navigation, children: /* @__PURE__ */ jsx(SidebarProvider, { children: tree }) });
|
|
7786
8164
|
}
|
|
7787
8165
|
if (enableAuth) {
|
|
7788
8166
|
tree = /* @__PURE__ */ jsx(AuthProvider, { config: auth, children: tree });
|
|
@@ -10049,6 +10427,7 @@ export {
|
|
|
10049
10427
|
DashboardCard,
|
|
10050
10428
|
DashboardGrid,
|
|
10051
10429
|
DashboardTemplate,
|
|
10430
|
+
DashboardWithSidePanel,
|
|
10052
10431
|
DataBadge,
|
|
10053
10432
|
DataDetailTemplate,
|
|
10054
10433
|
DataTable,
|
|
@@ -10076,14 +10455,17 @@ export {
|
|
|
10076
10455
|
FormField,
|
|
10077
10456
|
FormGroup,
|
|
10078
10457
|
GlobalSearch,
|
|
10458
|
+
Icon,
|
|
10079
10459
|
IconBadge,
|
|
10080
10460
|
Input,
|
|
10081
10461
|
Label,
|
|
10082
10462
|
Loading,
|
|
10083
10463
|
LoginForm,
|
|
10084
10464
|
LogoutButton,
|
|
10465
|
+
MetricCalculationEngine,
|
|
10085
10466
|
Modal,
|
|
10086
10467
|
NavMenu,
|
|
10468
|
+
NavigationProvider,
|
|
10087
10469
|
PageTemplate,
|
|
10088
10470
|
Pagination,
|
|
10089
10471
|
PaletteSwitcher,
|
|
@@ -10091,6 +10473,7 @@ export {
|
|
|
10091
10473
|
ProtectedRoute,
|
|
10092
10474
|
RESPONSIVE_CHART_HEIGHTS,
|
|
10093
10475
|
ROUTES,
|
|
10476
|
+
SalesPanel,
|
|
10094
10477
|
SearchInput,
|
|
10095
10478
|
SectionHeader,
|
|
10096
10479
|
Select,
|
|
@@ -10099,6 +10482,7 @@ export {
|
|
|
10099
10482
|
SelectTrigger,
|
|
10100
10483
|
SelectValue,
|
|
10101
10484
|
ShowcaseSection,
|
|
10485
|
+
Sidebar,
|
|
10102
10486
|
SidebarButton,
|
|
10103
10487
|
SidebarProvider,
|
|
10104
10488
|
Skeleton,
|
|
@@ -10134,7 +10518,10 @@ export {
|
|
|
10134
10518
|
getAnimationClasses,
|
|
10135
10519
|
getChartHeight,
|
|
10136
10520
|
getContainerHeightClass,
|
|
10521
|
+
getIcon,
|
|
10522
|
+
getNavigationItems,
|
|
10137
10523
|
interactionVariants,
|
|
10524
|
+
isValidIcon,
|
|
10138
10525
|
legacyPatterns,
|
|
10139
10526
|
setGlobalAuthService,
|
|
10140
10527
|
tooltipContent,
|
|
@@ -10144,6 +10531,7 @@ export {
|
|
|
10144
10531
|
useCreateExample,
|
|
10145
10532
|
useDeleteExample,
|
|
10146
10533
|
useGetExample,
|
|
10534
|
+
useNavigation,
|
|
10147
10535
|
usePermissions,
|
|
10148
10536
|
useSidebar,
|
|
10149
10537
|
useTextOverflow,
|