From 953b9098f88b62e7b77fd77525c9df0c20c93dfd Mon Sep 17 00:00:00 2001 From: Peter Vachon Date: Tue, 27 Jan 2026 16:21:16 -0500 Subject: [PATCH] chore(repo): create branch for layout collaboration This branch tests the process for creating and contributing new page layouts. Initial PR creates a unique link to share with team members for feedback and collaboration. --- apps/apollo-vertex/app/_meta.ts | 1 + .../app/page-layouts/dashboard/page.mdx | 15 + .../app/page-layouts/detail/page.mdx | 15 + .../app/page-layouts/list/page.mdx | 15 + apps/apollo-vertex/next-env.d.ts | 2 +- .../registry/dropdown-menu/dropdown-menu.tsx | 2 +- apps/apollo-vertex/registry/select/select.tsx | 2 +- .../registry/shell/internal/shell-layout.tsx | 111 +- .../templates/DashboardLayoutTemplate.tsx | 1021 +++++++++++++++++ .../templates/DetailLayoutTemplate.tsx | 18 + .../templates/ListLayoutTemplate.tsx | 18 + .../templates/components/DonutChart.tsx | 81 ++ .../components/FullscreenShellToggle.tsx | 90 ++ .../templates/components/GradientBars.tsx | 35 + .../templates/components/Sparkline.tsx | 86 ++ .../templates/components/TrendChart.tsx | 79 ++ .../templates/components/UserActivityBars.tsx | 55 + package.json | 4 + pnpm-lock.yaml | 97 +- 19 files changed, 1707 insertions(+), 40 deletions(-) create mode 100644 apps/apollo-vertex/app/page-layouts/dashboard/page.mdx create mode 100644 apps/apollo-vertex/app/page-layouts/detail/page.mdx create mode 100644 apps/apollo-vertex/app/page-layouts/list/page.mdx create mode 100644 apps/apollo-vertex/templates/DashboardLayoutTemplate.tsx create mode 100644 apps/apollo-vertex/templates/DetailLayoutTemplate.tsx create mode 100644 apps/apollo-vertex/templates/ListLayoutTemplate.tsx create mode 100644 apps/apollo-vertex/templates/components/DonutChart.tsx create mode 100644 apps/apollo-vertex/templates/components/FullscreenShellToggle.tsx create mode 100644 apps/apollo-vertex/templates/components/GradientBars.tsx create mode 100644 apps/apollo-vertex/templates/components/Sparkline.tsx create mode 100644 apps/apollo-vertex/templates/components/TrendChart.tsx create mode 100644 apps/apollo-vertex/templates/components/UserActivityBars.tsx diff --git a/apps/apollo-vertex/app/_meta.ts b/apps/apollo-vertex/app/_meta.ts index 9f7d255e1..af2969e3b 100644 --- a/apps/apollo-vertex/app/_meta.ts +++ b/apps/apollo-vertex/app/_meta.ts @@ -4,4 +4,5 @@ export default { "shadcn-components": "Shadcn Components", "vertex-components": "Vertex Components", themes: "Themes", + "page-layouts": "Page Layouts", }; diff --git a/apps/apollo-vertex/app/page-layouts/dashboard/page.mdx b/apps/apollo-vertex/app/page-layouts/dashboard/page.mdx new file mode 100644 index 000000000..7f6d2a3d1 --- /dev/null +++ b/apps/apollo-vertex/app/page-layouts/dashboard/page.mdx @@ -0,0 +1,15 @@ +import { DashboardLayoutTemplate } from '@/templates/DashboardLayoutTemplate'; + +# Dashboard + +This is a blank dashboard page layout with the shell component. + +
+
+ +
+
+ +## Usage + +This page provides a starting point for building dashboard layouts within the Apollo Vertex design system. diff --git a/apps/apollo-vertex/app/page-layouts/detail/page.mdx b/apps/apollo-vertex/app/page-layouts/detail/page.mdx new file mode 100644 index 000000000..a056c743f --- /dev/null +++ b/apps/apollo-vertex/app/page-layouts/detail/page.mdx @@ -0,0 +1,15 @@ +import { DetailLayoutTemplate } from '@/templates/DetailLayoutTemplate'; + +# Detail + +This is a blank detail page layout with the shell component. + +
+
+ +
+
+ +## Usage + +This page provides a starting point for building detail view layouts within the Apollo Vertex design system. diff --git a/apps/apollo-vertex/app/page-layouts/list/page.mdx b/apps/apollo-vertex/app/page-layouts/list/page.mdx new file mode 100644 index 000000000..7b736786e --- /dev/null +++ b/apps/apollo-vertex/app/page-layouts/list/page.mdx @@ -0,0 +1,15 @@ +import { ListLayoutTemplate } from '@/templates/ListLayoutTemplate'; + +# List + +This is a blank list page layout with the shell component. + +
+
+ +
+
+ +## Usage + +This page provides a starting point for building list view layouts within the Apollo Vertex design system. diff --git a/apps/apollo-vertex/next-env.d.ts b/apps/apollo-vertex/next-env.d.ts index 9edff1c7c..c4b7818fb 100644 --- a/apps/apollo-vertex/next-env.d.ts +++ b/apps/apollo-vertex/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/apollo-vertex/registry/dropdown-menu/dropdown-menu.tsx b/apps/apollo-vertex/registry/dropdown-menu/dropdown-menu.tsx index 6daf947a3..6a8861112 100644 --- a/apps/apollo-vertex/registry/dropdown-menu/dropdown-menu.tsx +++ b/apps/apollo-vertex/registry/dropdown-menu/dropdown-menu.tsx @@ -42,7 +42,7 @@ function DropdownMenuContent({ data-slot="dropdown-menu-content" sideOffset={sideOffset} className={cn( - "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", + "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-[10001] max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", className, )} {...props} diff --git a/apps/apollo-vertex/registry/select/select.tsx b/apps/apollo-vertex/registry/select/select.tsx index d163ffe05..fb8536152 100644 --- a/apps/apollo-vertex/registry/select/select.tsx +++ b/apps/apollo-vertex/registry/select/select.tsx @@ -62,7 +62,7 @@ function SelectContent({ ) { return ( -
+
-
- -
+
+ {/* Gradient Background for flat mode */} + {backgroundMode === "flat" && } + + {/* Expressive Background Elements */} + {backgroundMode === "expressive" && ( + <> + {/* Bg container */} +
+ {/* Vector 210 */} +
+ + {/* Vector 212 */} +
+ + {/* Vector 211 */} +
+ + {/* Vector 213 */} +
+
+ + {/* CoverLayer */} +
+ + )} + +
{children}
diff --git a/apps/apollo-vertex/templates/DashboardLayoutTemplate.tsx b/apps/apollo-vertex/templates/DashboardLayoutTemplate.tsx new file mode 100644 index 000000000..c63577012 --- /dev/null +++ b/apps/apollo-vertex/templates/DashboardLayoutTemplate.tsx @@ -0,0 +1,1021 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { ShellLayout } from "@/registry/shell/internal/shell-layout"; +import { Card } from "@/registry/card/card"; +import { Button } from "@/registry/button/button"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/registry/select/select"; +import { + InputGroup, + InputGroupInput, + InputGroupAddon, + InputGroupButton, +} from "@/registry/input-group/input-group"; +import { Sparkles, Mic, Eye, EyeOff, ArrowUpRight, X, Loader2, AlertCircle, Inbox } from "lucide-react"; +import { FullscreenShellToggle } from "./components/FullscreenShellToggle"; +import { GradientBars } from "./components/GradientBars"; +import { UserActivityBars } from "./components/UserActivityBars"; +import { DonutChart } from "./components/DonutChart"; +import { Sparkline } from "./components/Sparkline"; +import { TrendChart } from "./components/TrendChart"; + +export function DashboardLayoutTemplate() { + const [backgroundMode, setBackgroundMode] = useState("flat"); + const [viewState, setViewState] = useState("default"); + const [showContent, setShowContent] = useState(true); + const [expandedCard, setExpandedCard] = useState(null); + const [isExpanding, setIsExpanding] = useState(false); + const [showLoadingOverlay, setShowLoadingOverlay] = useState(false); + const [fadingFromSkeleton, setFadingFromSkeleton] = useState(false); + const [isInputFocused, setIsInputFocused] = useState(false); + const [cardData, setCardData] = useState({ + totalDocs: 1247, + pending: 342, + approved: 891, + rejected: 14, + accuracyRate: 96.8, + processingSpeed: 1.2, + docsProcessed: 8942, + errorRate: 3.2, + qualityScore: 87, + targetScore: 95, + activeAlerts: 23, + criticalIssues: 5, + resolvedToday: 12, + complianceRate: 94, + }); + + const [heroContent, setHeroContent] = useState({ + greeting: "Good morning, Peter", + headline: "Automation up 5%, cycle time down 15% over the last 30 days", + subheadline: "12,120 invoices process with 77% touch-less automation and an average posting time of 2.8 days. SLA risk remains low", + }); + + const [cardTitles, setCardTitles] = useState({ + card1: "Document review status", + card2: "AI processing metrics", + card3: "Quality score trends", + card4: "Compliance alerts", + }); + + const [issueBreakdown, setIssueBreakdown] = useState([ + { label: "Missing documentation", percentage: 85, gradient: "#9875FF", shadowColor: "rgba(152, 117, 255, 0.4)" }, + { label: "Incomplete data fields", percentage: 65, gradient: "#D582E3", shadowColor: "rgba(213, 130, 227, 0.4)" }, + { label: "Quality check failures", percentage: 45, gradient: "#EE77A2", shadowColor: "rgba(238, 119, 162, 0.4)" }, + { label: "Processing delays", percentage: 60, gradient: "#FFC775", shadowColor: "rgba(255, 199, 117, 0.4)" }, + ]); + + const [extractionPerformance, setExtractionPerformance] = useState([ + { name: "Sarah Chen", avatar: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=32&h=32&fit=crop&crop=face", percentage: 98, color: "rgb(6, 182, 212)", shadowColor: "rgba(6, 182, 212, 0.4)" }, + { name: "Michael Rodriguez", avatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=32&h=32&fit=crop&crop=face", percentage: 96, color: "rgb(6, 182, 212)", shadowColor: "rgba(6, 182, 212, 0.4)" }, + { name: "Emily Johnson", avatar: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=32&h=32&fit=crop&crop=face", percentage: 94, color: "rgb(6, 182, 212)", shadowColor: "rgba(6, 182, 212, 0.4)" }, + { name: "David Park", avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=32&h=32&fit=crop&crop=face", percentage: 92, color: "rgb(6, 182, 212)", shadowColor: "rgba(6, 182, 212, 0.4)" }, + ]); + + const [qualityBreakdown, setQualityBreakdown] = useState([ + { label: "Document accuracy", percentage: 92, gradient: "linear-gradient(90deg, #06B6D4 0%, #3B82F6 100%)", shadowColor: "rgba(6, 182, 212, 0.4)" }, + { label: "Processing speed", percentage: 88, gradient: "linear-gradient(90deg, #3B82F6 0%, #8B5CF6 100%)", shadowColor: "rgba(59, 130, 246, 0.4)" }, + { label: "SLA compliance", percentage: 85, gradient: "linear-gradient(90deg, #8B5CF6 0%, #A855F7 100%)", shadowColor: "rgba(139, 92, 246, 0.4)" }, + { label: "Exception rate", percentage: 82, gradient: "linear-gradient(90deg, #A855F7 0%, #D946EF 100%)", shadowColor: "rgba(168, 85, 247, 0.4)" }, + ]); + + const [alertBreakdown, setAlertBreakdown] = useState([ + { label: "Critical - Regulatory violations", percentage: 22, gradient: "#EF4444", shadowColor: "rgba(239, 68, 68, 0.4)" }, + { label: "High - Missing required docs", percentage: 35, gradient: "#F97316", shadowColor: "rgba(249, 115, 22, 0.4)" }, + { label: "Medium - Data inconsistencies", percentage: 52, gradient: "#F59E0B", shadowColor: "rgba(245, 158, 11, 0.4)" }, + { label: "Low - Minor discrepancies", percentage: 78, gradient: "#FBBF24", shadowColor: "rgba(251, 191, 36, 0.4)" }, + ]); + + const generateNewData = () => { + const automationChange = Math.floor(Math.random() * 10) - 2; // -2 to +7 + const cycleChange = Math.floor(Math.random() * 20) + 5; // 5 to 24 + const invoiceCount = Math.floor(Math.random() * 5000) + 10000; + const touchlessPercent = Math.floor(Math.random() * 15) + 70; + const postingTime = (Math.random() * 2 + 1.5).toFixed(1); + + const greetings = ["Good morning, Peter", "Hello Peter", "Welcome back, Peter", "Good afternoon, Peter"]; + const randomGreeting = greetings[Math.floor(Math.random() * greetings.length)]; + + setHeroContent({ + greeting: randomGreeting, + headline: `Automation ${automationChange >= 0 ? 'up' : 'down'} ${Math.abs(automationChange)}%, cycle time ${cycleChange >= 0 ? 'down' : 'up'} ${Math.abs(cycleChange)}% over the last 30 days`, + subheadline: `${invoiceCount.toLocaleString()} invoices processed with ${touchlessPercent}% touch-less automation and an average posting time of ${postingTime} days. SLA risk remains low`, + }); + + const titleOptions = { + card1: ["Document review status", "Processing overview", "Review analytics", "Document insights"], + card2: ["AI processing metrics", "AI performance", "Machine learning stats", "Automation metrics"], + card3: ["Quality score trends", "Performance indicators", "Quality metrics", "Operational excellence"], + card4: ["Compliance alerts", "Risk monitoring", "Compliance dashboard", "Regulatory status"], + }; + + setCardTitles({ + card1: titleOptions.card1[Math.floor(Math.random() * titleOptions.card1.length)], + card2: titleOptions.card2[Math.floor(Math.random() * titleOptions.card2.length)], + card3: titleOptions.card3[Math.floor(Math.random() * titleOptions.card3.length)], + card4: titleOptions.card4[Math.floor(Math.random() * titleOptions.card4.length)], + }); + + setCardData({ + totalDocs: Math.floor(Math.random() * 500) + 1000, + pending: Math.floor(Math.random() * 200) + 250, + approved: Math.floor(Math.random() * 300) + 700, + rejected: Math.floor(Math.random() * 30) + 5, + accuracyRate: Math.floor((Math.random() * 5 + 94) * 10) / 10, + processingSpeed: Math.floor((Math.random() * 1 + 0.8) * 10) / 10, + docsProcessed: Math.floor(Math.random() * 2000) + 7500, + errorRate: Math.floor((Math.random() * 3 + 2) * 10) / 10, + qualityScore: Math.floor(Math.random() * 15) + 80, + targetScore: 95, + activeAlerts: Math.floor(Math.random() * 20) + 15, + criticalIssues: Math.floor(Math.random() * 8) + 2, + resolvedToday: Math.floor(Math.random() * 15) + 5, + complianceRate: Math.floor(Math.random() * 8) + 90, + }); + + // Generate new issue breakdown percentages + setIssueBreakdown([ + { label: "Missing documentation", percentage: Math.floor(Math.random() * 30) + 65, gradient: "#9875FF", shadowColor: "rgba(152, 117, 255, 0.4)" }, + { label: "Incomplete data fields", percentage: Math.floor(Math.random() * 30) + 50, gradient: "#D582E3", shadowColor: "rgba(213, 130, 227, 0.4)" }, + { label: "Quality check failures", percentage: Math.floor(Math.random() * 30) + 30, gradient: "#EE77A2", shadowColor: "rgba(238, 119, 162, 0.4)" }, + { label: "Processing delays", percentage: Math.floor(Math.random() * 30) + 45, gradient: "#FFC775", shadowColor: "rgba(255, 199, 117, 0.4)" }, + ]); + + // Generate new extraction performance with random avatars and names + const avatars = [ + "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=32&h=32&fit=crop&crop=face", + "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=32&h=32&fit=crop&crop=face", + "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=32&h=32&fit=crop&crop=face", + "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=32&h=32&fit=crop&crop=face", + "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=32&h=32&fit=crop&crop=face", + "https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=32&h=32&fit=crop&crop=face", + "https://images.unsplash.com/photo-1539571696357-5a69c17a67c6?w=32&h=32&fit=crop&crop=face", + "https://images.unsplash.com/photo-1524504388940-b1c1722653e1?w=32&h=32&fit=crop&crop=face", + ]; + const shuffledAvatars = [...avatars].sort(() => Math.random() - 0.5); + + const firstNames = ["Sarah", "Michael", "Emily", "David", "Jessica", "James", "Ashley", "Christopher", "Amanda", "Daniel", "Olivia", "Matthew", "Sophia", "Andrew", "Emma", "Joshua", "Isabella", "Ryan", "Mia", "Brandon"]; + const lastNames = ["Chen", "Rodriguez", "Johnson", "Park", "Williams", "Garcia", "Martinez", "Davis", "Miller", "Anderson", "Taylor", "Thomas", "Moore", "Jackson", "Martin", "Lee", "Thompson", "White", "Harris", "Clark"]; + + const generateRandomName = () => { + const firstName = firstNames[Math.floor(Math.random() * firstNames.length)]; + const lastName = lastNames[Math.floor(Math.random() * lastNames.length)]; + return `${firstName} ${lastName}`; + }; + + setExtractionPerformance([ + { name: generateRandomName(), avatar: shuffledAvatars[0], percentage: Math.floor(Math.random() * 8) + 92, color: "rgb(6, 182, 212)", shadowColor: "rgba(6, 182, 212, 0.4)" }, + { name: generateRandomName(), avatar: shuffledAvatars[1], percentage: Math.floor(Math.random() * 8) + 90, color: "rgb(6, 182, 212)", shadowColor: "rgba(6, 182, 212, 0.4)" }, + { name: generateRandomName(), avatar: shuffledAvatars[2], percentage: Math.floor(Math.random() * 8) + 88, color: "rgb(6, 182, 212)", shadowColor: "rgba(6, 182, 212, 0.4)" }, + { name: generateRandomName(), avatar: shuffledAvatars[3], percentage: Math.floor(Math.random() * 8) + 86, color: "rgb(6, 182, 212)", shadowColor: "rgba(6, 182, 212, 0.4)" }, + ]); + + // Generate new quality breakdown + setQualityBreakdown([ + { label: "Document accuracy", percentage: Math.floor(Math.random() * 15) + 85, gradient: "linear-gradient(90deg, #06B6D4 0%, #3B82F6 100%)", shadowColor: "rgba(6, 182, 212, 0.4)" }, + { label: "Processing speed", percentage: Math.floor(Math.random() * 15) + 80, gradient: "linear-gradient(90deg, #3B82F6 0%, #8B5CF6 100%)", shadowColor: "rgba(59, 130, 246, 0.4)" }, + { label: "SLA compliance", percentage: Math.floor(Math.random() * 15) + 78, gradient: "linear-gradient(90deg, #8B5CF6 0%, #A855F7 100%)", shadowColor: "rgba(139, 92, 246, 0.4)" }, + { label: "Exception rate", percentage: Math.floor(Math.random() * 15) + 75, gradient: "linear-gradient(90deg, #A855F7 0%, #D946EF 100%)", shadowColor: "rgba(168, 85, 247, 0.4)" }, + ]); + + // Generate new alert breakdown + setAlertBreakdown([ + { label: "Critical - Regulatory violations", percentage: Math.floor(Math.random() * 20) + 15, gradient: "#EF4444", shadowColor: "rgba(239, 68, 68, 0.4)" }, + { label: "High - Missing required docs", percentage: Math.floor(Math.random() * 20) + 25, gradient: "#F97316", shadowColor: "rgba(249, 115, 22, 0.4)" }, + { label: "Medium - Data inconsistencies", percentage: Math.floor(Math.random() * 20) + 40, gradient: "#F59E0B", shadowColor: "rgba(245, 158, 11, 0.4)" }, + { label: "Low - Minor discrepancies", percentage: Math.floor(Math.random() * 20) + 65, gradient: "#FBBF24", shadowColor: "rgba(251, 191, 36, 0.4)" }, + ]); + }; + + useEffect(() => { + if (expandedCard) { + // Wait for other cards to fade out before expanding + const timer = setTimeout(() => setIsExpanding(true), 50); + return () => clearTimeout(timer); + } else { + setIsExpanding(false); + } + }, [expandedCard]); + + useEffect(() => { + if (viewState === 'loading' || viewState === 'skeleton') { + setShowLoadingOverlay(true); + setFadingFromSkeleton(false); + const timer = setTimeout(() => { + setShowLoadingOverlay(false); + if (viewState === 'skeleton') { + setFadingFromSkeleton(true); + setTimeout(() => setViewState('default'), 10); + } else { + setViewState('default'); + } + }, 3000); + return () => clearTimeout(timer); + } else { + setShowLoadingOverlay(false); + } + }, [viewState]); + + return ( + + + +
+ {/* Background Elements for Default Mode */} + {backgroundMode === "default" && ( + <> +
+ {/* Ellipse 744 - Dark Blue */} +
+ {/* Ellipse 743 - Gray */} +
+ {/* Ellipse 742 - Cyan */} +
+ {/* Ellipse 745 - Light Cyan */} +
+
+ {/* Dampening overlay */} +
+ + )} + + {/* Expressive Background Elements on Input Focus */} +
+ {/* Vector 210 */} +
+ + {/* Vector 212 */} +
+ + {/* Vector 211 */} +
+ + {/* Vector 213 */} +
+
+ + {/* CoverLayer for Input Focus */} +
+ + {/* Header Section */} +
+
+

+ UiPathâ„¢ Vertical Solutions +

+

Loan QC Management

+
+
+ + + +
+ +
+
+
+ + {/* Dashboard Grid */} +
+ {/* Loading Component */} + {viewState === 'loading' && ( +
+ +

Loading dashboard data...

+
+ )} + + {/* Error Component */} + {viewState === 'error' && ( +
+ +
+

Failed to load dashboard data

+

An error occurred while fetching the data

+
+ +
+ )} + + {/* Empty Component */} + {viewState === 'empty' && ( +
+ +
+

No data available

+

There's no dashboard data to display yet

+
+ +
+ )} + + {/* Expanded Card Overlay */} + {expandedCard && ( + + + + {expandedCard === 'card1' && ( +
+
+

+ {cardTitles.card1} +

+

+ Track completion rates and pending reviews across all loan documents +

+
+ +
+
+ Total documents + {cardData.totalDocs.toLocaleString()} + +12% this month +
+
+ Pending review + {cardData.pending} + -5% this week +
+
+ Approved + {cardData.approved} + +18% this month +
+
+ Rejected + {cardData.rejected} + No change +
+
+ +
+

Issue breakdown

+ +
+ +
+ + +
+
+ )} + + {expandedCard === 'card2' && ( +
+
+

+ {cardTitles.card2} +

+

+ Monitor extraction accuracy and processing speed for automated checks +

+
+ +
+
+ Accuracy rate + {cardData.accuracyRate}% + +2.3% this month +
+
+ Processing speed + {cardData.processingSpeed}s + -0.3s faster +
+
+ Documents processed + {cardData.docsProcessed.toLocaleString()} + +24% this month +
+
+ Error rate + {cardData.errorRate}% + Within target +
+
+ +
+

Extraction performance by field

+ +
+ +
+ + +
+
+ )} + + {expandedCard === 'card3' && ( +
+
+

+ {cardTitles.card3} +

+

+ Overall quality metrics and performance indicators across all processes +

+
+ +
+
+ Current score + {cardData.qualityScore} + +5 points +
+
+ Target score + {cardData.targetScore} + {cardData.targetScore - cardData.qualityScore} points to go +
+
+ Improvement + +12% + Last quarter +
+
+ Time to target + 2.8mo + Estimated +
+
+ +
+

Score breakdown by category

+ +
+ +
+ + +
+
+ )} + + {expandedCard === 'card4' && ( +
+
+

+ {cardTitles.card4} +

+

+ Monitor regulatory compliance and critical alerts requiring attention +

+
+ +
+
+ Active alerts + {cardData.activeAlerts} + 3 new today +
+
+ Critical issues + {cardData.criticalIssues} + Needs attention +
+
+ Resolved today + {cardData.resolvedToday} + Above average +
+
+ Compliance rate + {cardData.complianceRate}% + +3% this week +
+
+ +
+

Alert breakdown by severity

+ +
+ +
+ + +
+
+ )} +
+ )} + + {/* Hero Card + Chat Input - Left Side */} +
+ {/* Hero Card */} + + {viewState === 'skeleton' ? ( + <> +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + ) : ( +
+ +

+ {heroContent.greeting} +

+
+

+ {heroContent.headline} +

+

+ {heroContent.subheadline} +

+
+
+ +
+ )} + + + {/* AI Chat Input */} + + setIsInputFocused(true)} + onBlur={() => setIsInputFocused(false)} + /> + + + + + + + + + +
+ + {/* Top Right Card 1 */} + setExpandedCard(expandedCard === 'card1' ? null : 'card1')} + className={`lg:min-h-[200px] gap-1 rounded-md border-0 p-6 shadow-none cursor-pointer group ${ + backgroundMode === "default" + ? "bg-primary-foreground/40 hover:bg-primary-foreground/50" + : "bg-accent-foreground/6 hover:bg-accent-foreground/9" + }`} + style={{ + opacity: (viewState === 'error' || viewState === 'empty') ? 0 : (viewState === 'skeleton' ? 1 : (showContent && !showLoadingOverlay ? (expandedCard ? 0 : 1) : 0)), + transform: viewState === 'skeleton' ? 'translateY(0)' : (showContent ? (showLoadingOverlay ? 'translateY(16px)' : 'translateY(0)') : 'translateY(16px)'), + transition: viewState === 'skeleton' ? 'none' : `opacity ${fadingFromSkeleton ? '150ms' : '400ms'} ease-in-out ${fadingFromSkeleton ? '50ms' : '25ms'}, transform ${fadingFromSkeleton ? '150ms' : '400ms'} ease-in-out ${fadingFromSkeleton ? '50ms' : '25ms'}`, + }} + > + + {viewState === 'skeleton' ? ( + <> +
+
+
+ {[1, 2, 3, 4].map((i) => ( +
+
+
+
+
+
+
+
+
+ ))} +
+ + ) : ( +
+

+ {cardTitles.card1} +

+

+ Track completion rates and pending reviews across all loan documents +

+ +
+ )} + + + {/* Top Right Card 2 */} + setExpandedCard(expandedCard === 'card2' ? null : 'card2')} + className={`lg:min-h-[200px] gap-1 rounded-md border-0 p-6 shadow-none cursor-pointer group ${ + backgroundMode === "default" + ? "bg-primary-foreground/40 hover:bg-primary-foreground/50" + : "bg-accent-foreground/6 hover:bg-accent-foreground/9" + }`} + style={{ + opacity: (viewState === 'error' || viewState === 'empty') ? 0 : (viewState === 'skeleton' ? 1 : (showContent && !showLoadingOverlay ? (expandedCard ? 0 : 1) : 0)), + transform: viewState === 'skeleton' ? 'translateY(0)' : (showContent ? (showLoadingOverlay ? 'translateY(16px)' : 'translateY(0)') : 'translateY(16px)'), + transition: viewState === 'skeleton' ? 'none' : `opacity ${fadingFromSkeleton ? '150ms' : '400ms'} ease-in-out ${fadingFromSkeleton ? '100ms' : '50ms'}, transform ${fadingFromSkeleton ? '150ms' : '400ms'} ease-in-out ${fadingFromSkeleton ? '100ms' : '50ms'}`, + }} + > + + {viewState === 'skeleton' ? ( + <> +
+
+
+ {[1, 2, 3, 4].map((i) => ( +
+
+
+
+
+
+
+
+
+
+
+
+ ))} +
+ + ) : ( +
+

+ {cardTitles.card2} +

+

+ Monitor extraction accuracy and processing speed for automated checks +

+ +
+ )} + + + {/* Bottom Right Card 1 */} + setExpandedCard(expandedCard === 'card3' ? null : 'card3')} + className={`lg:min-h-[200px] gap-1 rounded-md border-0 p-6 pb-4 shadow-none flex flex-col cursor-pointer group ${ + backgroundMode === "default" + ? "bg-primary-foreground/40 hover:bg-primary-foreground/50" + : "bg-accent-foreground/6 hover:bg-accent-foreground/9" + }`} + style={{ + opacity: (viewState === 'error' || viewState === 'empty') ? 0 : (viewState === 'skeleton' ? 1 : (showContent && !showLoadingOverlay ? (expandedCard ? 0 : 1) : 0)), + transform: viewState === 'skeleton' ? 'translateY(0)' : (showContent ? (showLoadingOverlay ? 'translateY(16px)' : 'translateY(0)') : 'translateY(16px)'), + transition: viewState === 'skeleton' ? 'none' : `opacity ${fadingFromSkeleton ? '150ms' : '400ms'} ease-in-out ${fadingFromSkeleton ? '150ms' : '75ms'}, transform ${fadingFromSkeleton ? '150ms' : '400ms'} ease-in-out ${fadingFromSkeleton ? '150ms' : '75ms'}`, + }} + > + + {viewState === 'skeleton' ? ( + <> +
+
+
+
+
+
+
+
+
+
+ + ) : ( +
+

+ {cardTitles.card3} +

+
+ +
+ )} + + + {/* Bottom Right Card 2 */} + setExpandedCard(expandedCard === 'card4' ? null : 'card4')} + className={`lg:min-h-[200px] gap-1 rounded-md border-0 p-6 pb-4 shadow-none flex flex-col cursor-pointer group ${ + backgroundMode === "default" + ? "bg-primary-foreground/40 hover:bg-primary-foreground/50" + : "bg-accent-foreground/6 hover:bg-accent-foreground/9" + }`} + style={{ + opacity: (viewState === 'error' || viewState === 'empty') ? 0 : (viewState === 'skeleton' ? 1 : (showContent && !showLoadingOverlay ? (expandedCard ? 0 : 1) : 0)), + transform: viewState === 'skeleton' ? 'translateY(0)' : (showContent ? (showLoadingOverlay ? 'translateY(16px)' : 'translateY(0)') : 'translateY(16px)'), + transition: viewState === 'skeleton' ? 'none' : `opacity ${fadingFromSkeleton ? '150ms' : '400ms'} ease-in-out ${fadingFromSkeleton ? '200ms' : '100ms'}, transform ${fadingFromSkeleton ? '150ms' : '400ms'} ease-in-out ${fadingFromSkeleton ? '200ms' : '100ms'}`, + }} + > + + {viewState === 'skeleton' ? ( + <> +
+
+
+
+
+
+
+ + ) : ( +
+

+ {cardTitles.card4} +

+
+ +
+ )} + +
+
+ + ); +} diff --git a/apps/apollo-vertex/templates/DetailLayoutTemplate.tsx b/apps/apollo-vertex/templates/DetailLayoutTemplate.tsx new file mode 100644 index 000000000..e620ec192 --- /dev/null +++ b/apps/apollo-vertex/templates/DetailLayoutTemplate.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { ShellLayout } from "@/registry/shell/internal/shell-layout"; +import { FullscreenShellToggle } from "./components/FullscreenShellToggle"; + +export function DetailLayoutTemplate() { + return ( + + +
+

Detail

+

+ This is a blank detail layout. Use this area to build your detail page. +

+
+
+ ); +} diff --git a/apps/apollo-vertex/templates/ListLayoutTemplate.tsx b/apps/apollo-vertex/templates/ListLayoutTemplate.tsx new file mode 100644 index 000000000..7f478cfb3 --- /dev/null +++ b/apps/apollo-vertex/templates/ListLayoutTemplate.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { ShellLayout } from "@/registry/shell/internal/shell-layout"; +import { FullscreenShellToggle } from "./components/FullscreenShellToggle"; + +export function ListLayoutTemplate() { + return ( + + +
+

List

+

+ This is a blank list layout. Use this area to build your list page. +

+
+
+ ); +} diff --git a/apps/apollo-vertex/templates/components/DonutChart.tsx b/apps/apollo-vertex/templates/components/DonutChart.tsx new file mode 100644 index 000000000..d6be712ad --- /dev/null +++ b/apps/apollo-vertex/templates/components/DonutChart.tsx @@ -0,0 +1,81 @@ +interface DonutChartProps { + value: number; + total: number; + description?: string; +} + +export function DonutChart({ value, total, description }: DonutChartProps) { + const percentage = (value / total) * 100; + const radius = 80; + const strokeWidth = 12; + const normalizedRadius = radius - strokeWidth / 2; + const circumference = normalizedRadius * 2 * Math.PI; + const strokeDashoffset = circumference - (percentage / 100) * circumference; + + return ( +
+
+ + {/* Define gradient */} + + + + + + + + + + + + + + + + + + + {/* Background circle (gray segment) */} + + + {/* Progress circle (gradient) */} + + + + {/* Center text */} +
+
+
+ {Math.round(percentage)}% +
+
+
+
+ + {description && ( +

+ {description} +

+ )} +
+ ); +} diff --git a/apps/apollo-vertex/templates/components/FullscreenShellToggle.tsx b/apps/apollo-vertex/templates/components/FullscreenShellToggle.tsx new file mode 100644 index 000000000..dca5fdfa9 --- /dev/null +++ b/apps/apollo-vertex/templates/components/FullscreenShellToggle.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { useState, useEffect, useRef } from "react"; +import { Button } from "@/registry/button/button"; + +export function FullscreenShellToggle() { + const [isFullscreen, setIsFullscreen] = useState(false); + const containerRef = useRef(null); + + useEffect(() => { + if (!containerRef.current) return; + + const container = containerRef.current; + const shellElement = container.closest('[class*="h-screen"]')?.parentElement; + + if (isFullscreen && shellElement) { + shellElement.style.position = "fixed"; + shellElement.style.top = "0"; + shellElement.style.left = "0"; + shellElement.style.right = "0"; + shellElement.style.bottom = "0"; + shellElement.style.width = "100vw"; + shellElement.style.height = "100vh"; + shellElement.style.zIndex = "9999"; + shellElement.style.backgroundColor = "var(--background)"; + } else if (!isFullscreen && shellElement) { + shellElement.style.position = ""; + shellElement.style.top = ""; + shellElement.style.left = ""; + shellElement.style.right = ""; + shellElement.style.bottom = ""; + shellElement.style.width = ""; + shellElement.style.height = ""; + shellElement.style.zIndex = ""; + shellElement.style.backgroundColor = ""; + } + }, [isFullscreen]); + + const toggleFullscreen = () => { + setIsFullscreen(!isFullscreen); + }; + + return ( + <> +
+ + + ); +} diff --git a/apps/apollo-vertex/templates/components/GradientBars.tsx b/apps/apollo-vertex/templates/components/GradientBars.tsx new file mode 100644 index 000000000..88145038e --- /dev/null +++ b/apps/apollo-vertex/templates/components/GradientBars.tsx @@ -0,0 +1,35 @@ +interface BarData { + label: string; + percentage: number; + gradient: string; + shadowColor: string; +} + +interface GradientBarsProps { + bars: BarData[]; +} + +export function GradientBars({ bars }: GradientBarsProps) { + return ( +
+ {bars.map((bar, index) => ( +
+
+ {bar.label} + {bar.percentage}% +
+
+
+
+
+ ))} +
+ ); +} diff --git a/apps/apollo-vertex/templates/components/Sparkline.tsx b/apps/apollo-vertex/templates/components/Sparkline.tsx new file mode 100644 index 000000000..0e1db3dbe --- /dev/null +++ b/apps/apollo-vertex/templates/components/Sparkline.tsx @@ -0,0 +1,86 @@ +interface SparklineProps { + data: number[]; + value?: string; + unit?: string; + description?: string; +} + +export function Sparkline({ data, value, unit, description }: SparklineProps) { + const width = 400; + const height = 120; + const padding = 10; + + // Calculate min and max for scaling + const min = Math.min(...data); + const max = Math.max(...data); + const range = max - min; + + // Create points for the path + const points = data.map((value, index) => { + const x = (index / (data.length - 1)) * (width - padding * 2) + padding; + const y = height - padding - ((value - min) / range) * (height - padding * 2); + return { x, y }; + }); + + // Create smooth curve using quadratic bezier curves + const pathData = points.reduce((path, point, index) => { + if (index === 0) { + return `M ${point.x} ${point.y}`; + } + const prevPoint = points[index - 1]; + const midX = (prevPoint.x + point.x) / 2; + return `${path} Q ${prevPoint.x} ${prevPoint.y}, ${midX} ${(prevPoint.y + point.y) / 2} Q ${point.x} ${point.y}, ${point.x} ${point.y}`; + }, ""); + + return ( +
+
+ + + + + + + + + + + + + + {/* Area fill */} + + + {/* Line */} + + +
+ +
+ {value && unit && ( +
+ {value} + {unit} +
+ )} + + {description && ( +

+ {description} +

+ )} +
+
+ ); +} diff --git a/apps/apollo-vertex/templates/components/TrendChart.tsx b/apps/apollo-vertex/templates/components/TrendChart.tsx new file mode 100644 index 000000000..0749d7bcf --- /dev/null +++ b/apps/apollo-vertex/templates/components/TrendChart.tsx @@ -0,0 +1,79 @@ +"use client"; + +interface TrendChartProps { + data: number[]; + startLabel?: string; + endLabel?: string; + startValue?: string; + endValue?: string; + subtitle?: string; +} + +export function TrendChart({ + data, + startLabel = "30 days ago", + endLabel = "Today", + startValue, + endValue, + subtitle, +}: TrendChartProps) { + const width = 800; + const height = 180; + const padding = 20; + + // Calculate min and max for scaling + const min = Math.min(...data); + const max = Math.max(...data); + const range = max - min; + + // Create points for the path + const points = data.map((value, index) => { + const x = (index / (data.length - 1)) * (width - padding * 2) + padding; + const y = height - padding - ((value - min) / range) * (height - padding * 2); + return { x, y }; + }); + + // Create smooth curve + const pathData = points.reduce((path, point, index) => { + if (index === 0) { + return `M ${point.x} ${point.y}`; + } + const prevPoint = points[index - 1]; + const midX = (prevPoint.x + point.x) / 2; + return `${path} Q ${prevPoint.x} ${prevPoint.y}, ${midX} ${(prevPoint.y + point.y) / 2} Q ${point.x} ${point.y}, ${point.x} ${point.y}`; + }, ""); + + return ( +
+
+ + {/* Line */} + + +
+ + {/* Bottom labels */} +
+ {startLabel} + {endLabel} +
+ + {subtitle && ( +

+ {subtitle} +

+ )} + +

+ Target 80% automation +

+
+ ); +} diff --git a/apps/apollo-vertex/templates/components/UserActivityBars.tsx b/apps/apollo-vertex/templates/components/UserActivityBars.tsx new file mode 100644 index 000000000..68b73b821 --- /dev/null +++ b/apps/apollo-vertex/templates/components/UserActivityBars.tsx @@ -0,0 +1,55 @@ +interface UserActivity { + name: string; + avatar?: string; + initials?: string; + percentage: number; + color: string; + shadowColor: string; +} + +interface UserActivityBarsProps { + users: UserActivity[]; +} + +export function UserActivityBars({ users }: UserActivityBarsProps) { + return ( +
+ {users.map((user, index) => ( +
+ {/* Avatar */} + {user.avatar ? ( + {user.name} + ) : ( +
+ + {user.initials} + +
+ )} + + {/* Bar Section */} +
+
+ {user.name} + {user.percentage}% +
+
+
+
+
+
+ ))} +
+ ); +} diff --git a/package.json b/package.json index 76bb24114..dd78b70bf 100644 --- a/package.json +++ b/package.json @@ -99,5 +99,9 @@ "lodash": "^4.17.23", "lodash-es": "^4.17.23" } + }, + "dependencies": { + "chart.js": "^4.5.1", + "react-chartjs-2": "^5.3.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4d0624a2d..cdf4dec39 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,6 +19,13 @@ overrides: importers: .: + dependencies: + chart.js: + specifier: ^4.5.1 + version: 4.5.1 + react-chartjs-2: + specifier: ^5.3.1 + version: 5.3.1(chart.js@4.5.1)(react@19.2.3) devDependencies: '@biomejs/biome': specifier: ^2.3.6 @@ -134,7 +141,7 @@ importers: devDependencies: '@angular-devkit/build-angular': specifier: ^20.3.8 - version: 20.3.11(9342b44c042909395d5f7e6c73c2bb9d) + version: 20.3.11(@angular/compiler-cli@19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3))(@angular/compiler@19.2.18)(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.18(@angular/animations@19.2.18(@angular/common@19.2.18(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.18(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1)))(@rspack/core@1.6.4(@swc/helpers@0.5.17))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(ng-packagr@20.3.2(@angular/compiler-cli@19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3))(tailwindcss@4.1.17)(tslib@2.8.1)(typescript@5.7.3))(tailwindcss@4.1.17)(tsx@4.20.6)(typescript@5.7.3)(vitest@4.0.14)(yaml@2.8.1) '@angular/cli': specifier: ^20.3.8 version: 20.3.11(@types/node@24.10.1)(chokidar@4.0.3)(hono@4.11.4) @@ -817,7 +824,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.5 - version: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.14(vitest@4.0.14))(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@27.3.0)(less@4.4.2)(lightningcss@1.30.2)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(sass@1.94.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1) + version: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@27.3.0)(less@4.4.2)(lightningcss@1.30.2)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(sass@1.94.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1) packages/apollo-wind: dependencies: @@ -1144,7 +1151,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.5 - version: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.14(vitest@4.0.14))(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@27.3.0)(less@4.4.2)(lightningcss@1.30.2)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(sass@1.94.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1) + version: 4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@27.3.0)(less@4.4.2)(lightningcss@1.30.2)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(sass@1.94.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1) packages: @@ -3580,6 +3587,9 @@ packages: '@keyv/serialize@1.1.1': resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + '@kurkle/color@0.3.4': + resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} + '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} @@ -7407,6 +7417,10 @@ packages: chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + chart.js@4.5.1: + resolution: {integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==} + engines: {pnpm: '>=8'} + check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -11704,6 +11718,12 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-chartjs-2@5.3.1: + resolution: {integrity: sha512-h5IPXKg9EXpjoBzUfyWJvllMjG2mQ4EiuHQFhms/AjUm0XSZHhyRy2xVmLXHKrtcdrPO4mnGqRtYoD0vp95A0A==} + peerDependencies: + chart.js: ^4.1.1 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-clientside-effect@1.2.8: resolution: {integrity: sha512-ma2FePH0z3px2+WOu6h+YycZcEvFmmxIlAb62cF52bG86eMySciO/EQZeQMXd07kPCYB0a1dWDT5J+KE9mCDUw==} peerDependencies: @@ -14133,13 +14153,13 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@20.3.11(9342b44c042909395d5f7e6c73c2bb9d)': + '@angular-devkit/build-angular@20.3.11(@angular/compiler-cli@19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3))(@angular/compiler@19.2.18)(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.18(@angular/animations@19.2.18(@angular/common@19.2.18(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.18(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1)))(@rspack/core@1.6.4(@swc/helpers@0.5.17))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(ng-packagr@20.3.2(@angular/compiler-cli@19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3))(tailwindcss@4.1.17)(tslib@2.8.1)(typescript@5.7.3))(tailwindcss@4.1.17)(tsx@4.20.6)(typescript@5.7.3)(vitest@4.0.14)(yaml@2.8.1)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2003.11(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.2003.11(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.101.2))(webpack@5.101.2) + '@angular-devkit/build-webpack': 0.2003.11(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.101.2))(webpack@5.101.2(esbuild@0.25.9)) '@angular-devkit/core': 20.3.11(chokidar@4.0.3) - '@angular/build': 20.3.11(e10da3677be0d28a771e42718d565797) + '@angular/build': 20.3.11(@angular/compiler-cli@19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3))(@angular/compiler@19.2.18)(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.18(@angular/animations@19.2.18(@angular/common@19.2.18(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.18(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.0)(lightningcss@1.30.2)(ng-packagr@20.3.2(@angular/compiler-cli@19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3))(tailwindcss@4.1.17)(tslib@2.8.1)(typescript@5.7.3))(postcss@8.5.6)(tailwindcss@4.1.17)(terser@5.43.1)(tslib@2.8.1)(tsx@4.20.6)(typescript@5.7.3)(vitest@4.0.14)(yaml@2.8.1) '@angular/compiler-cli': 19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3) '@babel/core': 7.28.3 '@babel/generator': 7.28.3 @@ -14151,13 +14171,13 @@ snapshots: '@babel/preset-env': 7.28.3(@babel/core@7.28.3) '@babel/runtime': 7.28.3 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 20.3.11(@angular/compiler-cli@19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3))(typescript@5.7.3)(webpack@5.101.2) + '@ngtools/webpack': 20.3.11(@angular/compiler-cli@19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3))(typescript@5.7.3)(webpack@5.101.2(esbuild@0.25.9)) ansi-colors: 4.1.3 autoprefixer: 10.4.21(postcss@8.5.6) - babel-loader: 10.0.0(@babel/core@7.28.3)(webpack@5.101.2) + babel-loader: 10.0.0(@babel/core@7.28.3)(webpack@5.101.2(esbuild@0.25.9)) browserslist: 4.28.0 - copy-webpack-plugin: 13.0.1(webpack@5.101.2) - css-loader: 7.1.2(@rspack/core@1.6.4(@swc/helpers@0.5.17))(webpack@5.101.2) + copy-webpack-plugin: 13.0.1(webpack@5.101.2(esbuild@0.25.9)) + css-loader: 7.1.2(@rspack/core@1.6.4(@swc/helpers@0.5.17))(webpack@5.101.2(esbuild@0.25.9)) esbuild-wasm: 0.25.9 fast-glob: 3.3.3 http-proxy-middleware: 3.0.5 @@ -14165,22 +14185,22 @@ snapshots: jsonc-parser: 3.3.1 karma-source-map-support: 1.4.0 less: 4.4.0 - less-loader: 12.3.0(@rspack/core@1.6.4(@swc/helpers@0.5.17))(less@4.4.0)(webpack@5.101.2) - license-webpack-plugin: 4.0.2(webpack@5.101.2) + less-loader: 12.3.0(@rspack/core@1.6.4(@swc/helpers@0.5.17))(less@4.4.0)(webpack@5.101.2(esbuild@0.25.9)) + license-webpack-plugin: 4.0.2(webpack@5.101.2(esbuild@0.25.9)) loader-utils: 3.3.1 - mini-css-extract-plugin: 2.9.4(webpack@5.101.2) + mini-css-extract-plugin: 2.9.4(webpack@5.101.2(esbuild@0.25.9)) open: 10.2.0 ora: 8.2.0 picomatch: 4.0.3 piscina: 5.1.3 postcss: 8.5.6 - postcss-loader: 8.1.1(@rspack/core@1.6.4(@swc/helpers@0.5.17))(postcss@8.5.6)(typescript@5.7.3)(webpack@5.101.2) + postcss-loader: 8.1.1(@rspack/core@1.6.4(@swc/helpers@0.5.17))(postcss@8.5.6)(typescript@5.7.3)(webpack@5.101.2(esbuild@0.25.9)) resolve-url-loader: 5.0.0 rxjs: 7.8.2 sass: 1.90.0 - sass-loader: 16.0.5(@rspack/core@1.6.4(@swc/helpers@0.5.17))(sass@1.90.0)(webpack@5.101.2) + sass-loader: 16.0.5(@rspack/core@1.6.4(@swc/helpers@0.5.17))(sass@1.90.0)(webpack@5.101.2(esbuild@0.25.9)) semver: 7.7.2 - source-map-loader: 5.0.0(webpack@5.101.2) + source-map-loader: 5.0.0(webpack@5.101.2(esbuild@0.25.9)) source-map-support: 0.5.21 terser: 5.43.1 tree-kill: 1.2.2 @@ -14190,7 +14210,7 @@ snapshots: webpack-dev-middleware: 7.4.2(webpack@5.101.2) webpack-dev-server: 5.2.2(webpack@5.101.2) webpack-merge: 6.0.1 - webpack-subresource-integrity: 5.1.0(webpack@5.101.2) + webpack-subresource-integrity: 5.1.0(webpack@5.101.2(esbuild@0.25.9)) optionalDependencies: '@angular/core': 19.2.18(rxjs@7.8.2)(zone.js@0.15.1) '@angular/platform-browser': 19.2.18(@angular/animations@19.2.18(@angular/common@19.2.18(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.18(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1)) @@ -14220,7 +14240,7 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-webpack@0.2003.11(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.101.2))(webpack@5.101.2)': + '@angular-devkit/build-webpack@0.2003.11(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.101.2))(webpack@5.101.2(esbuild@0.25.9))': dependencies: '@angular-devkit/architect': 0.2003.11(chokidar@4.0.3) rxjs: 7.8.2 @@ -14256,7 +14276,7 @@ snapshots: '@angular/core': 19.2.18(rxjs@7.8.2)(zone.js@0.15.1) tslib: 2.8.1 - '@angular/build@20.3.11(e10da3677be0d28a771e42718d565797)': + '@angular/build@20.3.11(@angular/compiler-cli@19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3))(@angular/compiler@19.2.18)(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.18(@angular/animations@19.2.18(@angular/common@19.2.18(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.18(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.18(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@2.6.1)(less@4.4.0)(lightningcss@1.30.2)(ng-packagr@20.3.2(@angular/compiler-cli@19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3))(tailwindcss@4.1.17)(tslib@2.8.1)(typescript@5.7.3))(postcss@8.5.6)(tailwindcss@4.1.17)(terser@5.43.1)(tslib@2.8.1)(tsx@4.20.6)(typescript@5.7.3)(vitest@4.0.14)(yaml@2.8.1)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2003.11(chokidar@4.0.3) @@ -17068,6 +17088,8 @@ snapshots: '@keyv/serialize@1.1.1': {} + '@kurkle/color@0.3.4': {} + '@leichtgewicht/ip-codec@2.0.5': {} '@lingui/babel-plugin-extract-messages@5.6.1': {} @@ -17828,7 +17850,7 @@ snapshots: '@next/swc-win32-x64-msvc@16.1.1': optional: true - '@ngtools/webpack@20.3.11(@angular/compiler-cli@19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3))(typescript@5.7.3)(webpack@5.101.2)': + '@ngtools/webpack@20.3.11(@angular/compiler-cli@19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3))(typescript@5.7.3)(webpack@5.101.2(esbuild@0.25.9))': dependencies: '@angular/compiler-cli': 19.2.18(@angular/compiler@19.2.18)(typescript@5.7.3) typescript: 5.7.3 @@ -20689,7 +20711,7 @@ snapshots: obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.14(@types/node@22.19.3)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@27.3.0)(less@4.4.2)(lightningcss@1.30.2)(msw@2.12.3(@types/node@22.19.3)(typescript@5.9.3))(sass@1.94.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1) + vitest: 4.0.14(@types/node@24.10.1)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@27.3.0)(less@4.4.2)(lightningcss@1.30.2)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(sass@1.94.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -20815,7 +20837,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vitest: 4.0.14(@types/node@22.19.3)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@27.3.0)(less@4.4.2)(lightningcss@1.30.2)(msw@2.12.3(@types/node@22.19.3)(typescript@5.9.3))(sass@1.94.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1) + vitest: 4.0.14(@types/node@24.10.1)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@27.3.0)(less@4.4.2)(lightningcss@1.30.2)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(sass@1.94.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1) '@vitest/utils@3.2.4': dependencies: @@ -21171,7 +21193,7 @@ snapshots: axe-core@4.11.0: {} - babel-loader@10.0.0(@babel/core@7.28.3)(webpack@5.101.2): + babel-loader@10.0.0(@babel/core@7.28.3)(webpack@5.101.2(esbuild@0.25.9)): dependencies: '@babel/core': 7.28.3 find-up: 5.0.0 @@ -21428,6 +21450,10 @@ snapshots: chardet@2.1.1: {} + chart.js@4.5.1: + dependencies: + '@kurkle/color': 0.3.4 + check-error@2.1.1: {} chevrotain-allstar@0.3.1(chevrotain@11.0.3): @@ -21743,7 +21769,7 @@ snapshots: dependencies: is-what: 3.14.1 - copy-webpack-plugin@13.0.1(webpack@5.101.2): + copy-webpack-plugin@13.0.1(webpack@5.101.2(esbuild@0.25.9)): dependencies: glob-parent: 6.0.2 normalize-path: 3.0.0 @@ -21845,7 +21871,7 @@ snapshots: css-functions-list@3.2.3: {} - css-loader@7.1.2(@rspack/core@1.6.4(@swc/helpers@0.5.17))(webpack@5.101.2): + css-loader@7.1.2(@rspack/core@1.6.4(@swc/helpers@0.5.17))(webpack@5.101.2(esbuild@0.25.9)): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -24079,7 +24105,7 @@ snapshots: layout-base@2.0.1: {} - less-loader@12.3.0(@rspack/core@1.6.4(@swc/helpers@0.5.17))(less@4.4.0)(webpack@5.101.2): + less-loader@12.3.0(@rspack/core@1.6.4(@swc/helpers@0.5.17))(less@4.4.0)(webpack@5.101.2(esbuild@0.25.9)): dependencies: less: 4.4.0 optionalDependencies: @@ -24116,7 +24142,7 @@ snapshots: leven@3.1.0: {} - license-webpack-plugin@4.0.2(webpack@5.101.2): + license-webpack-plugin@4.0.2(webpack@5.101.2(esbuild@0.25.9)): dependencies: webpack-sources: 3.3.3 optionalDependencies: @@ -25012,7 +25038,7 @@ snapshots: min-indent@1.0.1: {} - mini-css-extract-plugin@2.9.4(webpack@5.101.2): + mini-css-extract-plugin@2.9.4(webpack@5.101.2(esbuild@0.25.9)): dependencies: schema-utils: 4.3.3 tapable: 2.3.0 @@ -25951,7 +25977,7 @@ snapshots: optionalDependencies: postcss: 8.5.6 - postcss-loader@8.1.1(@rspack/core@1.6.4(@swc/helpers@0.5.17))(postcss@8.5.6)(typescript@5.7.3)(webpack@5.101.2): + postcss-loader@8.1.1(@rspack/core@1.6.4(@swc/helpers@0.5.17))(postcss@8.5.6)(typescript@5.7.3)(webpack@5.101.2(esbuild@0.25.9)): dependencies: cosmiconfig: 9.0.0(typescript@5.7.3) jiti: 1.21.7 @@ -26348,6 +26374,11 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-chartjs-2@5.3.1(chart.js@4.5.1)(react@19.2.3): + dependencies: + chart.js: 4.5.1 + react: 19.2.3 + react-clientside-effect@1.2.8(react@19.2.3): dependencies: '@babel/runtime': 7.28.4 @@ -27101,7 +27132,7 @@ snapshots: parse-srcset: 1.0.2 postcss: 8.5.6 - sass-loader@16.0.5(@rspack/core@1.6.4(@swc/helpers@0.5.17))(sass@1.90.0)(webpack@5.101.2): + sass-loader@16.0.5(@rspack/core@1.6.4(@swc/helpers@0.5.17))(sass@1.90.0)(webpack@5.101.2(esbuild@0.25.9)): dependencies: neo-async: 2.6.2 optionalDependencies: @@ -27584,7 +27615,7 @@ snapshots: source-map-js@1.2.1: {} - source-map-loader@5.0.0(webpack@5.101.2): + source-map-loader@5.0.0(webpack@5.101.2(esbuild@0.25.9)): dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 @@ -28631,7 +28662,7 @@ snapshots: tsx: 4.20.6 yaml: 2.8.1 - vitest@4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.14(vitest@4.0.14))(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@27.3.0)(less@4.4.2)(lightningcss@1.30.2)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(sass@1.94.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1): + vitest@4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/ui@4.0.14)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@27.3.0)(less@4.4.2)(lightningcss@1.30.2)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(sass@1.94.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1): dependencies: '@vitest/expect': 4.0.12 '@vitest/mocker': 4.0.12(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(vite@6.4.1(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(lightningcss@1.30.2)(sass@1.94.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1)) @@ -28878,7 +28909,7 @@ snapshots: webpack-sources@3.3.3: {} - webpack-subresource-integrity@5.1.0(webpack@5.101.2): + webpack-subresource-integrity@5.1.0(webpack@5.101.2(esbuild@0.25.9)): dependencies: typed-assert: 1.0.9 webpack: 5.101.2(esbuild@0.25.9)