@umituz/web-dashboard 3.2.4 → 4.1.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.
- package/README.md +228 -14
- package/package.json +23 -30
- package/src/domains/analytics/components/AnalyticsCard.tsx +43 -42
- package/src/domains/analytics/components/AnalyticsChart.tsx +345 -149
- package/src/domains/analytics/components/AnalyticsLayout.tsx +93 -112
- package/src/domains/analytics/components/MetricCard.tsx +29 -87
- package/src/domains/analytics/hooks/index.ts +7 -1
- package/src/domains/analytics/hooks/useAnalytics.ts +144 -139
- package/src/domains/analytics/index.ts +11 -0
- package/src/domains/analytics/services/AnalyticsEngineService.ts +103 -55
- package/src/domains/analytics/services/PerformanceService.ts +117 -69
- package/src/domains/analytics/types/analytics.ts +20 -6
- package/src/domains/analytics/types/index.ts +3 -0
- package/src/domains/analytics/utils/analytics.test.ts +183 -0
- package/src/domains/analytics/utils/analytics.ts +26 -9
- package/src/domains/analytics/utils/i18nKeys.ts +40 -0
- package/src/domains/analytics/utils/index.ts +2 -0
- package/src/domains/auth/components/AuthLayout.tsx +63 -54
- package/src/domains/auth/components/ForgotPasswordForm.tsx +96 -98
- package/src/domains/auth/components/LoginForm.tsx +167 -226
- package/src/domains/auth/components/RegisterForm.tsx +202 -272
- package/src/domains/auth/components/ResetPasswordForm.tsx +105 -151
- package/src/domains/auth/components/shared/AuthErrorBanner.tsx +35 -0
- package/src/domains/auth/components/shared/EmailInput.tsx +91 -0
- package/src/domains/auth/components/shared/PasswordInput.tsx +119 -0
- package/src/domains/auth/components/shared/PasswordStrengthIndicator.tsx +75 -0
- package/src/domains/auth/components/shared/SocialLoginButtons.tsx +58 -0
- package/src/domains/auth/components/shared/SocialProviderIcons.tsx +48 -0
- package/src/domains/auth/components/shared/index.ts +20 -0
- package/src/domains/auth/hooks/index.ts +1 -0
- package/src/domains/auth/hooks/useAuth.ts +151 -232
- package/src/domains/auth/index.ts +8 -4
- package/src/domains/auth/types/auth.ts +26 -5
- package/src/domains/auth/types/index.ts +1 -0
- package/src/domains/auth/utils/auth.test.ts +182 -0
- package/src/domains/auth/utils/auth.ts +71 -130
- package/src/domains/auth/utils/i18nKeys.ts +127 -0
- package/src/domains/auth/utils/index.ts +7 -1
- package/src/domains/auth/utils/secureToken.ts +33 -0
- package/src/domains/auth/utils/validationKeys.ts +23 -0
- package/src/domains/calendar/hooks/useCalendar.ts +145 -151
- package/src/domains/calendar/index.ts +0 -1
- package/src/domains/calendar/services/CalendarService.ts +122 -179
- package/src/domains/calendar/utils/contentItemMapper.test.ts +108 -0
- package/src/domains/calendar/utils/contentItemMapper.ts +99 -0
- package/src/domains/calendar/utils/index.ts +33 -30
- package/src/domains/layouts/components/BrandLogo.tsx +12 -9
- package/src/domains/layouts/components/DashboardHeader.tsx +78 -80
- package/src/domains/layouts/components/DashboardLayout.tsx +45 -21
- package/src/domains/layouts/components/DashboardSidebar.tsx +48 -51
- package/src/domains/layouts/types/layout.ts +7 -1
- package/src/domains/layouts/types/user.ts +3 -1
- package/src/domains/modules/build.test.tsx +97 -0
- package/src/domains/modules/build.tsx +71 -0
- package/src/domains/modules/index.ts +2 -0
- package/src/domains/modules/types.ts +29 -0
- package/src/domains/onboarding/components/AppFocusStep.tsx +99 -68
- package/src/domains/onboarding/components/OnboardingWizard.tsx +109 -183
- package/src/domains/onboarding/components/PlanStep.tsx +156 -123
- package/src/domains/onboarding/components/PlatformsStep.tsx +74 -54
- package/src/domains/onboarding/components/StepNavigation.tsx +108 -0
- package/src/domains/onboarding/components/StepProgress.tsx +62 -0
- package/src/domains/onboarding/components/UserTypeStep.tsx +59 -80
- package/src/domains/onboarding/components/index.ts +4 -0
- package/src/domains/onboarding/hooks/useOnboarding.ts +76 -66
- package/src/domains/onboarding/index.ts +1 -0
- package/src/domains/onboarding/types/onboarding.ts +57 -17
- package/src/domains/onboarding/utils/i18nKeys.ts +72 -0
- package/src/domains/onboarding/utils/index.ts +2 -0
- package/src/domains/permissions/PermissionsContext.tsx +103 -0
- package/src/domains/permissions/hasPermission.test.ts +50 -0
- package/src/domains/permissions/hasPermission.ts +38 -0
- package/src/domains/permissions/index.ts +19 -0
- package/src/domains/permissions/types.ts +35 -0
- package/src/domains/permissions/utils/i18nKeys.ts +11 -0
- package/src/domains/session/DemoLogin.tsx +50 -0
- package/src/domains/session/OrgSwitcher.tsx +110 -0
- package/src/domains/session/SessionContext.tsx +150 -0
- package/src/domains/session/inMemoryAuthAdapter.ts +53 -0
- package/src/domains/session/index.ts +18 -0
- package/src/domains/session/types.ts +47 -0
- package/src/domains/session/utils/i18nKeys.ts +12 -0
- package/src/domains/settings/components/AuditLogList.tsx +59 -0
- package/src/domains/settings/components/PermissionMatrixScreen.tsx +80 -0
- package/src/domains/settings/components/SettingsLayout.tsx +75 -60
- package/src/domains/settings/components/SettingsSection.tsx +35 -22
- package/src/domains/settings/components/UserManagementTable.tsx +69 -0
- package/src/domains/settings/components/index.ts +10 -0
- package/src/domains/settings/hooks/useSettings.ts +77 -52
- package/src/domains/settings/types/settings.ts +2 -0
- package/src/domains/settings/utils/i18nKeys.ts +18 -0
- package/src/domains/settings/utils/index.ts +1 -0
- package/src/domains/settings/utils/settings.ts +11 -4
- package/src/i18n/I18nContext.tsx +33 -0
- package/src/i18n/defaultT.test.ts +43 -0
- package/src/i18n/defaultT.ts +34 -0
- package/src/i18n/index.ts +7 -0
- package/src/i18n/types.ts +13 -0
- package/src/index.ts +1 -7
- package/src/primitives/Button.tsx +24 -0
- package/src/primitives/Checkbox.tsx +17 -0
- package/src/primitives/Input.tsx +23 -0
- package/src/primitives/Skeleton.tsx +6 -0
- package/src/primitives/cn.ts +10 -0
- package/src/primitives/index.ts +23 -0
- package/src/styles/dashboard.css +2731 -0
- package/src/styles/tokens.contract.test.ts +91 -0
- package/src/styles/tokens.css +116 -0
- package/src/domains/billing/components/BillingLayout.tsx +0 -63
- package/src/domains/billing/components/BillingPage.tsx +0 -49
- package/src/domains/billing/components/BillingPortal.tsx +0 -198
- package/src/domains/billing/components/InvoiceCard.tsx +0 -143
- package/src/domains/billing/components/PaymentMethodsList.tsx +0 -116
- package/src/domains/billing/components/PlanComparison.tsx +0 -199
- package/src/domains/billing/components/UsageCard.tsx +0 -103
- package/src/domains/billing/components/index.ts +0 -13
- package/src/domains/billing/constants/billing.ts +0 -69
- package/src/domains/billing/hooks/index.ts +0 -7
- package/src/domains/billing/hooks/useBilling.ts +0 -385
- package/src/domains/billing/index.ts +0 -73
- package/src/domains/billing/types/billing.ts +0 -342
- package/src/domains/billing/types/index.ts +0 -30
- package/src/domains/billing/utils/billing.ts +0 -360
- package/src/domains/billing/utils/index.ts +0 -31
- package/src/domains/billing/utils/polar.ts +0 -63
- package/src/domains/calendar/components/index.ts +0 -7
package/README.md
CHANGED
|
@@ -1,32 +1,246 @@
|
|
|
1
1
|
# @umituz/web-dashboard
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
-
|
|
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
|
+
- **Styling is shipped, not assumed.** Import `@umituz/web-dashboard/styles.css`
|
|
18
|
+
once; all components use `udb-*` BEM classes.
|
|
19
|
+
- **No i18n runtime.** `react-i18next` is out; hosts bridge their own `t` via
|
|
20
|
+
`<DashboardI18nProvider t={i18n.t}>` (see below). Without a provider,
|
|
21
|
+
keys humanize to readable English.
|
|
22
|
+
- **No Firebase.** The calendar persists through the injected
|
|
23
|
+
`ICalendarDatabase` seam; `createInMemoryCalendarDatabase()` is the default.
|
|
24
|
+
- **recharts is out.** `AnalyticsChart` renders line/area/bar/pie/donut as
|
|
25
|
+
dependency-free SVG with native `<title>` tooltips.
|
|
26
|
+
- **New fleet modules**: `./permissions`, `./session`, `./modules` — the
|
|
27
|
+
texora/visora pattern (one module list drives routes + nav + guards) as
|
|
28
|
+
reusable building blocks.
|
|
29
|
+
|
|
30
|
+
## What changed in 4.1
|
|
31
|
+
|
|
32
|
+
- **The design system is back — for real this time.** 4.0 dropped
|
|
33
|
+
`@umituz/web-design-system` entirely; 4.1 makes it a runtime dependency
|
|
34
|
+
again and actually renders it:
|
|
35
|
+
- `./primitives` (`Button`, `Input`, `Checkbox`, `Skeleton`, `cn`) are thin
|
|
36
|
+
adapters that **delegate to the DS components** — the dashboard prop API
|
|
37
|
+
is unchanged, call sites need zero edits.
|
|
38
|
+
- `UserManagementTable` renders the DS `DataTable`; `MetricCard` composes
|
|
39
|
+
the DS `MetricCard`; `AnalyticsCard` is built on DS `Card` primitives.
|
|
40
|
+
- `styles.css` imports `@umituz/web-design-system/styles.css` transitively
|
|
41
|
+
(Tailwind v4 precompiled) — hosts still import only the dashboard
|
|
42
|
+
stylesheet.
|
|
43
|
+
- **`--u-*` tokens are now bridges.** `tokens.css` maps every color/radius/
|
|
44
|
+
shadow token to the DS `--color-*`/`--radius-*` variables (with the old
|
|
45
|
+
literal as fallback). Override either side; dark mode (`data-udb-theme="dark"`)
|
|
46
|
+
now sets DS variables and the bridge flows through.
|
|
47
|
+
- **Primitive base CSS is gone from `dashboard.css`.** Only dashboard-owned
|
|
48
|
+
modifiers remain (`udb-btn__spinner`, `udb-input--error/--with-toggle`,
|
|
49
|
+
`udb-skeleton--*`, `udb-card--sm/--lg`, `udb-table` for the permission
|
|
50
|
+
matrix); bases come from the DS stylesheet.
|
|
51
|
+
- **Peer footprint**: pulling the dashboard in transitively installs the DS's
|
|
52
|
+
Radix UI peers (npm 7+ handles this automatically; only imported components
|
|
53
|
+
end up in the bundle).
|
|
12
54
|
|
|
13
55
|
## Installation
|
|
14
56
|
|
|
15
57
|
```bash
|
|
16
58
|
npm install @umituz/web-dashboard
|
|
59
|
+
import "@umituz/web-dashboard/styles.css";
|
|
17
60
|
```
|
|
18
61
|
|
|
19
|
-
|
|
62
|
+
Peers: `react` + `react-dom` (^18 or ^19) and `react-router-dom` (^6 or ^7).
|
|
63
|
+
Runtime deps: `@umituz/web-design-system` + `lucide-react`. Nothing else.
|
|
64
|
+
|
|
65
|
+
### Tailwind hosts
|
|
66
|
+
|
|
67
|
+
Every `udb-*` rule sets its own `box-sizing`/`background`/`border` explicitly,
|
|
68
|
+
so preflight cannot leak in — but load order still matters with `*`-selector
|
|
69
|
+
resets: import the package stylesheet **after** your base layer.
|
|
70
|
+
|
|
71
|
+
### Theming & dark mode
|
|
72
|
+
|
|
73
|
+
`tokens.css` bridges every `--u-*` token to a design-system variable
|
|
74
|
+
(`--u-color-primary: var(--color-primary, #2563eb)`), so you can theme two
|
|
75
|
+
ways: override the `--u-*` bridge heads, or set the DS `--color-*`/`--radius-*`
|
|
76
|
+
variables (e.g. from your own Tailwind `@theme`). Dark mode is host-driven:
|
|
77
|
+
put `data-udb-theme="dark"` on any ancestor — the dashboard dark block sets
|
|
78
|
+
the DS variables and both layers switch together.
|
|
20
79
|
|
|
21
|
-
|
|
22
|
-
import { useAnalytics } from '@umituz/web-dashboard';
|
|
80
|
+
## Entry points
|
|
23
81
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
82
|
+
```bash
|
|
83
|
+
@umituz/web-dashboard/styles.css # component stylesheet (udb-* BEM)
|
|
84
|
+
@umituz/web-dashboard/tokens.css # --u-* custom properties only
|
|
85
|
+
@umituz/web-dashboard/primitives # Button, Input, Checkbox, Skeleton, cn
|
|
86
|
+
@umituz/web-dashboard/i18n # DashboardI18nProvider, useT, defaultT
|
|
87
|
+
@umituz/web-dashboard/permissions # PermissionMatrix, hasPermission, guards
|
|
88
|
+
@umituz/web-dashboard/session # AuthAdapter, SessionProvider, OrgSwitcher, DemoLogin
|
|
89
|
+
@umituz/web-dashboard/modules # DashboardModule, buildRoutes, buildNav
|
|
90
|
+
@umituz/web-dashboard/layouts # DashboardLayout, DashboardSidebar, DashboardHeader
|
|
91
|
+
@umituz/web-dashboard/settings # SettingsLayout, admin skeletons (users, matrix, audit)
|
|
92
|
+
@umituz/web-dashboard/onboarding # OnboardingWizard + steps, useOnboarding
|
|
93
|
+
@umituz/web-dashboard/auth # Login/Register/Forgot/Reset forms, AuthLayout
|
|
94
|
+
@umituz/web-dashboard/analytics # AnalyticsLayout, SVG AnalyticsChart, services
|
|
95
|
+
@umituz/web-dashboard/calendar # useCalendar, CalendarService (injected DB)
|
|
96
|
+
@umituz/web-dashboard/config # dashboard config helpers
|
|
28
97
|
```
|
|
29
98
|
|
|
99
|
+
Each domain also exposes `components`, `hooks`, `utils`, `types`
|
|
100
|
+
sub-entries (and `services` for analytics/calendar). The new v4 modules
|
|
101
|
+
(`permissions`, `session`, `modules`) are **deep-entry only** — they are not
|
|
102
|
+
re-exported from the root, so router-less hosts never pull router code in
|
|
103
|
+
transitively.
|
|
104
|
+
|
|
105
|
+
## The v4 composition
|
|
106
|
+
|
|
107
|
+
A host app assembles the skeleton from four seams:
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { DashboardI18nProvider } from "@umituz/web-dashboard/i18n";
|
|
111
|
+
import { SessionProvider, createInMemoryAuthAdapter } from "@umituz/web-dashboard/session";
|
|
112
|
+
import { PermissionsProvider } from "@umituz/web-dashboard/permissions";
|
|
113
|
+
|
|
114
|
+
const adapter = createInMemoryAuthAdapter({ users: DEMO_USERS });
|
|
115
|
+
|
|
116
|
+
<DashboardI18nProvider t={i18n.t}>
|
|
117
|
+
<SessionProvider adapter={adapter}>
|
|
118
|
+
<PermissionsProvider matrix={ROLE_PERMISSIONS} role={user?.role}>
|
|
119
|
+
<App />
|
|
120
|
+
</PermissionsProvider>
|
|
121
|
+
</SessionProvider>
|
|
122
|
+
</DashboardI18nProvider>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### One module list → routes + nav + guards
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
import { buildRoutes, buildNav } from "@umituz/web-dashboard/modules";
|
|
129
|
+
import { usePermissions } from "@umituz/web-dashboard/permissions";
|
|
130
|
+
|
|
131
|
+
const MODULES: DashboardModule[] = [
|
|
132
|
+
{ path: "/dashboard", labelKey: "nav.dashboard", icon: LayoutDashboard,
|
|
133
|
+
navGroup: "nav.overview", order: 1, permission: "dashboard.view",
|
|
134
|
+
element: <DashboardPage /> },
|
|
135
|
+
{ path: "/sales/:id", labelKey: "nav.salesDetail", icon: Circle,
|
|
136
|
+
navGroup: "nav.commercial", inNav: false, permission: "sales.view",
|
|
137
|
+
element: <SalesDetailPage /> },
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
// routes: element pre-wrapped in <RequirePermission>
|
|
141
|
+
const routes = buildRoutes(MODULES); // -> <Route path key={...} {...route} />
|
|
142
|
+
|
|
143
|
+
// nav: permission-filtered, grouped, ordered -> feed DashboardLayout config
|
|
144
|
+
const { can } = usePermissions();
|
|
145
|
+
const sidebarGroups = buildNav(MODULES, { can });
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Groups keep their first-appearance order in the list; `order` sorts items
|
|
149
|
+
within a group; `inNav: false` hides detail routes from the sidebar.
|
|
150
|
+
|
|
151
|
+
### Permissions
|
|
152
|
+
|
|
153
|
+
`hasPermission(matrix, role, permission)` is pure and deny-by-default.
|
|
154
|
+
Matrix grants are `"*"` (everything), `"sales.*"` (module wildcard) or exact
|
|
155
|
+
strings. `<RequirePermission permission>` guards pages (lock screen
|
|
156
|
+
fallback); `<Can>` is its inline twin.
|
|
157
|
+
|
|
158
|
+
### Session
|
|
159
|
+
|
|
160
|
+
Hosts adapt their auth to `AuthAdapter` (`login`/`logout`/`current`;
|
|
161
|
+
`listTenants`/`demoLogin` optional). `useSession()` exposes user, tenants,
|
|
162
|
+
active tenant, status + `switchTenant`. `OrgSwitcher` renders the tenant
|
|
163
|
+
dropdown; `DemoLogin` the one-click demo entry.
|
|
164
|
+
|
|
165
|
+
### Calendar
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
import { CalendarService, createInMemoryCalendarDatabase } from "@umituz/web-dashboard/calendar/services";
|
|
169
|
+
|
|
170
|
+
// Bring your own backend:
|
|
171
|
+
class MyDatabase implements ICalendarDatabase { /* ... */ }
|
|
172
|
+
const service = new CalendarService(new MyDatabase());
|
|
173
|
+
// or demo/test:
|
|
174
|
+
const demo = new CalendarService(createInMemoryCalendarDatabase());
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Analytics
|
|
178
|
+
|
|
179
|
+
`AnalyticsChart` consumes the same `ChartConfig` as v3 for
|
|
180
|
+
line/area/bar/pie/donut. Tooltips are native SVG `<title>` elements;
|
|
181
|
+
legends are color chips; unsupported types render an empty state.
|
|
182
|
+
`useAnalytics` stays dependency-injected (`createStubAnalyticsApiClient`).
|
|
183
|
+
|
|
184
|
+
## i18n contract
|
|
185
|
+
|
|
186
|
+
Components render **i18n keys**, never English strings. Bridge your runtime
|
|
187
|
+
once:
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
import { DashboardI18nProvider } from "@umituz/web-dashboard/i18n";
|
|
191
|
+
|
|
192
|
+
<DashboardI18nProvider t={i18n.t}> {/* i18next's t — signature already matches */}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Interpolation uses the standard `{{placeholder}}` syntax. Without a
|
|
196
|
+
provider, `useT()` falls back to `defaultT`, which humanizes keys
|
|
197
|
+
(`auth.login.welcomeBack` → "Welcome back") so a bare install still renders
|
|
198
|
+
readably. The per-domain key catalogs are the string contract:
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import { AUTH_KEYS } from "@umituz/web-dashboard/auth";
|
|
202
|
+
import { ANALYTICS_KEYS } from "@umituz/web-dashboard/analytics";
|
|
203
|
+
import { ONBOARDING_KEYS } from "@umituz/web-dashboard/onboarding";
|
|
204
|
+
import { SETTINGS_ADMIN_KEYS } from "@umituz/web-dashboard/settings/utils";
|
|
205
|
+
import { PERMISSIONS_KEYS } from "@umituz/web-dashboard/permissions";
|
|
206
|
+
import { SESSION_KEYS } from "@umituz/web-dashboard/session";
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Platform compatibility
|
|
210
|
+
|
|
211
|
+
- React 18 and 19, react-router-dom 6 and 7
|
|
212
|
+
- `generateResetToken` requires `crypto.getRandomValues` (all modern browsers,
|
|
213
|
+
Node ≥ 19) and **throws** rather than falling back to an insecure PRNG
|
|
214
|
+
- Rendering targets the DOM; React Native is not supported
|
|
215
|
+
|
|
216
|
+
## Development
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
npm install
|
|
220
|
+
npm run lint # eslint
|
|
221
|
+
npm run typecheck # tsc --noEmit
|
|
222
|
+
npm test # vitest unit tests (pure utils + module builders)
|
|
223
|
+
npm run test:watch
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
CI runs lint + typecheck + test on every push/PR to `main`.
|
|
227
|
+
|
|
228
|
+
## Migration: 3.x → 4.0
|
|
229
|
+
|
|
230
|
+
1. Billing UI: switch imports to `@umituz/web-polar-payment/portal`.
|
|
231
|
+
2. Design-system imports: use `@umituz/web-dashboard/primitives`
|
|
232
|
+
(`Button`/`Input`/`Checkbox`/`Skeleton` cover the used surface).
|
|
233
|
+
3. Styling: import `styles.css` once; Tailwind utility classes no longer
|
|
234
|
+
exist inside the package. Config values that used to carry Tailwind
|
|
235
|
+
strings changed: `PlatformOption.color` is a plain CSS color string,
|
|
236
|
+
`PlanOption.badgeColor` became `badgeTone: "muted" | "primary" | "accent"`.
|
|
237
|
+
4. i18n: wrap your app in `DashboardI18nProvider t={i18n.t}`; the
|
|
238
|
+
`react-i18next` peer is gone.
|
|
239
|
+
5. Calendar: pass your own `ICalendarDatabase` (or the in-memory default);
|
|
240
|
+
the Firebase implementation was removed.
|
|
241
|
+
6. v3.2.5 behavioral notes still apply (opt-in mock auth, route skeletons
|
|
242
|
+
off by default, `PerformanceService.startMonitoring()` explicit).
|
|
243
|
+
|
|
30
244
|
## License
|
|
31
245
|
|
|
32
246
|
MIT
|
package/package.json
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/web-dashboard",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "4.1.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":
|
|
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,22 @@
|
|
|
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
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"lucide-react": "^0.577.0",
|
|
73
|
-
"recharts": "^2.15.0",
|
|
74
|
-
"tailwind-merge": "^3.5.0"
|
|
74
|
+
"@umituz/web-design-system": "^4.0.2",
|
|
75
|
+
"lucide-react": "^0.577.0"
|
|
75
76
|
},
|
|
76
77
|
"devDependencies": {
|
|
78
|
+
"@types/node": "^22.20.5",
|
|
77
79
|
"@types/react": "^19.2.14",
|
|
78
80
|
"@types/react-dom": "^19.2.3",
|
|
79
81
|
"@typescript-eslint/eslint-plugin": "^8.57.2",
|
|
80
82
|
"@typescript-eslint/parser": "^8.57.2",
|
|
81
|
-
"@umituz/web-design-system": "^3.1.11",
|
|
82
|
-
"@umituz/web-firebase": "^3.6.0",
|
|
83
83
|
"eslint": "^10.0.2",
|
|
84
|
-
"firebase": "^12.0.0",
|
|
85
|
-
"react-i18next": "^16.5.4",
|
|
86
84
|
"react-router-dom": "^7.13.1",
|
|
87
|
-
"typescript": "^5.9.3"
|
|
85
|
+
"typescript": "^5.9.3",
|
|
86
|
+
"vitest": "^5.0.1"
|
|
88
87
|
},
|
|
89
88
|
"keywords": [
|
|
90
89
|
"dashboard",
|
|
@@ -113,13 +112,7 @@
|
|
|
113
112
|
"metrics",
|
|
114
113
|
"kpi",
|
|
115
114
|
"visualization",
|
|
116
|
-
"
|
|
117
|
-
"billing",
|
|
118
|
-
"subscription",
|
|
119
|
-
"payment",
|
|
120
|
-
"invoices",
|
|
121
|
-
"pricing",
|
|
122
|
-
"plans",
|
|
115
|
+
"svg-charts",
|
|
123
116
|
"react",
|
|
124
117
|
"typescript",
|
|
125
118
|
"components",
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Analytics Card
|
|
3
|
-
*
|
|
4
|
-
* Configurable analytics card with chart or metrics
|
|
2
|
+
* Analytics Card — chart/metric container on the design-system Card.
|
|
3
|
+
* Size modifiers ("sm"/"lg") are dashboard-owned padding overrides.
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
import { Loader2, AlertCircle } from "lucide-react";
|
|
8
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
Card,
|
|
9
|
+
CardHeader,
|
|
10
|
+
CardTitle,
|
|
11
|
+
CardDescription,
|
|
12
|
+
CardContent,
|
|
13
|
+
} from "@umituz/web-design-system/organisms";
|
|
14
|
+
import { cn } from "../../../primitives";
|
|
9
15
|
import type { AnalyticsCardProps } from "../types/analytics";
|
|
10
16
|
import { MetricCard } from "./MetricCard";
|
|
11
17
|
import { AnalyticsChart } from "./AnalyticsChart";
|
|
12
18
|
|
|
19
|
+
const SURFACE_CLASS = {
|
|
20
|
+
sm: "udb-card--sm",
|
|
21
|
+
md: undefined,
|
|
22
|
+
lg: "udb-card--lg",
|
|
23
|
+
full: undefined,
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
13
26
|
export const AnalyticsCard = ({
|
|
14
27
|
title,
|
|
15
28
|
description,
|
|
@@ -21,59 +34,47 @@ export const AnalyticsCard = ({
|
|
|
21
34
|
className,
|
|
22
35
|
children,
|
|
23
36
|
}: AnalyticsCardProps) => {
|
|
24
|
-
const sizeClasses = {
|
|
25
|
-
sm: "p-4",
|
|
26
|
-
md: "p-6",
|
|
27
|
-
lg: "p-8",
|
|
28
|
-
full: "p-6",
|
|
29
|
-
};
|
|
30
|
-
|
|
31
37
|
if (loading) {
|
|
32
38
|
return (
|
|
33
|
-
<
|
|
34
|
-
<div className="
|
|
35
|
-
<Loader2 className="
|
|
39
|
+
<Card className={cn(SURFACE_CLASS[size], className)}>
|
|
40
|
+
<div className="udb-analytics-card__status" role="status">
|
|
41
|
+
<Loader2 className="udb-spin udb-analytics-card__spinner" aria-hidden="true" />
|
|
36
42
|
</div>
|
|
37
|
-
</
|
|
43
|
+
</Card>
|
|
38
44
|
);
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
if (error) {
|
|
42
48
|
return (
|
|
43
|
-
<
|
|
44
|
-
<div className="
|
|
45
|
-
<AlertCircle className="
|
|
46
|
-
<p className="text
|
|
49
|
+
<Card className={cn(SURFACE_CLASS[size], className)}>
|
|
50
|
+
<div className="udb-analytics-card__status udb-analytics-card__status--error" role="alert">
|
|
51
|
+
<AlertCircle className="udb-analytics-card__error-icon" aria-hidden="true" />
|
|
52
|
+
<p className="udb-analytics-card__error-text">{error}</p>
|
|
47
53
|
</div>
|
|
48
|
-
</
|
|
54
|
+
</Card>
|
|
49
55
|
);
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
return (
|
|
53
|
-
<
|
|
54
|
-
{/* Header */}
|
|
59
|
+
<Card className={cn(SURFACE_CLASS[size], className)}>
|
|
55
60
|
{(title || description) && (
|
|
56
|
-
<
|
|
57
|
-
{title && <
|
|
58
|
-
{description && <
|
|
59
|
-
</
|
|
60
|
-
)}
|
|
61
|
-
|
|
62
|
-
{/* Chart */}
|
|
63
|
-
{chart && <AnalyticsChart config={chart} />}
|
|
64
|
-
|
|
65
|
-
{/* Metrics */}
|
|
66
|
-
{metrics && metrics.length > 0 && (
|
|
67
|
-
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
68
|
-
{metrics.map((metric) => (
|
|
69
|
-
<MetricCard key={metric.id} metric={metric} size="sm" />
|
|
70
|
-
))}
|
|
71
|
-
</div>
|
|
61
|
+
<CardHeader>
|
|
62
|
+
{title && <CardTitle>{title}</CardTitle>}
|
|
63
|
+
{description && <CardDescription>{description}</CardDescription>}
|
|
64
|
+
</CardHeader>
|
|
72
65
|
)}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
<CardContent>
|
|
67
|
+
{chart && <AnalyticsChart config={chart} />}
|
|
68
|
+
{metrics && metrics.length > 0 && (
|
|
69
|
+
<div className="udb-analytics-card__metrics">
|
|
70
|
+
{metrics.map((metric) => (
|
|
71
|
+
<MetricCard key={metric.id} metric={metric} size="sm" />
|
|
72
|
+
))}
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
{children}
|
|
76
|
+
</CardContent>
|
|
77
|
+
</Card>
|
|
77
78
|
);
|
|
78
79
|
};
|
|
79
80
|
|