diff --git a/apps/apollo-vertex/app/globals.css b/apps/apollo-vertex/app/globals.css index 646f02405..b6ebbc8fb 100644 --- a/apps/apollo-vertex/app/globals.css +++ b/apps/apollo-vertex/app/globals.css @@ -1,4 +1,6 @@ @import 'tailwindcss'; +@source "../registry"; +@source "../templates"; /* Optional: import Nextra theme styles */ @import 'nextra-theme-docs/style.css'; diff --git a/apps/apollo-vertex/app/preview/_meta.ts b/apps/apollo-vertex/app/preview/_meta.ts new file mode 100644 index 000000000..e48eafb8a --- /dev/null +++ b/apps/apollo-vertex/app/preview/_meta.ts @@ -0,0 +1,3 @@ +export default { + "*": { display: "hidden" }, +}; diff --git a/apps/apollo-vertex/app/preview/dashboard-minimal/page.tsx b/apps/apollo-vertex/app/preview/dashboard-minimal/page.tsx new file mode 100644 index 000000000..deba5cb66 --- /dev/null +++ b/apps/apollo-vertex/app/preview/dashboard-minimal/page.tsx @@ -0,0 +1,19 @@ +"use client"; + +import dynamic from "next/dynamic"; + +const DashboardTemplate = dynamic( + () => + import("@/templates/dashboard/DashboardTemplate").then( + (mod) => mod.DashboardTemplate, + ), + { ssr: false }, +); + +export default function DashboardMinimalPreviewPage() { + return ( +
+ +
+ ); +} diff --git a/apps/apollo-vertex/app/preview/dashboard/page.tsx b/apps/apollo-vertex/app/preview/dashboard/page.tsx new file mode 100644 index 000000000..018db5cc2 --- /dev/null +++ b/apps/apollo-vertex/app/preview/dashboard/page.tsx @@ -0,0 +1,19 @@ +"use client"; + +import dynamic from "next/dynamic"; + +const DashboardTemplate = dynamic( + () => + import("@/templates/dashboard/DashboardTemplate").then( + (mod) => mod.DashboardTemplate, + ), + { ssr: false }, +); + +export default function DashboardPreviewPage() { + return ( +
+ +
+ ); +} diff --git a/apps/apollo-vertex/public/Autopilot_dark.svg b/apps/apollo-vertex/public/Autopilot_dark.svg new file mode 100644 index 000000000..5f35c08dc --- /dev/null +++ b/apps/apollo-vertex/public/Autopilot_dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/apollo-vertex/public/Autopilot_light.svg b/apps/apollo-vertex/public/Autopilot_light.svg new file mode 100644 index 000000000..4f28c9a53 --- /dev/null +++ b/apps/apollo-vertex/public/Autopilot_light.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/apollo-vertex/registry.json b/apps/apollo-vertex/registry.json index aa715781d..ac8939b6a 100644 --- a/apps/apollo-vertex/registry.json +++ b/apps/apollo-vertex/registry.json @@ -66,6 +66,16 @@ "color-sidebar-accent-foreground": "var(--sidebar-accent-foreground)", "color-sidebar-border": "var(--sidebar-border)", "color-sidebar-ring": "var(--sidebar-ring)", + "color-insight-50": "var(--insight-50)", + "color-insight-100": "var(--insight-100)", + "color-insight-200": "var(--insight-200)", + "color-insight-300": "var(--insight-300)", + "color-insight-400": "var(--insight-400)", + "color-insight-500": "var(--insight-500)", + "color-insight-600": "var(--insight-600)", + "color-insight-700": "var(--insight-700)", + "color-insight-800": "var(--insight-800)", + "color-insight-900": "var(--insight-900)", "font-sans": "var(--font-sans)" }, "light": { @@ -120,6 +130,19 @@ "sidebar-accent-foreground": "oklch(0.1660 0.0283 203.3380)", "sidebar-border": "oklch(0.9237 0.0133 262.3780)", "sidebar-ring": "oklch(0.64 0.115 208)", + "insight-50": "oklch(0.96 0.03 277)", + "insight-100": "oklch(0.92 0.05 277)", + "insight-200": "oklch(0.86 0.09 277)", + "insight-300": "oklch(0.78 0.14 277)", + "insight-400": "oklch(0.70 0.19 277)", + "insight-500": "oklch(0.62 0.22 277)", + "insight-600": "oklch(0.56 0.20 277)", + "insight-700": "oklch(0.48 0.17 277)", + "insight-800": "oklch(0.38 0.13 278)", + "insight-900": "oklch(0.30 0.10 278)", + "font-sans": "Inter, ui-sans-serif, sans-serif, system-ui", + "font-serif": "IBM Plex Serif, ui-serif, serif", + "font-mono": "IBM Plex Mono, ui-monospace, monospace", "radius": "0.625rem", "shadow-x": "0", "shadow-y": "0px", @@ -206,6 +229,19 @@ "sidebar-accent-foreground": "oklch(0.9525 0.0110 225.9830)", "sidebar-border": "oklch(0.9525 0.0110 225.9830)", "sidebar-ring": "oklch(0.69 0.112 207)", + "insight-50": "oklch(0.96 0.03 277)", + "insight-100": "oklch(0.92 0.05 277)", + "insight-200": "oklch(0.86 0.09 277)", + "insight-300": "oklch(0.78 0.14 277)", + "insight-400": "oklch(0.70 0.19 277)", + "insight-500": "oklch(0.62 0.22 277)", + "insight-600": "oklch(0.56 0.20 277)", + "insight-700": "oklch(0.48 0.17 277)", + "insight-800": "oklch(0.38 0.13 278)", + "insight-900": "oklch(0.30 0.10 278)", + "font-sans": "Inter, ui-sans-serif, sans-serif, system-ui", + "font-serif": "IBM Plex Serif, ui-serif, serif", + "font-mono": "IBM Plex Mono, ui-monospace, monospace", "radius": "0.625rem", "shadow-x": "0", "shadow-y": "0px", diff --git a/apps/apollo-vertex/registry/card/card.tsx b/apps/apollo-vertex/registry/card/card.tsx index 7bd13d2e1..f61a8ae2d 100644 --- a/apps/apollo-vertex/registry/card/card.tsx +++ b/apps/apollo-vertex/registry/card/card.tsx @@ -10,7 +10,7 @@ export const GLASS_CLASSES = [ "dark:shadow-[0_2px_24px_2px_rgba(0,0,0,0.12),inset_0_1px_0_0_color-mix(in_srgb,var(--sidebar)_5%,transparent)]", ] as const; -const cardVariants = cva("flex flex-col text-card-foreground", { +const cardVariants = cva("flex flex-col gap-6 py-6 text-card-foreground", { variants: { variant: { default: GLASS_CLASSES, diff --git a/apps/apollo-vertex/templates/dashboard/DashboardContent.tsx b/apps/apollo-vertex/templates/dashboard/DashboardContent.tsx new file mode 100644 index 000000000..70dd812bd --- /dev/null +++ b/apps/apollo-vertex/templates/dashboard/DashboardContent.tsx @@ -0,0 +1,6 @@ +"use client"; + +// Stub — full implementation is added in a later PR once all dependencies are available. +export function DashboardContent() { + return null; +} diff --git a/apps/apollo-vertex/templates/dashboard/DashboardDataProvider.tsx b/apps/apollo-vertex/templates/dashboard/DashboardDataProvider.tsx new file mode 100644 index 000000000..c29b620f7 --- /dev/null +++ b/apps/apollo-vertex/templates/dashboard/DashboardDataProvider.tsx @@ -0,0 +1,15 @@ +"use client"; + +import { useState, type ReactNode } from "react"; +import { ecommerceDataset, type DashboardDataset } from "./dashboard-data"; +import { DashboardDataContext } from "./dashboard-data-context"; + +export function DashboardDataProvider({ children }: { children: ReactNode }) { + const [data, setData] = useState(ecommerceDataset); + + return ( + + {children} + + ); +} diff --git a/apps/apollo-vertex/templates/dashboard/DashboardLoading.tsx b/apps/apollo-vertex/templates/dashboard/DashboardLoading.tsx new file mode 100644 index 000000000..c023f29d8 --- /dev/null +++ b/apps/apollo-vertex/templates/dashboard/DashboardLoading.tsx @@ -0,0 +1,135 @@ +"use client"; + +import { useEffect, useState } from "react"; + +type Phase = "logo" | "skeleton" | "done"; + +interface DashboardLoadingProps { + children: React.ReactNode; + triggerReplay?: number; +} + +function LogoPhase({ exiting }: { exiting: boolean }) { + return ( +
+ {/* Morphing glow */} +
+
+
+
+
+
+ + {/* App icon */} +
+ UiPath +
+ + {/* Loading text */} +

+ Creating your overview... +

+ + +
+ ); +} + +function SkeletonPhase({ exiting }: { exiting: boolean }) { + return ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ); +} + +export function DashboardLoading({ + children, + triggerReplay, +}: DashboardLoadingProps) { + const [phase, setPhase] = useState("done"); + const [exiting, setExiting] = useState(false); + + useEffect(() => { + if (triggerReplay === 0) return; + if (triggerReplay) { + setExiting(false); + setPhase("logo"); + } + }, [triggerReplay]); + + useEffect(() => { + if (phase === "done") return; + + if (phase === "logo") { + const timer = setTimeout(() => { + setExiting(true); + setTimeout(() => { + setExiting(false); + setPhase("skeleton"); + }, 500); + }, 2000); + return () => clearTimeout(timer); + } + + if (phase === "skeleton") { + const timer = setTimeout(() => { + setExiting(true); + setTimeout(() => { + setPhase("done"); + }, 500); + }, 1000); + return () => clearTimeout(timer); + } + }, [phase]); + + if (phase === "done") { + return ( +
{children}
+ ); + } + + return ( +
+ {phase === "logo" && } + {phase === "skeleton" && } +
+ ); +} diff --git a/apps/apollo-vertex/templates/dashboard/DashboardRoutes.tsx b/apps/apollo-vertex/templates/dashboard/DashboardRoutes.tsx new file mode 100644 index 000000000..30965402e --- /dev/null +++ b/apps/apollo-vertex/templates/dashboard/DashboardRoutes.tsx @@ -0,0 +1,59 @@ +import { createRootRoute, createRoute, Outlet } from "@tanstack/react-router"; +import { DashboardContent } from "./DashboardContent"; +import { DashboardShellWrapper } from "./DashboardShellWrapper"; + +export const dashboardRootRoute = createRootRoute(); + +// --- Sidebar variant routes --- + +export const dashboardShellRoute = createRoute({ + getParentRoute: () => dashboardRootRoute, + path: "/preview/dashboard", + component: () => ( + + + + ), +}); + +export const dashboardIndexRoute = createRoute({ + getParentRoute: () => dashboardShellRoute, + path: "/", + component: DashboardContent, +}); + +export const dashboardHomeRoute = createRoute({ + getParentRoute: () => dashboardShellRoute, + path: "/home", + component: DashboardContent, +}); + +export const dashboardCatchAllRoute = createRoute({ + getParentRoute: () => dashboardShellRoute, + path: "$", + component: DashboardContent, +}); + +// --- Minimal variant routes --- + +export const dashboardMinimalShellRoute = createRoute({ + getParentRoute: () => dashboardRootRoute, + path: "/preview/dashboard-minimal", + component: () => ( + + + + ), +}); + +export const dashboardMinimalIndexRoute = createRoute({ + getParentRoute: () => dashboardMinimalShellRoute, + path: "/", + component: DashboardContent, +}); + +export const dashboardMinimalCatchAllRoute = createRoute({ + getParentRoute: () => dashboardMinimalShellRoute, + path: "$", + component: DashboardContent, +}); diff --git a/apps/apollo-vertex/templates/dashboard/DashboardShellWrapper.tsx b/apps/apollo-vertex/templates/dashboard/DashboardShellWrapper.tsx new file mode 100644 index 000000000..95b838514 --- /dev/null +++ b/apps/apollo-vertex/templates/dashboard/DashboardShellWrapper.tsx @@ -0,0 +1,53 @@ +import type { ReactNode } from "react"; +import type { ShellNavItem } from "@/registry/shell/shell"; +import { ApolloShell } from "@/registry/shell/shell"; +import { SidebarProvider } from "@/components/ui/sidebar"; +import { BarChart3, FolderOpen, Home, Settings, Users } from "lucide-react"; + +const sidebarNavItems: ShellNavItem[] = [ + { path: "/preview/dashboard/home", label: "dashboard", icon: Home }, + { path: "/preview/dashboard/projects", label: "projects", icon: FolderOpen }, + { path: "/preview/dashboard/analytics", label: "analytics", icon: BarChart3 }, + { path: "/preview/dashboard/team", label: "team", icon: Users }, + { path: "/preview/dashboard/settings", label: "settings", icon: Settings }, +]; + +const minimalNavItems: ShellNavItem[] = [ + { path: "/preview/dashboard-minimal", label: "dashboard", icon: Home }, + { + path: "/preview/dashboard-minimal/projects", + label: "projects", + icon: FolderOpen, + }, + { + path: "/preview/dashboard-minimal/analytics", + label: "analytics", + icon: BarChart3, + }, +]; + +export function DashboardShellWrapper({ + variant, + children, +}: { + variant?: "minimal"; + children: ReactNode; +}) { + return ( + + + {children} + + + ); +} diff --git a/apps/apollo-vertex/templates/dashboard/DashboardTemplate.tsx b/apps/apollo-vertex/templates/dashboard/DashboardTemplate.tsx new file mode 100644 index 000000000..5e2d6ae00 --- /dev/null +++ b/apps/apollo-vertex/templates/dashboard/DashboardTemplate.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { + createMemoryHistory, + createRouter, + RouterProvider, +} from "@tanstack/react-router"; +import { useEffect, useState } from "react"; +import { + dashboardCatchAllRoute, + dashboardHomeRoute, + dashboardIndexRoute, + dashboardMinimalCatchAllRoute, + dashboardMinimalIndexRoute, + dashboardMinimalShellRoute, + dashboardRootRoute, + dashboardShellRoute, +} from "./DashboardRoutes"; + +export interface DashboardTemplateProps { + shellVariant?: "minimal"; +} + +const DASHBOARD_PREVIEW_PATH_KEY = "dashboard-preview-path"; +const DASHBOARD_MINIMAL_PREVIEW_PATH_KEY = "dashboard-minimal-preview-path"; + +type DashboardPreviewPathKey = + | typeof DASHBOARD_PREVIEW_PATH_KEY + | typeof DASHBOARD_MINIMAL_PREVIEW_PATH_KEY; + +const queryClient = new QueryClient(); + +const routeTree = dashboardRootRoute.addChildren([ + dashboardShellRoute.addChildren([ + dashboardIndexRoute, + dashboardHomeRoute, + dashboardCatchAllRoute, + ]), + dashboardMinimalShellRoute.addChildren([ + dashboardMinimalIndexRoute, + dashboardMinimalCatchAllRoute, + ]), +]); + +function getInitialEntry( + storageKey: DashboardPreviewPathKey, + variant?: "minimal", +) { + const stored = localStorage.getItem(storageKey); + if (stored) return stored; + return variant === "minimal" + ? "/preview/dashboard-minimal" + : "/preview/dashboard"; +} + +function createDashboardRouter( + storageKey: DashboardPreviewPathKey, + variant?: "minimal", +) { + const history = createMemoryHistory({ + initialEntries: [getInitialEntry(storageKey, variant)], + }); + return createRouter({ routeTree, history }); +} + +export function DashboardTemplate({ shellVariant }: DashboardTemplateProps) { + const storageKey = + shellVariant === "minimal" + ? DASHBOARD_MINIMAL_PREVIEW_PATH_KEY + : DASHBOARD_PREVIEW_PATH_KEY; + const [router] = useState(() => + createDashboardRouter(storageKey, shellVariant), + ); + + useEffect(() => { + const unsubscribe = router.subscribe("onResolved", ({ toLocation }) => { + localStorage.setItem(storageKey, toLocation.pathname); + }); + return unsubscribe; + }, [router, storageKey]); + + return ( + + + + ); +} diff --git a/apps/apollo-vertex/templates/dashboard/DashboardTemplateDynamic.tsx b/apps/apollo-vertex/templates/dashboard/DashboardTemplateDynamic.tsx new file mode 100644 index 000000000..8e522eaa5 --- /dev/null +++ b/apps/apollo-vertex/templates/dashboard/DashboardTemplateDynamic.tsx @@ -0,0 +1,16 @@ +"use client"; + +import dynamic from "next/dynamic"; +import type React from "react"; + +type DashboardTemplateProps = React.ComponentProps< + typeof import("./DashboardTemplate").DashboardTemplate +>; + +export const DashboardTemplate = dynamic( + () => + import("./DashboardTemplate").then((mod) => ({ + default: mod.DashboardTemplate, + })), + { ssr: false }, +); diff --git a/apps/apollo-vertex/templates/dashboard/dashboard-data-context.ts b/apps/apollo-vertex/templates/dashboard/dashboard-data-context.ts new file mode 100644 index 000000000..f628d6ac0 --- /dev/null +++ b/apps/apollo-vertex/templates/dashboard/dashboard-data-context.ts @@ -0,0 +1,22 @@ +"use client"; + +import { createContext, useContext } from "react"; +import { ecommerceDataset, type DashboardDataset } from "./dashboard-data"; + +export interface DashboardDataContextValue { + data: DashboardDataset; + setDataset: (data: DashboardDataset) => void; +} + +export const DashboardDataContext = createContext({ + data: ecommerceDataset, + setDataset: () => { + throw new Error( + "useDashboardData must be used within DashboardDataProvider", + ); + }, +}); + +export function useDashboardData() { + return useContext(DashboardDataContext); +} diff --git a/apps/apollo-vertex/templates/dashboard/dashboard-data.ts b/apps/apollo-vertex/templates/dashboard/dashboard-data.ts new file mode 100644 index 000000000..a16846b58 --- /dev/null +++ b/apps/apollo-vertex/templates/dashboard/dashboard-data.ts @@ -0,0 +1,228 @@ +export interface InsightCardData { + title: string; + type: "kpi" | "chart"; + chartType: "donut" | "horizontal-bars" | "sparkline" | "area" | "stacked-bar"; + size?: "sm" | "md" | "lg"; + interaction?: "static" | "expand" | "navigate"; + // Navigate config + navigateTo?: string; + // Expand config — additional content shown when card is expanded + expandContent?: { + summary?: string; + details?: string[]; + }; + // KPI data + kpiNumber?: string; + kpiBadge?: string; + kpiDescription?: string; + // Horizontal bars data + bars?: { label: string; value: number }[]; + // Stacked bar data + stackedBars?: { label: string; segments: number[] }[]; + stackedLegend?: string[]; + // Donut data + donutPercent?: number; + donutLabel?: string; + donutDescription?: string; + // Sparkline / Area data + points?: number[]; +} + +export interface DashboardDataset { + name: string; + brandName: string; + brandLine: string; + dashboardTitle: string; + badgeText: string; + greeting: string; + headline: string; + subhead: string; + chartLabels: { y: string[]; target: string }; + promptPlaceholder: string; + promptSuggestions: string[]; + insightCards: [ + InsightCardData, + InsightCardData, + InsightCardData, + InsightCardData, + ]; +} + +export const defaultDataset: DashboardDataset = { + name: "Loan Setup", + brandName: "UiPath", + brandLine: "Vertical Solutions", + dashboardTitle: "Product", + badgeText: "Experimental", + greeting: "Good morning, Peter", + headline: "Loan volume scales as setup time drops by 3.5 days.", + subhead: + "Setup time declined ↓21% month over month while volume increased ↑18%.", + chartLabels: { y: ["200", "150", "100", "50"], target: "Target" }, + promptPlaceholder: + "What would you like to understand about loan performance?", + promptSuggestions: [ + "Show me top risk factors", + "Compare Q1 vs Q2 performance", + ], + insightCards: [ + { + title: "Upfront decision efficiency", + type: "kpi", + chartType: "donut", + size: "sm", + interaction: "static", + kpiNumber: "94.2%", + kpiBadge: "+6.8%", + kpiDescription: "Loans finalized on first review without rework.", + }, + { + title: "Top issues", + type: "chart", + chartType: "horizontal-bars", + size: "md", + interaction: "expand", + expandContent: { + summary: "Risk flags have increased 12% this quarter", + details: ["Review underwriting criteria", "Update risk scoring model"], + }, + bars: [ + { label: "Risk flag in notes", value: 34 }, + { label: "Credit report >120 days old", value: 29 }, + { label: "Owner name mismatch", value: 23 }, + { label: "High DTI ratio", value: 14 }, + { label: "Missing appraisal docs", value: 11 }, + ], + }, + { + title: "Pipeline", + type: "chart", + chartType: "stacked-bar", + size: "md", + interaction: "expand", + expandContent: { + summary: "Weekly volume trending up with stable rejection rates", + details: [ + "Monitor Thursday spike pattern", + "Review rejected applications", + ], + }, + stackedBars: [ + { label: "Mon", segments: [30, 20, 10] }, + { label: "Tue", segments: [40, 15, 20] }, + { label: "Wed", segments: [25, 30, 15] }, + { label: "Thu", segments: [45, 10, 25] }, + { label: "Fri", segments: [35, 25, 18] }, + ], + stackedLegend: ["Approved", "Pending", "Rejected"], + }, + { + title: "SLA compliance", + type: "kpi", + chartType: "donut", + size: "sm", + interaction: "static", + kpiNumber: "99.5%", + kpiBadge: "+1.2%", + kpiDescription: "Loans processed within defined SLA thresholds.", + }, + ], +}; + +export const ecommerceDataset: DashboardDataset = { + name: "E-commerce Order Fulfillment", + brandName: "UiPath", + brandLine: "Vertical Solutions", + dashboardTitle: "Order fulfillment", + badgeText: "Experimental", + greeting: "Good morning, Peter", + headline: + "Order volume climbs as delivery performance improves, but fit-related returns remain the biggest drag on margin.", + subhead: + "Orders shipped increased ↑26% month over month while on-time delivery improved ↑2.4%, with size and fit issues now driving the largest share of returns.", + chartLabels: { y: ["600", "450", "300", "150"], target: "Target" }, + promptPlaceholder: + "What would you like to understand about order fulfillment?", + promptSuggestions: [ + "Why are fit-related returns increasing?", + "Show me products driving return volume", + "Compare warehouse performance", + "Which orders are most at risk of delay?", + ], + insightCards: [ + { + title: "On-time delivery rate", + type: "kpi", + chartType: "donut", + size: "sm", + interaction: "navigate", + kpiNumber: "97.1%", + kpiBadge: "+2.4%", + kpiDescription: + "Orders delivered within promised windows, supported by lower carrier delays and faster pick-pack turnaround.", + }, + { + title: "Top issues", + type: "chart", + chartType: "horizontal-bars", + size: "md", + interaction: "expand", + expandContent: { + summary: + "Return-related friction is now concentrated in product fit, transit handling, and expectation gaps, with apparel and footwear accounting for the highest exception volume.", + details: [ + "Investigate top SKUs contributing to wrong size and fit returns", + "Review packaging and carrier handoff for damage-related issues by warehouse", + "Use AI prompts to explain issue concentration by category, region, and fulfillment center", + ], + }, + bars: [ + { label: "Wrong size/fit", value: 39 }, + { label: "Damaged in transit", value: 23 }, + { label: "Not as described", value: 18 }, + { label: "Late delivery", value: 13 }, + { label: "Changed mind", value: 7 }, + ], + }, + { + title: "Pipeline", + type: "chart", + chartType: "stacked-bar", + size: "md", + interaction: "expand", + expandContent: { + summary: + "Fulfillment volume builds steadily through the week, with the highest shipped volume on Thursday and Friday and a midweek rise in processing backlog.", + details: [ + "Monitor Wednesday processing buildup for labor or inventory bottlenecks", + "Review Thursday and Friday shipment spikes by warehouse and carrier", + "Use AI prompts to identify whether growth is concentrated in apparel, footwear, or home goods", + ], + }, + stackedBars: [ + { label: "Mon", segments: [188, 46, 11] }, + { label: "Tue", segments: [204, 41, 13] }, + { label: "Wed", segments: [198, 57, 14] }, + { label: "Thu", segments: [236, 38, 15] }, + { label: "Fri", segments: [249, 43, 16] }, + ], + stackedLegend: ["Shipped", "Processing", "Returned"], + }, + { + title: "Customer satisfaction", + type: "kpi", + chartType: "donut", + size: "sm", + interaction: "navigate", + kpiNumber: "4.6", + kpiBadge: "+0.2", + kpiDescription: + "Average rating remains strong, though recent feedback highlights sizing inconsistency and occasional packaging damage.", + }, + ], +}; + +export const datasetPresets: Record = { + default: defaultDataset, + ecommerce: ecommerceDataset, +};