@umituz/web-dashboard 3.2.2 → 4.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 (125) hide show
  1. package/README.md +202 -14
  2. package/package.json +21 -29
  3. package/src/domains/analytics/components/AnalyticsCard.tsx +23 -21
  4. package/src/domains/analytics/components/AnalyticsChart.tsx +345 -149
  5. package/src/domains/analytics/components/AnalyticsLayout.tsx +93 -112
  6. package/src/domains/analytics/components/MetricCard.tsx +42 -64
  7. package/src/domains/analytics/hooks/index.ts +7 -1
  8. package/src/domains/analytics/hooks/useAnalytics.ts +144 -139
  9. package/src/domains/analytics/index.ts +11 -0
  10. package/src/domains/analytics/services/AnalyticsEngineService.ts +103 -55
  11. package/src/domains/analytics/services/PerformanceService.ts +117 -69
  12. package/src/domains/analytics/types/analytics.ts +20 -6
  13. package/src/domains/analytics/types/index.ts +3 -0
  14. package/src/domains/analytics/utils/analytics.test.ts +183 -0
  15. package/src/domains/analytics/utils/analytics.ts +26 -9
  16. package/src/domains/analytics/utils/i18nKeys.ts +40 -0
  17. package/src/domains/analytics/utils/index.ts +2 -0
  18. package/src/domains/auth/components/AuthLayout.tsx +63 -54
  19. package/src/domains/auth/components/ForgotPasswordForm.tsx +96 -98
  20. package/src/domains/auth/components/LoginForm.tsx +167 -226
  21. package/src/domains/auth/components/RegisterForm.tsx +202 -272
  22. package/src/domains/auth/components/ResetPasswordForm.tsx +105 -151
  23. package/src/domains/auth/components/shared/AuthErrorBanner.tsx +35 -0
  24. package/src/domains/auth/components/shared/EmailInput.tsx +91 -0
  25. package/src/domains/auth/components/shared/PasswordInput.tsx +119 -0
  26. package/src/domains/auth/components/shared/PasswordStrengthIndicator.tsx +75 -0
  27. package/src/domains/auth/components/shared/SocialLoginButtons.tsx +58 -0
  28. package/src/domains/auth/components/shared/SocialProviderIcons.tsx +48 -0
  29. package/src/domains/auth/components/shared/index.ts +20 -0
  30. package/src/domains/auth/hooks/index.ts +1 -0
  31. package/src/domains/auth/hooks/useAuth.ts +151 -232
  32. package/src/domains/auth/index.ts +8 -4
  33. package/src/domains/auth/types/auth.ts +26 -5
  34. package/src/domains/auth/types/index.ts +1 -0
  35. package/src/domains/auth/utils/auth.test.ts +182 -0
  36. package/src/domains/auth/utils/auth.ts +71 -130
  37. package/src/domains/auth/utils/i18nKeys.ts +127 -0
  38. package/src/domains/auth/utils/index.ts +7 -1
  39. package/src/domains/auth/utils/secureToken.ts +33 -0
  40. package/src/domains/auth/utils/validationKeys.ts +23 -0
  41. package/src/domains/calendar/hooks/useCalendar.ts +145 -151
  42. package/src/domains/calendar/index.ts +0 -1
  43. package/src/domains/calendar/services/CalendarService.ts +122 -179
  44. package/src/domains/calendar/utils/contentItemMapper.test.ts +108 -0
  45. package/src/domains/calendar/utils/contentItemMapper.ts +99 -0
  46. package/src/domains/calendar/utils/index.ts +33 -30
  47. package/src/domains/layouts/components/BrandLogo.tsx +12 -9
  48. package/src/domains/layouts/components/DashboardHeader.tsx +78 -80
  49. package/src/domains/layouts/components/DashboardLayout.tsx +45 -21
  50. package/src/domains/layouts/components/DashboardSidebar.tsx +48 -51
  51. package/src/domains/layouts/types/layout.ts +7 -1
  52. package/src/domains/layouts/types/user.ts +3 -1
  53. package/src/domains/modules/build.test.tsx +97 -0
  54. package/src/domains/modules/build.tsx +71 -0
  55. package/src/domains/modules/index.ts +2 -0
  56. package/src/domains/modules/types.ts +29 -0
  57. package/src/domains/onboarding/components/AppFocusStep.tsx +99 -68
  58. package/src/domains/onboarding/components/OnboardingWizard.tsx +109 -183
  59. package/src/domains/onboarding/components/PlanStep.tsx +156 -123
  60. package/src/domains/onboarding/components/PlatformsStep.tsx +74 -54
  61. package/src/domains/onboarding/components/StepNavigation.tsx +108 -0
  62. package/src/domains/onboarding/components/StepProgress.tsx +62 -0
  63. package/src/domains/onboarding/components/UserTypeStep.tsx +59 -80
  64. package/src/domains/onboarding/components/index.ts +4 -0
  65. package/src/domains/onboarding/hooks/useOnboarding.ts +76 -66
  66. package/src/domains/onboarding/index.ts +1 -0
  67. package/src/domains/onboarding/types/onboarding.ts +57 -17
  68. package/src/domains/onboarding/utils/i18nKeys.ts +72 -0
  69. package/src/domains/onboarding/utils/index.ts +2 -0
  70. package/src/domains/permissions/PermissionsContext.tsx +103 -0
  71. package/src/domains/permissions/hasPermission.test.ts +50 -0
  72. package/src/domains/permissions/hasPermission.ts +38 -0
  73. package/src/domains/permissions/index.ts +19 -0
  74. package/src/domains/permissions/types.ts +35 -0
  75. package/src/domains/permissions/utils/i18nKeys.ts +11 -0
  76. package/src/domains/session/DemoLogin.tsx +50 -0
  77. package/src/domains/session/OrgSwitcher.tsx +110 -0
  78. package/src/domains/session/SessionContext.tsx +150 -0
  79. package/src/domains/session/inMemoryAuthAdapter.ts +53 -0
  80. package/src/domains/session/index.ts +18 -0
  81. package/src/domains/session/types.ts +47 -0
  82. package/src/domains/session/utils/i18nKeys.ts +12 -0
  83. package/src/domains/settings/components/AuditLogList.tsx +59 -0
  84. package/src/domains/settings/components/PermissionMatrixScreen.tsx +80 -0
  85. package/src/domains/settings/components/SettingsLayout.tsx +75 -60
  86. package/src/domains/settings/components/SettingsSection.tsx +35 -22
  87. package/src/domains/settings/components/UserManagementTable.tsx +87 -0
  88. package/src/domains/settings/components/index.ts +10 -0
  89. package/src/domains/settings/hooks/useSettings.ts +77 -52
  90. package/src/domains/settings/types/settings.ts +2 -0
  91. package/src/domains/settings/utils/i18nKeys.ts +18 -0
  92. package/src/domains/settings/utils/settings.ts +11 -4
  93. package/src/i18n/I18nContext.tsx +33 -0
  94. package/src/i18n/defaultT.test.ts +43 -0
  95. package/src/i18n/defaultT.ts +34 -0
  96. package/src/i18n/index.ts +7 -0
  97. package/src/i18n/types.ts +13 -0
  98. package/src/index.ts +1 -7
  99. package/src/primitives/Button.tsx +30 -0
  100. package/src/primitives/Checkbox.tsx +40 -0
  101. package/src/primitives/Input.tsx +19 -0
  102. package/src/primitives/Skeleton.tsx +26 -0
  103. package/src/primitives/cn.ts +15 -0
  104. package/src/primitives/index.ts +23 -0
  105. package/src/styles/dashboard.css +3045 -0
  106. package/src/styles/tokens.contract.test.ts +66 -0
  107. package/src/styles/tokens.css +124 -0
  108. package/src/domains/billing/components/BillingLayout.tsx +0 -63
  109. package/src/domains/billing/components/BillingPage.tsx +0 -49
  110. package/src/domains/billing/components/BillingPortal.tsx +0 -198
  111. package/src/domains/billing/components/InvoiceCard.tsx +0 -143
  112. package/src/domains/billing/components/PaymentMethodsList.tsx +0 -116
  113. package/src/domains/billing/components/PlanComparison.tsx +0 -199
  114. package/src/domains/billing/components/UsageCard.tsx +0 -103
  115. package/src/domains/billing/components/index.ts +0 -13
  116. package/src/domains/billing/constants/billing.ts +0 -69
  117. package/src/domains/billing/hooks/index.ts +0 -7
  118. package/src/domains/billing/hooks/useBilling.ts +0 -385
  119. package/src/domains/billing/index.ts +0 -73
  120. package/src/domains/billing/types/billing.ts +0 -342
  121. package/src/domains/billing/types/index.ts +0 -30
  122. package/src/domains/billing/utils/billing.ts +0 -360
  123. package/src/domains/billing/utils/index.ts +0 -31
  124. package/src/domains/billing/utils/polar.ts +0 -63
  125. package/src/domains/calendar/components/index.ts +0 -7
package/README.md CHANGED
@@ -1,32 +1,220 @@
1
1
  # @umituz/web-dashboard
2
2
 
3
- Comprehensive dashboard library with analytics, billing, calendar, and more.
3
+ Installable app skeleton for SaaS dashboards: layouts, auth, onboarding,
4
+ analytics, a content calendar, an i18n bridge, and a permissions/session/
5
+ module registry. Every domain is an independent entry point — import only
6
+ what your app uses. Camp-agnostic by construction: the package ships its
7
+ own `udb-*` stylesheet driven by `--u-*` tokens, so it works identically in
8
+ vanilla-CSS and Tailwind hosts.
4
9
 
5
- ## Features
10
+ The package is **source-distributed** (published as raw TypeScript in `src/`).
11
+ Your bundler compiles it alongside your app; no prebuilt bundle ships.
6
12
 
7
- - Analytics dashboards with charts
8
- - Billing and subscription management
9
- - Calendar integration
10
- - User management
11
- - Real-time metrics
13
+ ## What changed in v4 (major)
14
+
15
+ - **Billing moved to [@umituz/web-polar-payment](https://github.com/umituz/web-polar-payment)**
16
+ under the `./portal` entry point. The old `./billing*` entry points are gone.
17
+ - **`@umituz/web-design-system` is no longer a dependency.** The primitives
18
+ (`Button`, `Input`, `Checkbox`, `Skeleton`, `cn`) live in `./primitives`.
19
+ - **Styling is shipped, not assumed.** Import `@umituz/web-dashboard/styles.css`
20
+ (and optionally `tokens.css`) once; all components use `udb-*` BEM classes.
21
+ Tailwind, design-system tokens and utility classes are gone from the package.
22
+ - **No i18n runtime.** `react-i18next` is out; hosts bridge their own `t` via
23
+ `<DashboardI18nProvider t={i18n.t}>` (see below). Without a provider,
24
+ keys humanize to readable English.
25
+ - **No Firebase.** The calendar persists through the injected
26
+ `ICalendarDatabase` seam; `createInMemoryCalendarDatabase()` is the default.
27
+ - **recharts is out.** `AnalyticsChart` renders line/area/bar/pie/donut as
28
+ dependency-free SVG with native `<title>` tooltips.
29
+ - **New fleet modules**: `./permissions`, `./session`, `./modules` — the
30
+ texora/visora pattern (one module list drives routes + nav + guards) as
31
+ reusable building blocks.
12
32
 
13
33
  ## Installation
14
34
 
15
35
  ```bash
16
36
  npm install @umituz/web-dashboard
37
+ import "@umituz/web-dashboard/styles.css";
38
+ ```
39
+
40
+ Peers: `react` + `react-dom` (^18 or ^19) and `react-router-dom` (^6 or ^7).
41
+ Runtime deps: `clsx` + `lucide-react`. Nothing else.
42
+
43
+ ### Tailwind hosts
44
+
45
+ Every `udb-*` rule sets its own `box-sizing`/`background`/`border` explicitly,
46
+ so preflight cannot leak in — but load order still matters with `*`-selector
47
+ resets: import the package stylesheet **after** your base layer.
48
+
49
+ ### Theming & dark mode
50
+
51
+ Override the `--u-*` custom properties (see `tokens.css` for the full list).
52
+ Dark mode is host-driven: put `data-udb-theme="dark"` on any ancestor.
53
+
54
+ ## Entry points
55
+
56
+ ```bash
57
+ @umituz/web-dashboard/styles.css # component stylesheet (udb-* BEM)
58
+ @umituz/web-dashboard/tokens.css # --u-* custom properties only
59
+ @umituz/web-dashboard/primitives # Button, Input, Checkbox, Skeleton, cn
60
+ @umituz/web-dashboard/i18n # DashboardI18nProvider, useT, defaultT
61
+ @umituz/web-dashboard/permissions # PermissionMatrix, hasPermission, guards
62
+ @umituz/web-dashboard/session # AuthAdapter, SessionProvider, OrgSwitcher, DemoLogin
63
+ @umituz/web-dashboard/modules # DashboardModule, buildRoutes, buildNav
64
+ @umituz/web-dashboard/layouts # DashboardLayout, DashboardSidebar, DashboardHeader
65
+ @umituz/web-dashboard/settings # SettingsLayout, admin skeletons (users, matrix, audit)
66
+ @umituz/web-dashboard/onboarding # OnboardingWizard + steps, useOnboarding
67
+ @umituz/web-dashboard/auth # Login/Register/Forgot/Reset forms, AuthLayout
68
+ @umituz/web-dashboard/analytics # AnalyticsLayout, SVG AnalyticsChart, services
69
+ @umituz/web-dashboard/calendar # useCalendar, CalendarService (injected DB)
70
+ @umituz/web-dashboard/config # dashboard config helpers
71
+ ```
72
+
73
+ Each domain also exposes `components`, `hooks`, `utils`, `types`
74
+ sub-entries (and `services` for analytics/calendar). The new v4 modules
75
+ (`permissions`, `session`, `modules`) are **deep-entry only** — they are not
76
+ re-exported from the root, so router-less hosts never pull router code in
77
+ transitively.
78
+
79
+ ## The v4 composition
80
+
81
+ A host app assembles the skeleton from four seams:
82
+
83
+ ```tsx
84
+ import { DashboardI18nProvider } from "@umituz/web-dashboard/i18n";
85
+ import { SessionProvider, createInMemoryAuthAdapter } from "@umituz/web-dashboard/session";
86
+ import { PermissionsProvider } from "@umituz/web-dashboard/permissions";
87
+
88
+ const adapter = createInMemoryAuthAdapter({ users: DEMO_USERS });
89
+
90
+ <DashboardI18nProvider t={i18n.t}>
91
+ <SessionProvider adapter={adapter}>
92
+ <PermissionsProvider matrix={ROLE_PERMISSIONS} role={user?.role}>
93
+ <App />
94
+ </PermissionsProvider>
95
+ </SessionProvider>
96
+ </DashboardI18nProvider>
17
97
  ```
18
98
 
19
- ## Usage
99
+ ### One module list → routes + nav + guards
100
+
101
+ ```tsx
102
+ import { buildRoutes, buildNav } from "@umituz/web-dashboard/modules";
103
+ import { usePermissions } from "@umituz/web-dashboard/permissions";
20
104
 
21
- ```typescript
22
- import { useAnalytics } from '@umituz/web-dashboard';
105
+ const MODULES: DashboardModule[] = [
106
+ { path: "/dashboard", labelKey: "nav.dashboard", icon: LayoutDashboard,
107
+ navGroup: "nav.overview", order: 1, permission: "dashboard.view",
108
+ element: <DashboardPage /> },
109
+ { path: "/sales/:id", labelKey: "nav.salesDetail", icon: Circle,
110
+ navGroup: "nav.commercial", inNav: false, permission: "sales.view",
111
+ element: <SalesDetailPage /> },
112
+ ];
23
113
 
24
- function Dashboard() {
25
- const { data, loading } = useAnalytics();
26
- // ...
27
- }
114
+ // routes: element pre-wrapped in <RequirePermission>
115
+ const routes = buildRoutes(MODULES); // -> <Route path key={...} {...route} />
116
+
117
+ // nav: permission-filtered, grouped, ordered -> feed DashboardLayout config
118
+ const { can } = usePermissions();
119
+ const sidebarGroups = buildNav(MODULES, { can });
28
120
  ```
29
121
 
122
+ Groups keep their first-appearance order in the list; `order` sorts items
123
+ within a group; `inNav: false` hides detail routes from the sidebar.
124
+
125
+ ### Permissions
126
+
127
+ `hasPermission(matrix, role, permission)` is pure and deny-by-default.
128
+ Matrix grants are `"*"` (everything), `"sales.*"` (module wildcard) or exact
129
+ strings. `<RequirePermission permission>` guards pages (lock screen
130
+ fallback); `<Can>` is its inline twin.
131
+
132
+ ### Session
133
+
134
+ Hosts adapt their auth to `AuthAdapter` (`login`/`logout`/`current`;
135
+ `listTenants`/`demoLogin` optional). `useSession()` exposes user, tenants,
136
+ active tenant, status + `switchTenant`. `OrgSwitcher` renders the tenant
137
+ dropdown; `DemoLogin` the one-click demo entry.
138
+
139
+ ### Calendar
140
+
141
+ ```tsx
142
+ import { CalendarService, createInMemoryCalendarDatabase } from "@umituz/web-dashboard/calendar/services";
143
+
144
+ // Bring your own backend:
145
+ class MyDatabase implements ICalendarDatabase { /* ... */ }
146
+ const service = new CalendarService(new MyDatabase());
147
+ // or demo/test:
148
+ const demo = new CalendarService(createInMemoryCalendarDatabase());
149
+ ```
150
+
151
+ ### Analytics
152
+
153
+ `AnalyticsChart` consumes the same `ChartConfig` as v3 for
154
+ line/area/bar/pie/donut. Tooltips are native SVG `<title>` elements;
155
+ legends are color chips; unsupported types render an empty state.
156
+ `useAnalytics` stays dependency-injected (`createStubAnalyticsApiClient`).
157
+
158
+ ## i18n contract
159
+
160
+ Components render **i18n keys**, never English strings. Bridge your runtime
161
+ once:
162
+
163
+ ```tsx
164
+ import { DashboardI18nProvider } from "@umituz/web-dashboard/i18n";
165
+
166
+ <DashboardI18nProvider t={i18n.t}> {/* i18next's t — signature already matches */}
167
+ ```
168
+
169
+ Interpolation uses the standard `{{placeholder}}` syntax. Without a
170
+ provider, `useT()` falls back to `defaultT`, which humanizes keys
171
+ (`auth.login.welcomeBack` → "Welcome back") so a bare install still renders
172
+ readably. The per-domain key catalogs are the string contract:
173
+
174
+ ```ts
175
+ import { AUTH_KEYS } from "@umituz/web-dashboard/auth";
176
+ import { ANALYTICS_KEYS } from "@umituz/web-dashboard/analytics";
177
+ import { ONBOARDING_KEYS } from "@umituz/web-dashboard/onboarding";
178
+ import { SETTINGS_ADMIN_KEYS } from "@umituz/web-dashboard/settings/utils/i18nKeys";
179
+ import { PERMISSIONS_KEYS } from "@umituz/web-dashboard/permissions";
180
+ import { SESSION_KEYS } from "@umituz/web-dashboard/session";
181
+ ```
182
+
183
+ ## Platform compatibility
184
+
185
+ - React 18 and 19, react-router-dom 6 and 7
186
+ - `generateResetToken` requires `crypto.getRandomValues` (all modern browsers,
187
+ Node ≥ 19) and **throws** rather than falling back to an insecure PRNG
188
+ - Rendering targets the DOM; React Native is not supported
189
+
190
+ ## Development
191
+
192
+ ```bash
193
+ npm install
194
+ npm run lint # eslint
195
+ npm run typecheck # tsc --noEmit
196
+ npm test # vitest unit tests (pure utils + module builders)
197
+ npm run test:watch
198
+ ```
199
+
200
+ CI runs lint + typecheck + test on every push/PR to `main`.
201
+
202
+ ## Migration: 3.x → 4.0
203
+
204
+ 1. Billing UI: switch imports to `@umituz/web-polar-payment/portal`.
205
+ 2. Design-system imports: use `@umituz/web-dashboard/primitives`
206
+ (`Button`/`Input`/`Checkbox`/`Skeleton` cover the used surface).
207
+ 3. Styling: import `styles.css` once; Tailwind utility classes no longer
208
+ exist inside the package. Config values that used to carry Tailwind
209
+ strings changed: `PlatformOption.color` is a plain CSS color string,
210
+ `PlanOption.badgeColor` became `badgeTone: "muted" | "primary" | "accent"`.
211
+ 4. i18n: wrap your app in `DashboardI18nProvider t={i18n.t}`; the
212
+ `react-i18next` peer is gone.
213
+ 5. Calendar: pass your own `ICalendarDatabase` (or the in-memory default);
214
+ the Firebase implementation was removed.
215
+ 6. v3.2.5 behavioral notes still apply (opt-in mock auth, route skeletons
216
+ off by default, `PerformanceService.startMonitoring()` explicit).
217
+
30
218
  ## License
31
219
 
32
220
  MIT
package/package.json CHANGED
@@ -1,12 +1,22 @@
1
1
  {
2
2
  "name": "@umituz/web-dashboard",
3
- "version": "3.2.2",
4
- "description": "Dashboard Layout System - Comprehensive analytics, calendar, customizable layouts, and config-based architecture",
3
+ "version": "4.0.0",
4
+ "description": "Installable app skeleton for the fleet — layouts, auth, onboarding, analytics (dependency-free SVG charts), calendar, i18n injection, permissions/session/module registry; ships its own udb-* stylesheet and --u-* tokens",
5
+ "type": "module",
5
6
  "main": "./src/index.ts",
6
7
  "types": "./src/index.ts",
7
- "sideEffects": false,
8
+ "sideEffects": [
9
+ "*.css"
10
+ ],
8
11
  "exports": {
9
12
  ".": "./src/index.ts",
13
+ "./styles.css": "./src/styles/dashboard.css",
14
+ "./tokens.css": "./src/styles/tokens.css",
15
+ "./primitives": "./src/primitives/index.ts",
16
+ "./i18n": "./src/i18n/index.ts",
17
+ "./permissions": "./src/domains/permissions/index.ts",
18
+ "./session": "./src/domains/session/index.ts",
19
+ "./modules": "./src/domains/modules/index.ts",
10
20
  "./layouts": "./src/domains/layouts/index.ts",
11
21
  "./layouts/components": "./src/domains/layouts/components/index.ts",
12
22
  "./layouts/hooks": "./src/domains/layouts/hooks/index.ts",
@@ -35,16 +45,11 @@
35
45
  "./analytics/types": "./src/domains/analytics/types/index.ts",
36
46
  "./analytics/services": "./src/domains/analytics/services/index.ts",
37
47
  "./calendar": "./src/domains/calendar/index.ts",
38
- "./calendar/types": "./src/domains/calendar/types/calendar.types",
48
+ "./calendar/types": "./src/domains/calendar/types/calendar.types.ts",
39
49
  "./calendar/services": "./src/domains/calendar/services/index.ts",
40
50
  "./calendar/hooks": "./src/domains/calendar/hooks/index.ts",
41
51
  "./calendar/utils": "./src/domains/calendar/utils/index.ts",
42
52
  "./config": "./src/domain/config/index.ts",
43
- "./billing": "./src/domains/billing/index.ts",
44
- "./billing/components": "./src/domains/billing/components/index.ts",
45
- "./billing/hooks": "./src/domains/billing/hooks/index.ts",
46
- "./billing/utils": "./src/domains/billing/utils/index.ts",
47
- "./billing/types": "./src/domains/billing/types/index.ts",
48
53
  "./package.json": "./package.json"
49
54
  },
50
55
  "files": [
@@ -54,6 +59,8 @@
54
59
  "scripts": {
55
60
  "typecheck": "tsc --noEmit",
56
61
  "lint": "eslint src",
62
+ "test": "vitest run",
63
+ "test:watch": "vitest",
57
64
  "version:patch": "npm version patch -m 'chore: release v%s'",
58
65
  "version:minor": "npm version minor -m 'chore: release v%s'",
59
66
  "version:major": "npm version major -m 'chore: release v%s'"
@@ -61,30 +68,21 @@
61
68
  "peerDependencies": {
62
69
  "react": "^18.0.0 || ^19.0.0",
63
70
  "react-dom": "^18.0.0 || ^19.0.0",
64
- "react-router-dom": "^6.0.0 || ^7.0.0",
65
- "firebase": "^10.0.0 || ^11.0.0 || ^12.0.0",
66
- "@umituz/web-firebase": "^3.0.0",
67
- "@umituz/web-polar-payment": "^1.0.20"
71
+ "react-router-dom": "^6.0.0 || ^7.0.0"
68
72
  },
69
73
  "dependencies": {
70
- "class-variance-authority": "^0.7.1",
71
74
  "clsx": "^2.1.1",
72
- "lucide-react": "^0.577.0",
73
- "recharts": "^2.15.0",
74
- "tailwind-merge": "^3.5.0"
75
+ "lucide-react": "^0.577.0"
75
76
  },
76
77
  "devDependencies": {
77
78
  "@types/react": "^19.2.14",
78
79
  "@types/react-dom": "^19.2.3",
79
80
  "@typescript-eslint/eslint-plugin": "^8.57.2",
80
81
  "@typescript-eslint/parser": "^8.57.2",
81
- "@umituz/web-design-system": "^3.1.11",
82
- "@umituz/web-firebase": "^3.6.0",
83
82
  "eslint": "^10.0.2",
84
- "firebase": "^12.0.0",
85
- "react-i18next": "^16.5.4",
86
83
  "react-router-dom": "^7.13.1",
87
- "typescript": "^5.9.3"
84
+ "typescript": "^5.9.3",
85
+ "vitest": "^5.0.1"
88
86
  },
89
87
  "keywords": [
90
88
  "dashboard",
@@ -113,13 +111,7 @@
113
111
  "metrics",
114
112
  "kpi",
115
113
  "visualization",
116
- "recharts",
117
- "billing",
118
- "subscription",
119
- "payment",
120
- "invoices",
121
- "pricing",
122
- "plans",
114
+ "svg-charts",
123
115
  "react",
124
116
  "typescript",
125
117
  "components",
@@ -1,15 +1,24 @@
1
1
  /**
2
2
  * Analytics Card Component
3
3
  *
4
- * Configurable analytics card with chart or metrics
4
+ * Configurable analytics card with chart or metrics. Surface comes
5
+ * from the shared `udb-card` helper; size modifiers shrink/grow the
6
+ * padding ("full" shares the md default).
5
7
  */
6
8
 
7
9
  import { Loader2, AlertCircle } from "lucide-react";
8
- import { cn } from "@umituz/web-design-system/utils";
10
+ import { cn } from "../../../primitives";
9
11
  import type { AnalyticsCardProps } from "../types/analytics";
10
12
  import { MetricCard } from "./MetricCard";
11
13
  import { AnalyticsChart } from "./AnalyticsChart";
12
14
 
15
+ const SURFACE_CLASS = {
16
+ sm: 'udb-card--sm',
17
+ md: undefined,
18
+ lg: 'udb-card--lg',
19
+ full: undefined,
20
+ } as const;
21
+
13
22
  export const AnalyticsCard = ({
14
23
  title,
15
24
  description,
@@ -21,18 +30,11 @@ export const AnalyticsCard = ({
21
30
  className,
22
31
  children,
23
32
  }: AnalyticsCardProps) => {
24
- const sizeClasses = {
25
- sm: "p-4",
26
- md: "p-6",
27
- lg: "p-8",
28
- full: "p-6",
29
- };
30
-
31
33
  if (loading) {
32
34
  return (
33
- <div className={cn("bg-background border border-border rounded-xl", sizeClasses[size], className)}>
34
- <div className="flex items-center justify-center h-48">
35
- <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
35
+ <div className={cn("udb-card", SURFACE_CLASS[size], className)}>
36
+ <div className="udb-analytics-card__status" role="status">
37
+ <Loader2 className="udb-spin udb-analytics-card__spinner" aria-hidden="true" />
36
38
  </div>
37
39
  </div>
38
40
  );
@@ -40,22 +42,22 @@ export const AnalyticsCard = ({
40
42
 
41
43
  if (error) {
42
44
  return (
43
- <div className={cn("bg-background border border-border rounded-xl", sizeClasses[size], className)}>
44
- <div className="flex items-center justify-center h-48 gap-3 text-destructive">
45
- <AlertCircle className="h-5 w-5" />
46
- <p className="text-sm">{error}</p>
45
+ <div className={cn("udb-card", SURFACE_CLASS[size], className)}>
46
+ <div className="udb-analytics-card__status udb-analytics-card__status--error" role="alert">
47
+ <AlertCircle className="udb-analytics-card__error-icon" aria-hidden="true" />
48
+ <p className="udb-analytics-card__error-text">{error}</p>
47
49
  </div>
48
50
  </div>
49
51
  );
50
52
  }
51
53
 
52
54
  return (
53
- <div className={cn("bg-background border border-border rounded-xl", sizeClasses[size], className)}>
55
+ <div className={cn("udb-card", SURFACE_CLASS[size], className)}>
54
56
  {/* Header */}
55
57
  {(title || description) && (
56
- <div className="mb-6">
57
- {title && <h3 className="text-lg font-semibold text-foreground">{title}</h3>}
58
- {description && <p className="text-sm text-muted-foreground mt-1">{description}</p>}
58
+ <div className="udb-analytics-card__header">
59
+ {title && <h3 className="udb-analytics-card__title">{title}</h3>}
60
+ {description && <p className="udb-analytics-card__description">{description}</p>}
59
61
  </div>
60
62
  )}
61
63
 
@@ -64,7 +66,7 @@ export const AnalyticsCard = ({
64
66
 
65
67
  {/* Metrics */}
66
68
  {metrics && metrics.length > 0 && (
67
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
69
+ <div className="udb-analytics-card__metrics">
68
70
  {metrics.map((metric) => (
69
71
  <MetricCard key={metric.id} metric={metric} size="sm" />
70
72
  ))}