create-app-ui 1.0.0

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.
Files changed (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +117 -0
  3. package/boilerplate/README.md +18 -0
  4. package/boilerplate/react-base/.env.example +1 -0
  5. package/boilerplate/react-base/README.md +3 -0
  6. package/boilerplate/react-base/components.json +19 -0
  7. package/boilerplate/react-base/eslint.config.js +32 -0
  8. package/boilerplate/react-base/index.html +12 -0
  9. package/boilerplate/react-base/package.json +71 -0
  10. package/boilerplate/react-base/postcss.config.js +6 -0
  11. package/boilerplate/react-base/prettier.config.js +6 -0
  12. package/boilerplate/react-base/src/api/axios.ts +20 -0
  13. package/boilerplate/react-base/src/app/store.ts +13 -0
  14. package/boilerplate/react-base/src/components/data-table.tsx +919 -0
  15. package/boilerplate/react-base/src/components/ui/accordion.tsx +44 -0
  16. package/boilerplate/react-base/src/components/ui/alert-dialog.tsx +105 -0
  17. package/boilerplate/react-base/src/components/ui/alert.tsx +40 -0
  18. package/boilerplate/react-base/src/components/ui/avatar.tsx +30 -0
  19. package/boilerplate/react-base/src/components/ui/badge.tsx +27 -0
  20. package/boilerplate/react-base/src/components/ui/bar-chart.tsx +76 -0
  21. package/boilerplate/react-base/src/components/ui/breadcrumb.tsx +87 -0
  22. package/boilerplate/react-base/src/components/ui/button.tsx +34 -0
  23. package/boilerplate/react-base/src/components/ui/calendar.tsx +63 -0
  24. package/boilerplate/react-base/src/components/ui/card.tsx +36 -0
  25. package/boilerplate/react-base/src/components/ui/chart.tsx +280 -0
  26. package/boilerplate/react-base/src/components/ui/checkbox.tsx +51 -0
  27. package/boilerplate/react-base/src/components/ui/context-menu.tsx +173 -0
  28. package/boilerplate/react-base/src/components/ui/date-picker.tsx +42 -0
  29. package/boilerplate/react-base/src/components/ui/dialog.tsx +87 -0
  30. package/boilerplate/react-base/src/components/ui/drawer.tsx +81 -0
  31. package/boilerplate/react-base/src/components/ui/dropdown-menu.tsx +81 -0
  32. package/boilerplate/react-base/src/components/ui/dropdown-types.ts +28 -0
  33. package/boilerplate/react-base/src/components/ui/field.tsx +194 -0
  34. package/boilerplate/react-base/src/components/ui/hover-card.tsx +26 -0
  35. package/boilerplate/react-base/src/components/ui/input-group.tsx +98 -0
  36. package/boilerplate/react-base/src/components/ui/input-otp.tsx +63 -0
  37. package/boilerplate/react-base/src/components/ui/input.tsx +12 -0
  38. package/boilerplate/react-base/src/components/ui/item.tsx +152 -0
  39. package/boilerplate/react-base/src/components/ui/kbd.tsx +13 -0
  40. package/boilerplate/react-base/src/components/ui/label.tsx +14 -0
  41. package/boilerplate/react-base/src/components/ui/line-chart.tsx +65 -0
  42. package/boilerplate/react-base/src/components/ui/menubar.tsx +217 -0
  43. package/boilerplate/react-base/src/components/ui/multi-select-dropdown.tsx +200 -0
  44. package/boilerplate/react-base/src/components/ui/navigation-menu.tsx +120 -0
  45. package/boilerplate/react-base/src/components/ui/pie-chart.tsx +87 -0
  46. package/boilerplate/react-base/src/components/ui/popover.tsx +29 -0
  47. package/boilerplate/react-base/src/components/ui/progress.tsx +19 -0
  48. package/boilerplate/react-base/src/components/ui/radio-group.tsx +36 -0
  49. package/boilerplate/react-base/src/components/ui/scroll-area.tsx +38 -0
  50. package/boilerplate/react-base/src/components/ui/searchable-dropdown.tsx +118 -0
  51. package/boilerplate/react-base/src/components/ui/select.tsx +140 -0
  52. package/boilerplate/react-base/src/components/ui/separator.tsx +20 -0
  53. package/boilerplate/react-base/src/components/ui/sheet.tsx +70 -0
  54. package/boilerplate/react-base/src/components/ui/sidebar.tsx +470 -0
  55. package/boilerplate/react-base/src/components/ui/skeleton.tsx +11 -0
  56. package/boilerplate/react-base/src/components/ui/slider.tsx +23 -0
  57. package/boilerplate/react-base/src/components/ui/sonner.tsx +21 -0
  58. package/boilerplate/react-base/src/components/ui/sparkline.tsx +38 -0
  59. package/boilerplate/react-base/src/components/ui/spinner.tsx +10 -0
  60. package/boilerplate/react-base/src/components/ui/switch.tsx +16 -0
  61. package/boilerplate/react-base/src/components/ui/table.tsx +80 -0
  62. package/boilerplate/react-base/src/components/ui/tabs.tsx +32 -0
  63. package/boilerplate/react-base/src/components/ui/textarea.tsx +12 -0
  64. package/boilerplate/react-base/src/components/ui/toggle-group.tsx +49 -0
  65. package/boilerplate/react-base/src/components/ui/toggle.tsx +33 -0
  66. package/boilerplate/react-base/src/components/ui/tooltip.tsx +23 -0
  67. package/boilerplate/react-base/src/components/ui/typography.tsx +76 -0
  68. package/boilerplate/react-base/src/config/constants.ts +3 -0
  69. package/boilerplate/react-base/src/config/theme.ts +432 -0
  70. package/boilerplate/react-base/src/config/user.ts +52 -0
  71. package/boilerplate/react-base/src/context/theme-provider.tsx +12 -0
  72. package/boilerplate/react-base/src/features/auth/authSlice.ts +19 -0
  73. package/boilerplate/react-base/src/hooks/index.ts +1 -0
  74. package/boilerplate/react-base/src/hooks/use-mobile.ts +17 -0
  75. package/boilerplate/react-base/src/lib/utils.ts +6 -0
  76. package/boilerplate/react-base/src/routes/index.tsx +7 -0
  77. package/boilerplate/react-base/src/styles/globals.css +15 -0
  78. package/boilerplate/react-base/src/vite-env.d.ts +31 -0
  79. package/boilerplate/react-base/tailwind.config.ts +75 -0
  80. package/boilerplate/react-base/tsconfig.app.json +20 -0
  81. package/boilerplate/react-base/tsconfig.json +7 -0
  82. package/boilerplate/react-base/tsconfig.node.json +16 -0
  83. package/boilerplate/react-base/vite.config.ts +12 -0
  84. package/dist/bin/index.js +8 -0
  85. package/dist/src/cli-args.js +52 -0
  86. package/dist/src/generator.js +85 -0
  87. package/dist/src/installer.js +7 -0
  88. package/dist/src/paths.js +61 -0
  89. package/dist/src/prompts.js +79 -0
  90. package/dist/src/replace-placeholders.js +22 -0
  91. package/dist/src/utils.js +16 -0
  92. package/package.json +63 -0
  93. package/templates/admin-portal/README.md +26 -0
  94. package/templates/admin-portal/src/App.tsx +85 -0
  95. package/templates/admin-portal/src/assets/auth-hero.jpg +0 -0
  96. package/templates/admin-portal/src/assets/brand-logo.png +0 -0
  97. package/templates/admin-portal/src/components/app-breadcrumb.tsx +41 -0
  98. package/templates/admin-portal/src/components/app-header.tsx +20 -0
  99. package/templates/admin-portal/src/components/app-sidebar.tsx +78 -0
  100. package/templates/admin-portal/src/components/auth-layout.tsx +66 -0
  101. package/templates/admin-portal/src/components/dashboard-metric-card.tsx +105 -0
  102. package/templates/admin-portal/src/components/data-table.tsx +919 -0
  103. package/templates/admin-portal/src/components/layout-shell.tsx +23 -0
  104. package/templates/admin-portal/src/components/notifications-sheet.tsx +91 -0
  105. package/templates/admin-portal/src/components/sidebar-nav.tsx +164 -0
  106. package/templates/admin-portal/src/components/user-avatar.tsx +26 -0
  107. package/templates/admin-portal/src/components/user-menu.tsx +163 -0
  108. package/templates/admin-portal/src/config/branding.ts +17 -0
  109. package/templates/admin-portal/src/config/chart-data.ts +44 -0
  110. package/templates/admin-portal/src/config/navigation.ts +42 -0
  111. package/templates/admin-portal/src/context/auth-context.tsx +32 -0
  112. package/templates/admin-portal/src/lib/breadcrumbs.ts +58 -0
  113. package/templates/admin-portal/src/main.tsx +18 -0
  114. package/templates/admin-portal/src/pages/components/demo-columns.tsx +170 -0
  115. package/templates/admin-portal/src/pages/components.tsx +1368 -0
  116. package/templates/admin-portal/src/pages/dashboard.tsx +143 -0
  117. package/templates/admin-portal/src/pages/login.tsx +81 -0
  118. package/templates/admin-portal/src/pages/settings/notifications.tsx +31 -0
  119. package/templates/admin-portal/src/pages/settings/profile.tsx +26 -0
  120. package/templates/admin-portal/src/pages/signup.tsx +81 -0
  121. package/templates/admin-portal/src/pages/users.tsx +12 -0
  122. package/templates/admin-portal/tsconfig.json +10 -0
  123. package/templates/blank/README.md +15 -0
  124. package/templates/blank/src/App.tsx +5 -0
  125. package/templates/blank/src/main.tsx +15 -0
  126. package/templates/blank/src/pages/home.tsx +20 -0
  127. package/templates/blank/tsconfig.json +10 -0
  128. package/templates/tsconfig.overlay.base.json +7 -0
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "create-app-ui",
3
+ "version": "1.0.0",
4
+ "description": "Scaffold enterprise React admin apps (Vite, TypeScript, shadcn-style UI) from the Omobio UI platform",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Omobio",
8
+ "keywords": [
9
+ "react",
10
+ "vite",
11
+ "typescript",
12
+ "boilerplate",
13
+ "cli",
14
+ "generator",
15
+ "admin",
16
+ "shadcn"
17
+ ],
18
+ "engines": {
19
+ "node": ">=18"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/malikmanujaya/omobio-common-gui.git",
24
+ "directory": "ui-platform/create-app-ui"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/malikmanujaya/omobio-common-gui/issues"
28
+ },
29
+ "homepage": "https://github.com/malikmanujaya/omobio-common-gui/tree/main/ui-platform/create-app-ui#readme",
30
+ "bin": {
31
+ "create-app-ui": "./dist/bin/index.js"
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "boilerplate",
36
+ "templates",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "scripts": {
41
+ "build": "tsc -p tsconfig.json",
42
+ "prepare-publish": "node scripts/prepare-publish.mjs",
43
+ "prepublishOnly": "npm run build && npm run prepare-publish",
44
+ "prepack": "npm run build && npm run prepare-publish",
45
+ "pack:check": "npm run prepack && npm pack --dry-run",
46
+ "dev:watch": "tsx watch bin/index.ts",
47
+ "start": "tsx bin/index.ts"
48
+ },
49
+ "dependencies": {
50
+ "chalk": "^5.3.0",
51
+ "execa": "^9.3.0",
52
+ "fs-extra": "^11.2.0",
53
+ "ora": "^8.0.1",
54
+ "prompts": "^2.4.2"
55
+ },
56
+ "devDependencies": {
57
+ "@types/fs-extra": "^11.0.4",
58
+ "@types/node": "^22.10.1",
59
+ "@types/prompts": "^2.4.9",
60
+ "tsx": "^4.19.2",
61
+ "typescript": "^5.7.2"
62
+ }
63
+ }
@@ -0,0 +1,26 @@
1
+ # admin-portal template (overlay)
2
+
3
+ Applied **on top of** `boilerplate/react-base` when you choose **admin-portal** in the CLI.
4
+
5
+ This folder must **not** contain a full app: no `package.json`, `vite.config.ts`, `node_modules`, or `src/components/ui/*` (those live in the boilerplate).
6
+
7
+ ## What belongs here (intentional)
8
+
9
+ | Path | Purpose |
10
+ |------|---------|
11
+ | `src/components/*` | **App shell** only (layout, sidebar, header, auth layout, data-table wrapper) — not the shared UI kit |
12
+ | `src/lib/breadcrumbs.ts` | Admin route labels for breadcrumbs |
13
+ | `src/assets/*` | Template images (`brand-logo`, `auth-hero`) |
14
+ | `src/pages/*` | Admin screens |
15
+ | `src/config/*` | `navigation`, `branding`, `chart-data` |
16
+ | `src/context/auth-context.tsx` | Admin auth state |
17
+ | `App.tsx`, `main.tsx` | Entry and routes |
18
+
19
+ ## Do not add here
20
+
21
+ - `components/ui/*`, `config/theme.ts`, `lib/utils.ts`, hooks, axios, store — edit `boilerplate/react-base` instead.
22
+
23
+ ## Local dev / IDE
24
+
25
+ - `tsconfig.json` in this folder is **IDE-only** (resolves `@/*` against boilerplate + overlay). The CLI does not copy it into generated apps.
26
+ - Run from a **generated** app or merge boilerplate + this overlay; do not run `npm run dev` inside `templates/admin-portal` alone.
@@ -0,0 +1,85 @@
1
+ import { Navigate, Route, Routes } from "react-router-dom";
2
+ import { LayoutShell } from "@/components/layout-shell";
3
+ import { AuthProvider, useAuth } from "@/context/auth-context";
4
+ import { ThemeProvider } from "@/context/theme-provider";
5
+ import { ComponentsPage } from "@/pages/components";
6
+ import { DashboardPage } from "@/pages/dashboard";
7
+ import { LoginPage } from "@/pages/login";
8
+ import { SettingsNotificationsPage } from "@/pages/settings/notifications";
9
+ import { SettingsProfilePage } from "@/pages/settings/profile";
10
+ import { SignupPage } from "@/pages/signup";
11
+ import { UsersPage } from "@/pages/users";
12
+
13
+ function ProtectedLayout({ children }: { children: JSX.Element }) {
14
+ const { isAuthenticated } = useAuth();
15
+
16
+ if (!isAuthenticated) {
17
+ return <Navigate to="/login" replace />;
18
+ }
19
+
20
+ return <LayoutShell>{children}</LayoutShell>;
21
+ }
22
+
23
+ function AppRoutes() {
24
+ const { isAuthenticated, login } = useAuth();
25
+
26
+ return (
27
+ <Routes>
28
+ <Route path="/login" element={<LoginPage isAuthenticated={isAuthenticated} onLogin={login} />} />
29
+ <Route path="/signup" element={<SignupPage isAuthenticated={isAuthenticated} onSignup={login} />} />
30
+ <Route
31
+ path="/components"
32
+ element={
33
+ <ProtectedLayout>
34
+ <ComponentsPage />
35
+ </ProtectedLayout>
36
+ }
37
+ />
38
+ <Route
39
+ path="/dashboard"
40
+ element={
41
+ <ProtectedLayout>
42
+ <DashboardPage />
43
+ </ProtectedLayout>
44
+ }
45
+ />
46
+ <Route
47
+ path="/users"
48
+ element={
49
+ <ProtectedLayout>
50
+ <UsersPage />
51
+ </ProtectedLayout>
52
+ }
53
+ />
54
+ <Route path="/settings" element={<Navigate to="/settings/profile" replace />} />
55
+ <Route
56
+ path="/settings/profile"
57
+ element={
58
+ <ProtectedLayout>
59
+ <SettingsProfilePage />
60
+ </ProtectedLayout>
61
+ }
62
+ />
63
+ <Route
64
+ path="/settings/notifications"
65
+ element={
66
+ <ProtectedLayout>
67
+ <SettingsNotificationsPage />
68
+ </ProtectedLayout>
69
+ }
70
+ />
71
+ <Route path="/" element={<Navigate to={isAuthenticated ? "/components" : "/login"} replace />} />
72
+ <Route path="*" element={<Navigate to="/" replace />} />
73
+ </Routes>
74
+ );
75
+ }
76
+
77
+ export default function App() {
78
+ return (
79
+ <ThemeProvider>
80
+ <AuthProvider>
81
+ <AppRoutes />
82
+ </AuthProvider>
83
+ </ThemeProvider>
84
+ );
85
+ }
@@ -0,0 +1,41 @@
1
+ import { Fragment } from "react";
2
+ import { Link, useLocation } from "react-router-dom";
3
+ import {
4
+ Breadcrumb,
5
+ BreadcrumbItem,
6
+ BreadcrumbLink,
7
+ BreadcrumbList,
8
+ BreadcrumbPage,
9
+ BreadcrumbSeparator,
10
+ } from "@/components/ui/breadcrumb";
11
+ import { getBreadcrumbs } from "@/lib/breadcrumbs";
12
+
13
+ export function AppBreadcrumb() {
14
+ const { pathname } = useLocation();
15
+ const segments = getBreadcrumbs(pathname);
16
+
17
+ return (
18
+ <Breadcrumb>
19
+ <BreadcrumbList>
20
+ {segments.map((segment, index) => {
21
+ const isLast = index === segments.length - 1;
22
+
23
+ return (
24
+ <Fragment key={`${segment.label}-${index}`}>
25
+ {index > 0 && <BreadcrumbSeparator />}
26
+ <BreadcrumbItem>
27
+ {isLast || !segment.to ? (
28
+ <BreadcrumbPage>{segment.label}</BreadcrumbPage>
29
+ ) : (
30
+ <BreadcrumbLink asChild>
31
+ <Link to={segment.to}>{segment.label}</Link>
32
+ </BreadcrumbLink>
33
+ )}
34
+ </BreadcrumbItem>
35
+ </Fragment>
36
+ );
37
+ })}
38
+ </BreadcrumbList>
39
+ </Breadcrumb>
40
+ );
41
+ }
@@ -0,0 +1,20 @@
1
+ import { NotificationsSheet } from "@/components/notifications-sheet";
2
+ import { UserMenu } from "@/components/user-menu";
3
+ import { SidebarTrigger } from "@/components/ui/sidebar";
4
+ import { CURRENT_USER } from "@/config/user";
5
+
6
+ export function AppHeader() {
7
+ return (
8
+ <header className="sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b bg-background/80 px-4 backdrop-blur supports-[backdrop-filter]:bg-background/60 sm:px-6">
9
+ <SidebarTrigger />
10
+ <div className="flex min-w-0 flex-1 flex-col justify-center">
11
+ <p className="truncate text-sm font-medium">Welcome back</p>
12
+ <p className="truncate text-xs text-muted-foreground">{CURRENT_USER.name}</p>
13
+ </div>
14
+ <div className="flex items-center gap-2">
15
+ <NotificationsSheet />
16
+ <UserMenu variant="header" />
17
+ </div>
18
+ </header>
19
+ );
20
+ }
@@ -0,0 +1,78 @@
1
+ import { ChevronsUpDown } from "lucide-react";
2
+ import { UserMenu } from "@/components/user-menu";
3
+ import {
4
+ Sidebar,
5
+ SidebarContent,
6
+ SidebarFooter,
7
+ SidebarGroup,
8
+ SidebarGroupContent,
9
+ SidebarGroupLabel,
10
+ SidebarHeader,
11
+ SidebarMenu,
12
+ SidebarMenuItem,
13
+ SidebarRail,
14
+ useSidebar,
15
+ } from "@/components/ui/sidebar";
16
+ import { SidebarNavMenu } from "@/components/sidebar-nav";
17
+ import { BRANDING } from "@/config/branding";
18
+ import { overviewNav, workspaceNav } from "@/config/navigation";
19
+
20
+ function SidebarBrand() {
21
+ const { state } = useSidebar();
22
+
23
+ return (
24
+ <div className="flex items-center gap-3 px-1 py-1">
25
+ <div className="flex h-9 w-9 shrink-0 items-center justify-center overflow-hidden rounded-lg border bg-background shadow-sm">
26
+ <img src={BRANDING.logoPath} alt="" className="h-6 w-6 object-contain" />
27
+ </div>
28
+ <div className="grid flex-1 text-left leading-tight group-data-[collapsible=icon]:hidden">
29
+ <span className="truncate text-sm font-semibold">{BRANDING.productName}</span>
30
+ <span className="truncate text-xs text-muted-foreground">{BRANDING.companyName}</span>
31
+ </div>
32
+ {state === "expanded" && (
33
+ <ChevronsUpDown className="h-4 w-4 shrink-0 text-muted-foreground group-data-[collapsible=icon]:hidden" />
34
+ )}
35
+ </div>
36
+ );
37
+ }
38
+
39
+ export function AppSidebar({ onNavigate }: { onNavigate?: () => void }) {
40
+ const { setOpenMobile, isMobile } = useSidebar();
41
+
42
+ const handleNavigate = () => {
43
+ onNavigate?.();
44
+ if (isMobile) {
45
+ setOpenMobile(false);
46
+ }
47
+ };
48
+
49
+ return (
50
+ <Sidebar collapsible="icon">
51
+ <SidebarHeader>
52
+ <SidebarBrand />
53
+ </SidebarHeader>
54
+ <SidebarContent>
55
+ <SidebarGroup>
56
+ <SidebarGroupLabel>Overview</SidebarGroupLabel>
57
+ <SidebarGroupContent>
58
+ <SidebarNavMenu items={overviewNav} onNavigate={handleNavigate} />
59
+ </SidebarGroupContent>
60
+ </SidebarGroup>
61
+ <SidebarGroup>
62
+ <SidebarGroupLabel>Workspace</SidebarGroupLabel>
63
+ <SidebarGroupContent>
64
+ <SidebarNavMenu items={workspaceNav} onNavigate={handleNavigate} />
65
+ </SidebarGroupContent>
66
+ </SidebarGroup>
67
+ </SidebarContent>
68
+ <SidebarFooter>
69
+ <SidebarMenu>
70
+ <SidebarMenuItem>
71
+ <UserMenu variant="sidebar" />
72
+ </SidebarMenuItem>
73
+ </SidebarMenu>
74
+ </SidebarFooter>
75
+ <SidebarRail />
76
+ </Sidebar>
77
+ );
78
+ }
@@ -0,0 +1,66 @@
1
+ import { ReactNode } from "react";
2
+ import { Link } from "react-router-dom";
3
+ import { BRANDING } from "@/config/branding";
4
+ import { THEME } from "@/config/theme";
5
+ import { cn } from "@/lib/utils";
6
+ import { Separator } from "@/components/ui/separator";
7
+
8
+ type AuthLayoutProps = {
9
+ title: string;
10
+ subtitle: string;
11
+ alternateLabel: string;
12
+ alternateHref: string;
13
+ alternateCta: string;
14
+ children: ReactNode;
15
+ };
16
+
17
+ export function AuthLayout({
18
+ title,
19
+ subtitle,
20
+ alternateLabel,
21
+ alternateHref,
22
+ alternateCta,
23
+ children,
24
+ }: AuthLayoutProps) {
25
+ return (
26
+ <main className={cn("grid h-dvh max-h-dvh overflow-hidden lg:grid-cols-2", THEME.classes.authPage)}>
27
+ <section className="flex min-h-0 items-center justify-center overflow-y-auto p-4 sm:p-6 lg:p-8">
28
+ <div className="w-full max-w-md space-y-4 py-2">
29
+ <img
30
+ alt={`${BRANDING.projectName} logo`}
31
+ className="h-16 w-auto sm:h-20"
32
+ src={BRANDING.logoPath}
33
+ />
34
+ <div>
35
+ <h1 className="text-2xl font-semibold tracking-tight sm:text-3xl">{title}</h1>
36
+ <p className="mt-1 text-sm text-muted-foreground">{subtitle}</p>
37
+ </div>
38
+ {children}
39
+ <Separator />
40
+ <p className="text-sm text-muted-foreground">
41
+ {alternateLabel}{" "}
42
+ <Link className="font-medium text-primary hover:underline" to={alternateHref}>
43
+ {alternateCta}
44
+ </Link>
45
+ </p>
46
+ </div>
47
+ </section>
48
+
49
+ <section className="hidden min-h-0 p-3 lg:block lg:p-4">
50
+ <div className={cn("relative h-full min-h-0 overflow-hidden rounded-3xl", THEME.classes.authHero)}>
51
+ <img
52
+ alt={`${BRANDING.projectName} hero`}
53
+ className="h-full w-full object-cover"
54
+ src={BRANDING.heroImagePath}
55
+ />
56
+ <div
57
+ className={cn(
58
+ "pointer-events-none absolute inset-0 bg-gradient-to-t to-transparent",
59
+ THEME.classes.authHeroOverlay,
60
+ )}
61
+ />
62
+ </div>
63
+ </section>
64
+ </main>
65
+ );
66
+ }
@@ -0,0 +1,105 @@
1
+ import { DollarSign, Percent, Users, type LucideIcon } from "lucide-react";
2
+ import { Badge } from "@/components/ui/badge";
3
+ import { Card, CardContent } from "@/components/ui/card";
4
+ import { Sparkline } from "@/components/ui/sparkline";
5
+ import { THEME } from "@/config/theme";
6
+ import { cn } from "@/lib/utils";
7
+
8
+ export type DashboardMetric = {
9
+ label: string;
10
+ value: string;
11
+ change: string;
12
+ trend: { value: number }[];
13
+ icon: LucideIcon;
14
+ iconClassName?: string;
15
+ sparklineColor?: string;
16
+ };
17
+
18
+ type DashboardMetricCardProps = {
19
+ metric: DashboardMetric;
20
+ };
21
+
22
+ export function DashboardMetricCard({ metric }: DashboardMetricCardProps) {
23
+ const Icon = metric.icon;
24
+
25
+ return (
26
+ <Card>
27
+ <CardContent className="p-6">
28
+ <div className="flex items-start justify-between gap-4">
29
+ <div className="min-w-0 space-y-2">
30
+ <p className="text-sm font-medium text-muted-foreground">{metric.label}</p>
31
+ <p className="text-2xl font-bold tracking-tight">{metric.value}</p>
32
+ <Badge variant="secondary" className="font-normal">
33
+ {metric.change} vs last month
34
+ </Badge>
35
+ </div>
36
+ <div className="flex shrink-0 flex-col items-end gap-3">
37
+ <div
38
+ className={cn(
39
+ "flex h-11 w-11 items-center justify-center rounded-xl border bg-muted/50",
40
+ metric.iconClassName,
41
+ )}
42
+ >
43
+ <Icon className="h-5 w-5" />
44
+ </div>
45
+ <Sparkline data={metric.trend} color={metric.sparklineColor} />
46
+ </div>
47
+ </div>
48
+ </CardContent>
49
+ </Card>
50
+ );
51
+ }
52
+
53
+ export const dashboardMetrics: DashboardMetric[] = [
54
+ {
55
+ label: "Revenue",
56
+ value: "$42.8k",
57
+ change: "+12.4%",
58
+ icon: DollarSign,
59
+ iconClassName: THEME.metrics.revenue.iconClassName,
60
+ sparklineColor: THEME.metrics.revenue.sparklineColor,
61
+ trend: [
62
+ { value: 38 },
63
+ { value: 40 },
64
+ { value: 39 },
65
+ { value: 41 },
66
+ { value: 43 },
67
+ { value: 42 },
68
+ { value: 43 },
69
+ ],
70
+ },
71
+ {
72
+ label: "Active Users",
73
+ value: "18,240",
74
+ change: "+8.1%",
75
+ icon: Users,
76
+ iconClassName: THEME.metrics.users.iconClassName,
77
+ sparklineColor: THEME.metrics.users.sparklineColor,
78
+ trend: [
79
+ { value: 16200 },
80
+ { value: 16800 },
81
+ { value: 17100 },
82
+ { value: 17500 },
83
+ { value: 17800 },
84
+ { value: 18000 },
85
+ { value: 18240 },
86
+ ],
87
+ },
88
+ {
89
+ label: "Conversion",
90
+ value: "12.4%",
91
+ change: "+2.3%",
92
+ icon: Percent,
93
+ iconClassName: THEME.metrics.conversion.iconClassName,
94
+ sparklineColor: THEME.metrics.conversion.sparklineColor,
95
+ trend: [
96
+ { value: 10.2 },
97
+ { value: 10.8 },
98
+ { value: 11.1 },
99
+ { value: 11.5 },
100
+ { value: 11.9 },
101
+ { value: 12.1 },
102
+ { value: 12.4 },
103
+ ],
104
+ },
105
+ ];