@umituz/web-dashboard 4.7.1 → 5.0.1
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 +63 -88
- package/package.json +5 -7
- package/src/domains/analytics/components/AnalyticsCard.tsx +1 -6
- package/src/domains/analytics/components/AnalyticsChart.tsx +2 -14
- package/src/domains/analytics/components/AnalyticsLayout.tsx +3 -17
- package/src/domains/analytics/components/MetricCard.tsx +1 -6
- package/src/domains/analytics/utils/i18nKeys.ts +0 -9
- package/src/domains/auth/components/AuthLayout.tsx +1 -26
- package/src/domains/auth/components/ForgotPasswordForm.tsx +2 -14
- package/src/domains/auth/components/LoginForm.tsx +2 -15
- package/src/domains/auth/components/RegisterForm.tsx +3 -14
- package/src/domains/auth/components/ResetPasswordForm.tsx +3 -14
- package/src/domains/auth/components/shared/AuthErrorBanner.tsx +1 -11
- package/src/domains/auth/components/shared/EmailInput.tsx +2 -20
- package/src/domains/auth/components/shared/PasswordInput.tsx +3 -22
- package/src/domains/auth/components/shared/PasswordStrengthIndicator.tsx +1 -13
- package/src/domains/auth/components/shared/SocialLoginButtons.tsx +1 -15
- package/src/domains/auth/utils/i18nKeys.ts +0 -18
- package/src/domains/charts/components.tsx +2 -2
- package/src/domains/layouts/components/BrandLogo.tsx +7 -22
- package/src/domains/layouts/components/DashboardHeader.tsx +2 -23
- package/src/domains/layouts/components/DashboardLayout.tsx +2 -39
- package/src/domains/layouts/components/DashboardSidebar.tsx +3 -21
- package/src/domains/permissions/PermissionsContext.tsx +1 -17
- package/src/domains/permissions/utils/i18nKeys.ts +0 -5
- package/src/domains/session/DemoLogin.tsx +2 -10
- package/src/domains/session/OrgSwitcher.tsx +2 -10
- package/src/domains/settings/components/AuditLogList.tsx +1 -10
- package/src/domains/settings/components/PermissionMatrixScreen.tsx +1 -8
- package/src/domains/settings/components/SettingsLayout.tsx +1 -21
- package/src/domains/settings/components/SettingsSection.tsx +1 -13
- package/src/domains/settings/components/UserManagementTable.tsx +1 -8
- package/src/domains/settings/utils/i18nKeys.ts +0 -6
- package/src/index.ts +0 -17
- package/src/styles/dashboard.contract.test.ts +42 -0
- package/src/styles/dashboard.css +492 -920
- package/src/i18n/I18nContext.tsx +0 -33
- package/src/i18n/defaultT.test.ts +0 -43
- package/src/i18n/defaultT.ts +0 -34
- package/src/i18n/index.ts +0 -7
- package/src/i18n/types.ts +0 -13
- package/src/primitives/Button.tsx +0 -24
- package/src/primitives/Checkbox.tsx +0 -17
- package/src/primitives/Input.tsx +0 -23
- package/src/primitives/Skeleton.tsx +0 -6
- package/src/primitives/cn.ts +0 -10
- package/src/primitives/index.ts +0 -23
- package/src/styles/tokens.contract.test.ts +0 -91
- package/src/styles/tokens.css +0 -116
package/README.md
CHANGED
|
@@ -1,56 +1,31 @@
|
|
|
1
1
|
# @umituz/web-dashboard
|
|
2
2
|
|
|
3
|
-
Installable app skeleton for SaaS dashboards: layouts, auth,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
vanilla-CSS and Tailwind hosts.
|
|
3
|
+
Installable app skeleton for SaaS dashboards: layouts, auth, analytics, a
|
|
4
|
+
content calendar, and a permissions/session/module registry. Every domain is
|
|
5
|
+
an independent entry point — import only what your app uses. UI atoms and
|
|
6
|
+
design tokens come from [@umituz/web-design-system](https://github.com/umituz/web-design-system);
|
|
7
|
+
translations from [@umituz/web-localization](https://github.com/umituz/web-localization).
|
|
9
8
|
|
|
10
9
|
The package is **source-distributed** (published as raw TypeScript in `src/`).
|
|
11
10
|
Your bundler compiles it alongside your app; no prebuilt bundle ships.
|
|
12
11
|
|
|
13
|
-
## What changed in
|
|
14
|
-
|
|
15
|
-
- **
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- **No
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`
|
|
24
|
-
- **
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
+
## What changed in v5 (major)
|
|
13
|
+
|
|
14
|
+
- **No more local primitives.** The `./primitives` entry point is gone;
|
|
15
|
+
components import `@umituz/web-design-system` directly. Hosts that need
|
|
16
|
+
`Button`/`Input`/`Checkbox`/`Skeleton`/`cn` import them from the design
|
|
17
|
+
system.
|
|
18
|
+
- **No more `--u-*` token layer.** `tokens.css` is deleted. The `udb-*`
|
|
19
|
+
stylesheet consumes the design-system variables (`--color-*`, `--radius-*`,
|
|
20
|
+
`--font-*`, `--shadow-*`) directly; dashboard-specific knobs (typography
|
|
21
|
+
scale, sidebar geometry) are `--udb-*` custom properties defined in
|
|
22
|
+
`styles.css` itself.
|
|
23
|
+
- **No more local i18n copy.** The `./i18n` entry point is gone; `useT` now
|
|
24
|
+
comes from `@umituz/web-localization/react` (`I18nProvider` there replaces
|
|
25
|
+
`DashboardI18nProvider`, same `t` signature).
|
|
26
|
+
- **No bundled dark preset.** Dark mode follows the design system: hosts set
|
|
27
|
+
dark values on the DS variables under `[data-theme="dark"]` / `.dark`
|
|
28
|
+
(see the design system's `bridge.css` contract).
|
|
54
29
|
|
|
55
30
|
## Installation
|
|
56
31
|
|
|
@@ -60,7 +35,8 @@ import "@umituz/web-dashboard/styles.css";
|
|
|
60
35
|
```
|
|
61
36
|
|
|
62
37
|
Peers: `react` + `react-dom` (^18 or ^19) and `react-router-dom` (^6 or ^7).
|
|
63
|
-
Runtime deps: `@umituz/web-design-system
|
|
38
|
+
Runtime deps: `@umituz/web-design-system`, `@umituz/web-localization`,
|
|
39
|
+
`lucide-react`. Nothing else.
|
|
64
40
|
|
|
65
41
|
### Tailwind hosts
|
|
66
42
|
|
|
@@ -70,56 +46,53 @@ resets: import the package stylesheet **after** your base layer.
|
|
|
70
46
|
|
|
71
47
|
### Theming & dark mode
|
|
72
48
|
|
|
73
|
-
`
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
49
|
+
`styles.css` imports `@umituz/web-design-system/styles.css` transitively and
|
|
50
|
+
reads its variables, so theming has exactly one path: set the DS variables.
|
|
51
|
+
Vanilla-CSS hosts define `--app-*` slots via the design system's `bridge.css`;
|
|
52
|
+
Tailwind hosts override `--color-*` from their own `@theme`. Dashboard-only
|
|
53
|
+
knobs (`--udb-font-size-*`, `--udb-space-*`, `--udb-sidebar-*`,
|
|
54
|
+
`--udb-header-height`, `--udb-border-strong`, `--udb-radius-pill`) can be overridden anywhere in the cascade.
|
|
79
55
|
|
|
80
56
|
## Entry points
|
|
81
57
|
|
|
82
58
|
```bash
|
|
83
59
|
@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
60
|
@umituz/web-dashboard/permissions # PermissionMatrix, hasPermission, guards
|
|
88
61
|
@umituz/web-dashboard/session # AuthAdapter, SessionProvider, OrgSwitcher, DemoLogin
|
|
89
62
|
@umituz/web-dashboard/modules # DashboardModule, buildRoutes, buildNav
|
|
90
63
|
@umituz/web-dashboard/layouts # DashboardLayout, DashboardSidebar, DashboardHeader
|
|
91
64
|
@umituz/web-dashboard/settings # SettingsLayout, admin skeletons (users, matrix, audit)
|
|
92
|
-
@umituz/web-dashboard/onboarding # OnboardingWizard + steps, useOnboarding
|
|
93
65
|
@umituz/web-dashboard/auth # Login/Register/Forgot/Reset forms, AuthLayout
|
|
94
66
|
@umituz/web-dashboard/analytics # AnalyticsLayout, SVG AnalyticsChart, services
|
|
95
67
|
@umituz/web-dashboard/calendar # useCalendar, CalendarService (injected DB)
|
|
96
68
|
@umituz/web-dashboard/config # dashboard config helpers
|
|
69
|
+
@umituz/web-dashboard/charts # dependency-free SVG chart components
|
|
97
70
|
```
|
|
98
71
|
|
|
99
72
|
Each domain also exposes `components`, `hooks`, `utils`, `types`
|
|
100
|
-
sub-entries (and `services` for analytics/calendar). The
|
|
73
|
+
sub-entries (and `services` for analytics/calendar). The v4 modules
|
|
101
74
|
(`permissions`, `session`, `modules`) are **deep-entry only** — they are not
|
|
102
75
|
re-exported from the root, so router-less hosts never pull router code in
|
|
103
76
|
transitively.
|
|
104
77
|
|
|
105
|
-
## The
|
|
78
|
+
## The composition
|
|
106
79
|
|
|
107
80
|
A host app assembles the skeleton from four seams:
|
|
108
81
|
|
|
109
82
|
```tsx
|
|
110
|
-
import {
|
|
83
|
+
import { I18nProvider } from "@umituz/web-localization/react";
|
|
111
84
|
import { SessionProvider, createInMemoryAuthAdapter } from "@umituz/web-dashboard/session";
|
|
112
85
|
import { PermissionsProvider } from "@umituz/web-dashboard/permissions";
|
|
113
86
|
|
|
114
87
|
const adapter = createInMemoryAuthAdapter({ users: DEMO_USERS });
|
|
115
88
|
|
|
116
|
-
<
|
|
89
|
+
<I18nProvider t={i18n.t}>
|
|
117
90
|
<SessionProvider adapter={adapter}>
|
|
118
91
|
<PermissionsProvider matrix={ROLE_PERMISSIONS} role={user?.role}>
|
|
119
92
|
<App />
|
|
120
93
|
</PermissionsProvider>
|
|
121
94
|
</SessionProvider>
|
|
122
|
-
</
|
|
95
|
+
</I18nProvider>
|
|
123
96
|
```
|
|
124
97
|
|
|
125
98
|
### One module list → routes + nav + guards
|
|
@@ -176,20 +149,20 @@ const demo = new CalendarService(createInMemoryCalendarDatabase());
|
|
|
176
149
|
|
|
177
150
|
### Analytics
|
|
178
151
|
|
|
179
|
-
`AnalyticsChart` consumes
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
152
|
+
`AnalyticsChart` consumes `ChartConfig` for line/area/bar/pie/donut. Tooltips
|
|
153
|
+
are native SVG `<title>` elements; legends are color chips; unsupported types
|
|
154
|
+
render an empty state. `useAnalytics` stays dependency-injected
|
|
155
|
+
(`createStubAnalyticsApiClient`).
|
|
183
156
|
|
|
184
157
|
## i18n contract
|
|
185
158
|
|
|
186
159
|
Components render **i18n keys**, never English strings. Bridge your runtime
|
|
187
|
-
once
|
|
160
|
+
once with `@umituz/web-localization/react`:
|
|
188
161
|
|
|
189
162
|
```tsx
|
|
190
|
-
import {
|
|
163
|
+
import { I18nProvider } from "@umituz/web-localization/react";
|
|
191
164
|
|
|
192
|
-
<
|
|
165
|
+
<I18nProvider t={i18n.t}> {/* i18next's t — signature already matches */}
|
|
193
166
|
```
|
|
194
167
|
|
|
195
168
|
Interpolation uses the standard `{{placeholder}}` syntax. Without a
|
|
@@ -200,7 +173,6 @@ readably. The per-domain key catalogs are the string contract:
|
|
|
200
173
|
```ts
|
|
201
174
|
import { AUTH_KEYS } from "@umituz/web-dashboard/auth";
|
|
202
175
|
import { ANALYTICS_KEYS } from "@umituz/web-dashboard/analytics";
|
|
203
|
-
import { ONBOARDING_KEYS } from "@umituz/web-dashboard/onboarding";
|
|
204
176
|
import { SETTINGS_ADMIN_KEYS } from "@umituz/web-dashboard/settings/utils";
|
|
205
177
|
import { PERMISSIONS_KEYS } from "@umituz/web-dashboard/permissions";
|
|
206
178
|
import { SESSION_KEYS } from "@umituz/web-dashboard/session";
|
|
@@ -223,23 +195,26 @@ npm test # vitest unit tests (pure utils + module builders)
|
|
|
223
195
|
npm run test:watch
|
|
224
196
|
```
|
|
225
197
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
2.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
`
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
198
|
+
## Migration: 4.x → 5.0
|
|
199
|
+
|
|
200
|
+
1. `@umituz/web-dashboard/primitives` → import `Button`/`Input`/`Checkbox`/
|
|
201
|
+
`Skeleton` from `@umituz/web-design-system/atoms` and `cn` from
|
|
202
|
+
`@umituz/web-design-system/utils/cn`.
|
|
203
|
+
2. `@umituz/web-dashboard/i18n` → `DashboardI18nProvider` becomes
|
|
204
|
+
`I18nProvider` from `@umituz/web-localization/react`; `useT`, `defaultT`
|
|
205
|
+
and `TranslateFn` come from the same entry point.
|
|
206
|
+
3. `@umituz/web-dashboard/tokens.css` → drop the import; `styles.css` is the
|
|
207
|
+
only stylesheet. Migrate `--u-*` overrides to the DS variables
|
|
208
|
+
(`--u-color-primary` → `--color-primary`, `--u-color-surface` →
|
|
209
|
+
`--color-card`, `--u-color-bg` → `--color-background`, `--u-color-text` →
|
|
210
|
+
`--color-foreground`, `--u-color-muted` → `--color-muted-foreground`,
|
|
211
|
+
`--u-color-surface-muted` → `--color-muted`, `--u-color-danger` →
|
|
212
|
+
`--color-destructive`) and dashboard knobs to their `--udb-*` names
|
|
213
|
+
(`--u-font-size-*` → `--udb-font-size-*`, `--u-color-border-strong` →
|
|
214
|
+
`--udb-border-strong`, `--u-sidebar-*` → `--udb-sidebar-*`, `--u-space-*` →
|
|
215
|
+
`--udb-space-*`).
|
|
216
|
+
4. Dark mode: replace `data-udb-theme="dark"` with the design-system
|
|
217
|
+
convention (`[data-theme="dark"]` / `.dark` + dark DS variable values).
|
|
243
218
|
|
|
244
219
|
## License
|
|
245
220
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/web-dashboard",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Installable app skeleton for the fleet — layouts, auth, analytics (dependency-free SVG charts), calendar, i18n
|
|
3
|
+
"version": "5.0.1",
|
|
4
|
+
"description": "Installable app skeleton for the fleet — layouts, auth, analytics (dependency-free SVG charts), calendar, i18n via @umituz/web-localization, permissions/session/module registry; udb-* stylesheet on design-system tokens",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
7
7
|
"types": "./src/index.ts",
|
|
@@ -11,9 +11,6 @@
|
|
|
11
11
|
"exports": {
|
|
12
12
|
".": "./src/index.ts",
|
|
13
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
14
|
"./permissions": "./src/domains/permissions/index.ts",
|
|
18
15
|
"./session": "./src/domains/session/index.ts",
|
|
19
16
|
"./modules": "./src/domains/modules/index.ts",
|
|
@@ -67,8 +64,9 @@
|
|
|
67
64
|
"react-router-dom": "^6.0.0 || ^7.0.0"
|
|
68
65
|
},
|
|
69
66
|
"dependencies": {
|
|
70
|
-
"@umituz/web-design-system": "^4.
|
|
71
|
-
"lucide-react": "^0.577.0"
|
|
67
|
+
"@umituz/web-design-system": "^4.8.1",
|
|
68
|
+
"lucide-react": "^0.577.0",
|
|
69
|
+
"@umituz/web-localization": "^1.2.1"
|
|
72
70
|
},
|
|
73
71
|
"devDependencies": {
|
|
74
72
|
"@types/node": "^22.20.5",
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Analytics Card — chart/metric container on the design-system Card.
|
|
3
|
-
* Size modifiers ("sm"/"lg") are dashboard-owned padding overrides.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import { Loader2, AlertCircle } from "lucide-react";
|
|
7
2
|
import {
|
|
8
3
|
Card,
|
|
@@ -11,7 +6,7 @@ import {
|
|
|
11
6
|
CardDescription,
|
|
12
7
|
CardContent,
|
|
13
8
|
} from "@umituz/web-design-system/organisms";
|
|
14
|
-
import { cn } from "
|
|
9
|
+
import { cn } from "@umituz/web-design-system/utils/cn";
|
|
15
10
|
import type { AnalyticsCardProps } from "../types/analytics";
|
|
16
11
|
import { MetricCard } from "./MetricCard";
|
|
17
12
|
import { AnalyticsChart } from "./AnalyticsChart";
|
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AnalyticsChart
|
|
3
|
-
*
|
|
4
|
-
* Dependency-free SVG chart renderer (line / area / bar / pie / donut).
|
|
5
|
-
* Replaced the recharts wrapper in v4 — same ChartConfig contract,
|
|
6
|
-
* colors from `--u-*`-friendly CSS color strings, geometry centered
|
|
7
|
-
* here (no magic numbers at call sites). Tooltips are native SVG
|
|
8
|
-
* <title> elements; unsupported types render the empty state.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
1
|
import { useMemo } from "react";
|
|
12
|
-
import { cn } from "
|
|
13
|
-
import { useT } from "
|
|
2
|
+
import { cn } from "@umituz/web-design-system/utils/cn";
|
|
3
|
+
import { useT } from "@umituz/web-localization/react";
|
|
14
4
|
import type { ChartConfig, ChartData, TimeSeriesData } from "../types/analytics";
|
|
15
5
|
import { generateChartColors } from "../utils/analytics";
|
|
16
6
|
import { ANALYTICS_KEYS } from "../utils/i18nKeys";
|
|
@@ -46,7 +36,6 @@ const toNumber = (value: unknown): number => {
|
|
|
46
36
|
return Number.isFinite(num) ? num : 0;
|
|
47
37
|
};
|
|
48
38
|
|
|
49
|
-
/** Normalize config.data into labeled rows of series values. */
|
|
50
39
|
const toRows = (
|
|
51
40
|
data: TimeSeriesData[] | ChartData[],
|
|
52
41
|
keys: string[],
|
|
@@ -66,7 +55,6 @@ const rowsDomain = (rows: ChartRow[]): { min: number; max: number } => {
|
|
|
66
55
|
return { min, max };
|
|
67
56
|
};
|
|
68
57
|
|
|
69
|
-
/** Keep at most `X_LABEL_SAMPLE` x labels evenly spaced. */
|
|
70
58
|
const shouldShowLabel = (index: number, count: number): boolean => {
|
|
71
59
|
if (count <= X_LABEL_SAMPLE) return true;
|
|
72
60
|
const step = Math.ceil((count - 1) / (X_LABEL_SAMPLE - 1));
|
|
@@ -1,27 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AnalyticsLayout
|
|
3
|
-
*
|
|
4
|
-
* Top-level analytics page shell. All actions delegate to the
|
|
5
|
-
* consumer via callbacks — no console.log, no no-op handlers.
|
|
6
|
-
* The legacy "kpis" prop has been removed in favor of the typed
|
|
7
|
-
* `metrics` array (breaking change called out in changelog).
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
1
|
import { useMemo } from "react";
|
|
11
2
|
import { RefreshCw, Download, Calendar, Loader2 } from "lucide-react";
|
|
12
|
-
import { useT } from "
|
|
13
|
-
import { cn } from "
|
|
14
|
-
import { Button } from "
|
|
3
|
+
import { useT } from "@umituz/web-localization/react";
|
|
4
|
+
import { cn } from "@umituz/web-design-system/utils/cn";
|
|
5
|
+
import { Button } from "@umituz/web-design-system/atoms";
|
|
15
6
|
import type { AnalyticsLayoutProps, AnalyticsPeriod } from "../types/analytics";
|
|
16
7
|
import { AnalyticsCard } from "./AnalyticsCard";
|
|
17
8
|
import { MetricCard } from "./MetricCard";
|
|
18
9
|
import { ANALYTICS_KEYS } from "../utils/i18nKeys";
|
|
19
10
|
|
|
20
|
-
/**
|
|
21
|
-
* Map analytics period tokens to localized display labels.
|
|
22
|
-
* Periods not in this map fall back to the raw token — better
|
|
23
|
-
* to show the key than an empty select option.
|
|
24
|
-
*/
|
|
25
11
|
const PERIOD_LABEL: Record<AnalyticsPeriod, string> = {
|
|
26
12
|
'7d': ANALYTICS_KEYS.common.last7Days,
|
|
27
13
|
'30d': ANALYTICS_KEYS.common.last30Days,
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MetricCard — dashboard logic (growth, i18n, a11y) on top of the
|
|
3
|
-
* design-system MetricCard presentation.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import {
|
|
7
2
|
MetricCard as DSMetricCard,
|
|
8
3
|
} from "@umituz/web-design-system/organisms";
|
|
9
|
-
import { useT } from "
|
|
4
|
+
import { useT } from "@umituz/web-localization/react";
|
|
10
5
|
import type { MetricCardProps } from "../types/analytics";
|
|
11
6
|
import { formatMetricValue, calculateGrowth, getTrend } from "../utils/analytics";
|
|
12
7
|
import { ANALYTICS_KEYS } from "../utils/i18nKeys";
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Analytics Domain i18n Keys
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
export const ANALYTICS_KEYS = {
|
|
6
|
-
// Common
|
|
7
2
|
common: {
|
|
8
3
|
export: 'analytics.common.export',
|
|
9
4
|
refresh: 'analytics.common.refresh',
|
|
@@ -16,23 +11,19 @@ export const ANALYTICS_KEYS = {
|
|
|
16
11
|
loading: 'analytics.common.loading',
|
|
17
12
|
},
|
|
18
13
|
|
|
19
|
-
// Metric card
|
|
20
14
|
metric: {
|
|
21
15
|
noData: 'analytics.metric.noData',
|
|
22
16
|
change: 'analytics.metric.change',
|
|
23
17
|
},
|
|
24
18
|
|
|
25
|
-
// Analytics card
|
|
26
19
|
card: {
|
|
27
20
|
title: 'analytics.card.title',
|
|
28
21
|
},
|
|
29
22
|
|
|
30
|
-
// Chart
|
|
31
23
|
chart: {
|
|
32
24
|
noData: 'analytics.chart.noData',
|
|
33
25
|
},
|
|
34
26
|
|
|
35
|
-
// Layout
|
|
36
27
|
layout: {
|
|
37
28
|
title: 'analytics.layout.title',
|
|
38
29
|
export: 'analytics.layout.export',
|
|
@@ -1,22 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth Layout Component
|
|
3
|
-
*
|
|
4
|
-
* Configurable layout wrapper for authentication pages.
|
|
5
|
-
* Strings stay localizable via the optional `translate` prop; when it
|
|
6
|
-
* is not provided the embedded English fallbacks are rendered.
|
|
7
|
-
* Styling is entirely `udb-auth*` classes from the package stylesheet.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
1
|
import { BrandLogo } from "../../layouts/components";
|
|
11
|
-
import { cn } from "
|
|
2
|
+
import { cn } from "@umituz/web-design-system/utils/cn";
|
|
12
3
|
import { AUTH_KEYS } from "../utils/i18nKeys";
|
|
13
4
|
import type { AuthLayoutProps, Translate } from "../types/auth";
|
|
14
5
|
|
|
15
|
-
/**
|
|
16
|
-
* Embedded fallbacks used when no `translate` prop is supplied.
|
|
17
|
-
* Keys mirror the AUTH_KEYS catalog so consumers can override
|
|
18
|
-
* every string via their own i18n setup.
|
|
19
|
-
*/
|
|
20
6
|
const DEFAULT_STRINGS: Record<string, string> = {
|
|
21
7
|
[AUTH_KEYS.layout.backToHome]: "Back to home",
|
|
22
8
|
[AUTH_KEYS.layout.orContinueWith]: "Or continue with",
|
|
@@ -30,20 +16,17 @@ export const AuthLayout = ({
|
|
|
30
16
|
children,
|
|
31
17
|
}: AuthLayoutProps) => {
|
|
32
18
|
const t: Translate = translate ?? ((key) => DEFAULT_STRINGS[key] ?? key);
|
|
33
|
-
// Prefer an explicit home route; fall back to afterLoginRoute for backwards compatibility.
|
|
34
19
|
const homeRoute = config.homeRoute ?? config.afterLoginRoute;
|
|
35
20
|
const footerLinks = config.footerLinks ?? [];
|
|
36
21
|
|
|
37
22
|
return (
|
|
38
23
|
<div className="udb-root udb-auth">
|
|
39
|
-
{/* Header */}
|
|
40
24
|
<header className="udb-auth__header">
|
|
41
25
|
<div className="udb-auth__brand">
|
|
42
26
|
<BrandLogo size={32} />
|
|
43
27
|
<span className="udb-auth__brand-name">{config.brandName}</span>
|
|
44
28
|
</div>
|
|
45
29
|
|
|
46
|
-
{/* Additional Header Content */}
|
|
47
30
|
{homeRoute && (
|
|
48
31
|
<a href={homeRoute} className="udb-auth__home-link">
|
|
49
32
|
{t(AUTH_KEYS.layout.backToHome)}
|
|
@@ -51,33 +34,26 @@ export const AuthLayout = ({
|
|
|
51
34
|
)}
|
|
52
35
|
</header>
|
|
53
36
|
|
|
54
|
-
{/* Main Content */}
|
|
55
37
|
<main className="udb-auth__main">
|
|
56
38
|
<div className="udb-auth__column">
|
|
57
|
-
{/* Card Container */}
|
|
58
39
|
<div className="udb-auth__card">
|
|
59
|
-
{/* Tagline */}
|
|
60
40
|
{config.brandTagline && (
|
|
61
41
|
<p className="udb-auth__tagline">
|
|
62
42
|
{config.brandTagline}
|
|
63
43
|
</p>
|
|
64
44
|
)}
|
|
65
45
|
|
|
66
|
-
{/* Children Content (Forms) */}
|
|
67
46
|
{children}
|
|
68
47
|
</div>
|
|
69
48
|
|
|
70
|
-
{/* Social Login */}
|
|
71
49
|
{config.showSocialLogin && config.socialProviders && config.socialProviders.length > 0 && (
|
|
72
50
|
<div className="udb-auth__social">
|
|
73
|
-
{/* Divider */}
|
|
74
51
|
<div className="udb-divider-row">
|
|
75
52
|
<span className="udb-divider-row__label">
|
|
76
53
|
{t(AUTH_KEYS.layout.orContinueWith)}
|
|
77
54
|
</span>
|
|
78
55
|
</div>
|
|
79
56
|
|
|
80
|
-
{/* Social Buttons */}
|
|
81
57
|
<div className="udb-social-grid">
|
|
82
58
|
{config.socialProviders.map((provider) => (
|
|
83
59
|
<button
|
|
@@ -96,7 +72,6 @@ export const AuthLayout = ({
|
|
|
96
72
|
</div>
|
|
97
73
|
</main>
|
|
98
74
|
|
|
99
|
-
{/* Footer */}
|
|
100
75
|
<footer className="udb-auth__footer">
|
|
101
76
|
<div className="udb-auth__footer-inner">
|
|
102
77
|
<p>
|
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ForgotPasswordForm
|
|
3
|
-
*
|
|
4
|
-
* Composed of shared auth primitives (EmailInput, AuthErrorBanner).
|
|
5
|
-
* All copy is i18n-keyed; the success state uses lucide icons
|
|
6
|
-
* instead of the previous `✓` Unicode glyphs.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
1
|
import { useState } from "react";
|
|
10
2
|
import { useNavigate } from "react-router-dom";
|
|
11
|
-
import { useT } from "
|
|
3
|
+
import { useT } from "@umituz/web-localization/react";
|
|
12
4
|
import { ArrowLeft, Loader2, CheckCircle2, Check } from "lucide-react";
|
|
13
|
-
import { Button } from "
|
|
5
|
+
import { Button } from "@umituz/web-design-system/atoms";
|
|
14
6
|
import type { ForgotPasswordFormProps, Translate } from "../types/auth";
|
|
15
7
|
import { validateForgotPassword } from "../utils/auth";
|
|
16
8
|
import { AUTH_KEYS } from "../utils/i18nKeys";
|
|
@@ -64,10 +56,6 @@ export const ForgotPasswordForm = ({
|
|
|
64
56
|
|
|
65
57
|
setIsLoading(true);
|
|
66
58
|
try {
|
|
67
|
-
// Hand off to the consumer's auth provider. The form has
|
|
68
|
-
// no network knowledge of its own — it's pure orchestration.
|
|
69
|
-
// The submitted email is passed through so the consumer can
|
|
70
|
-
// actually trigger the reset email.
|
|
71
59
|
await onSuccess?.({ email });
|
|
72
60
|
setSuccess(true);
|
|
73
61
|
} catch (err) {
|
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LoginForm
|
|
3
|
-
*
|
|
4
|
-
* Composed from shared auth primitives (EmailInput, PasswordInput,
|
|
5
|
-
* AuthErrorBanner, SocialLoginButtons). The form's job is to
|
|
6
|
-
* orchestrate them — UI rendering and validation live elsewhere.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
1
|
import { useState, useCallback } from "react";
|
|
10
2
|
import { useNavigate } from "react-router-dom";
|
|
11
|
-
import { useT } from "
|
|
12
|
-
import { Button, Checkbox } from "
|
|
3
|
+
import { useT } from "@umituz/web-localization/react";
|
|
4
|
+
import { Button, Checkbox } from "@umituz/web-design-system/atoms";
|
|
13
5
|
import { Loader2 } from "lucide-react";
|
|
14
6
|
import { validateLogin } from "../utils/auth";
|
|
15
7
|
import type {
|
|
@@ -68,11 +60,6 @@ export const LoginForm = ({
|
|
|
68
60
|
const [loading, setLoading] = useState<boolean>(false);
|
|
69
61
|
const [error, setError] = useState<string | null>(null);
|
|
70
62
|
|
|
71
|
-
/**
|
|
72
|
-
* Auth execution. A real provider is supplied via onLoginAttempt.
|
|
73
|
-
* The mock path accepts ANY credentials and is therefore opt-in
|
|
74
|
-
* (`enableMockAuth`) — never a silent default in production builds.
|
|
75
|
-
*/
|
|
76
63
|
const performLogin = useCallback(
|
|
77
64
|
async (credentials: LoginCredentials): Promise<User> => {
|
|
78
65
|
if (onLoginAttempt) {
|
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RegisterForm
|
|
3
|
-
*
|
|
4
|
-
* Composed from shared auth primitives (EmailInput, PasswordInput,
|
|
5
|
-
* PasswordStrengthIndicator, AuthErrorBanner, SocialLoginButtons).
|
|
6
|
-
* Hardcoded mock user data + 1s setTimeout are gone — the form is
|
|
7
|
-
* pure orchestration over the consumer's `onRegisterAttempt` provider.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
1
|
import { useState, useCallback } from "react";
|
|
11
2
|
import { useNavigate } from "react-router-dom";
|
|
12
|
-
import { useT } from "
|
|
3
|
+
import { useT } from "@umituz/web-localization/react";
|
|
13
4
|
import { Loader2 } from "lucide-react";
|
|
14
|
-
import { Button, Checkbox, Input } from "
|
|
15
|
-
import { cn } from "
|
|
5
|
+
import { Button, Checkbox, Input } from "@umituz/web-design-system/atoms";
|
|
6
|
+
import { cn } from "@umituz/web-design-system/utils/cn";
|
|
16
7
|
import type { RegisterFormProps, RegisterData, Translate } from "../types/auth";
|
|
17
8
|
import { validateRegister } from "../utils/auth";
|
|
18
9
|
import { AUTH_KEYS } from "../utils/i18nKeys";
|
|
@@ -101,8 +92,6 @@ export const RegisterForm = ({
|
|
|
101
92
|
setIsLoading(true);
|
|
102
93
|
try {
|
|
103
94
|
if (!onRegisterAttempt) {
|
|
104
|
-
// The form has no auth provider — fail loud rather than
|
|
105
|
-
// silently inserting a mock user.
|
|
106
95
|
throw new Error('No register handler configured');
|
|
107
96
|
}
|
|
108
97
|
const registerData: RegisterData = { email, password, name };
|
|
@@ -1,16 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ResetPasswordForm
|
|
3
|
-
*
|
|
4
|
-
* Composed from shared auth primitives (PasswordInput, PasswordStrengthIndicator,
|
|
5
|
-
* AuthErrorBanner). All copy is i18n-keyed.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
import { useState } from "react";
|
|
9
2
|
import { useNavigate } from "react-router-dom";
|
|
10
|
-
import { useT } from "
|
|
3
|
+
import { useT } from "@umituz/web-localization/react";
|
|
11
4
|
import { Loader2, CheckCircle2 } from "lucide-react";
|
|
12
|
-
import { Button, Input } from "
|
|
13
|
-
import { cn } from "
|
|
5
|
+
import { Button, Input } from "@umituz/web-design-system/atoms";
|
|
6
|
+
import { cn } from "@umituz/web-design-system/utils/cn";
|
|
14
7
|
import type { ResetPasswordFormProps, Translate } from "../types/auth";
|
|
15
8
|
import { validateResetPassword } from "../utils/auth";
|
|
16
9
|
import { AUTH_KEYS } from "../utils/i18nKeys";
|
|
@@ -68,11 +61,7 @@ export const ResetPasswordForm = ({
|
|
|
68
61
|
|
|
69
62
|
setIsLoading(true);
|
|
70
63
|
try {
|
|
71
|
-
// Hand off the actual reset to the consumer. The form is
|
|
72
|
-
// pure orchestration; no network knowledge here. Token and the
|
|
73
|
-
// new password are passed through so the consumer can persist it.
|
|
74
64
|
await onSuccess?.({ token, password });
|
|
75
|
-
// Don't keep the new password in component state after completion.
|
|
76
65
|
setPassword("");
|
|
77
66
|
setConfirmPassword("");
|
|
78
67
|
setSuccess(true);
|
|
@@ -1,19 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AuthErrorBanner
|
|
3
|
-
*
|
|
4
|
-
* Inline error display for auth forms. Renders an alert with the
|
|
5
|
-
* provided i18n key resolved by the consumer's translation function.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
import { AlertCircle } from "lucide-react";
|
|
9
|
-
import { cn } from "
|
|
2
|
+
import { cn } from "@umituz/web-design-system/utils/cn";
|
|
10
3
|
|
|
11
4
|
export interface AuthErrorBannerProps {
|
|
12
|
-
/** i18n key for the message (already resolved by the consumer) */
|
|
13
5
|
message?: string | null;
|
|
14
|
-
/** Translation function used to resolve the key */
|
|
15
6
|
translate: (key: string) => string;
|
|
16
|
-
/** Optional className for layout integration */
|
|
17
7
|
className?: string;
|
|
18
8
|
}
|
|
19
9
|
|