@umituz/web-dashboard 4.7.0 → 5.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 (56) hide show
  1. package/README.md +62 -88
  2. package/package.json +6 -15
  3. package/src/{domain/config → config}/DashboardConfig.ts +1 -1
  4. package/src/config/index.ts +1 -0
  5. package/src/domains/analytics/components/AnalyticsCard.tsx +1 -6
  6. package/src/domains/analytics/components/AnalyticsChart.tsx +2 -14
  7. package/src/domains/analytics/components/AnalyticsLayout.tsx +3 -17
  8. package/src/domains/analytics/components/MetricCard.tsx +1 -6
  9. package/src/domains/analytics/utils/i18nKeys.ts +0 -9
  10. package/src/domains/auth/components/AuthLayout.tsx +1 -26
  11. package/src/domains/auth/components/ForgotPasswordForm.tsx +2 -14
  12. package/src/domains/auth/components/LoginForm.tsx +2 -15
  13. package/src/domains/auth/components/RegisterForm.tsx +3 -14
  14. package/src/domains/auth/components/ResetPasswordForm.tsx +3 -14
  15. package/src/domains/auth/components/shared/AuthErrorBanner.tsx +1 -11
  16. package/src/domains/auth/components/shared/EmailInput.tsx +2 -20
  17. package/src/domains/auth/components/shared/PasswordInput.tsx +3 -22
  18. package/src/domains/auth/components/shared/PasswordStrengthIndicator.tsx +1 -13
  19. package/src/domains/auth/components/shared/SocialLoginButtons.tsx +1 -15
  20. package/src/domains/auth/utils/i18nKeys.ts +0 -18
  21. package/src/{domain/config/CalendarConfig.ts → domains/calendar/config.ts} +1 -1
  22. package/src/domains/calendar/hooks/useCalendar.ts +1 -1
  23. package/src/domains/calendar/index.ts +1 -0
  24. package/src/domains/calendar/utils/index.ts +0 -6
  25. package/src/domains/charts/components.tsx +2 -2
  26. package/src/domains/layouts/components/BrandLogo.tsx +7 -22
  27. package/src/domains/layouts/components/DashboardHeader.tsx +2 -23
  28. package/src/domains/layouts/components/DashboardLayout.tsx +2 -39
  29. package/src/domains/layouts/components/DashboardSidebar.tsx +3 -21
  30. package/src/domains/permissions/PermissionsContext.tsx +1 -17
  31. package/src/domains/permissions/utils/i18nKeys.ts +0 -5
  32. package/src/domains/session/DemoLogin.tsx +2 -10
  33. package/src/domains/session/OrgSwitcher.tsx +2 -10
  34. package/src/domains/settings/components/AuditLogList.tsx +1 -10
  35. package/src/domains/settings/components/PermissionMatrixScreen.tsx +1 -8
  36. package/src/domains/settings/components/SettingsLayout.tsx +1 -21
  37. package/src/domains/settings/components/SettingsSection.tsx +1 -13
  38. package/src/domains/settings/components/UserManagementTable.tsx +1 -8
  39. package/src/domains/settings/utils/i18nKeys.ts +0 -6
  40. package/src/index.ts +2 -19
  41. package/src/styles/dashboard.contract.test.ts +42 -0
  42. package/src/styles/dashboard.css +483 -920
  43. package/src/domain/config/index.ts +0 -6
  44. package/src/i18n/I18nContext.tsx +0 -33
  45. package/src/i18n/defaultT.test.ts +0 -43
  46. package/src/i18n/defaultT.ts +0 -34
  47. package/src/i18n/index.ts +0 -7
  48. package/src/i18n/types.ts +0 -13
  49. package/src/primitives/Button.tsx +0 -24
  50. package/src/primitives/Checkbox.tsx +0 -17
  51. package/src/primitives/Input.tsx +0 -23
  52. package/src/primitives/Skeleton.tsx +0 -6
  53. package/src/primitives/cn.ts +0 -10
  54. package/src/primitives/index.ts +0 -23
  55. package/src/styles/tokens.contract.test.ts +0 -91
  56. package/src/styles/tokens.css +0 -116
@@ -1,6 +0,0 @@
1
- /**
2
- * Domain Config Index
3
- */
4
-
5
- export * from './DashboardConfig';
6
- export * from './CalendarConfig';
@@ -1,33 +0,0 @@
1
- /**
2
- * Dashboard i18n injection.
3
- *
4
- * The package ships no i18n runtime. Hosts that have one wrap their
5
- * app once: `<DashboardI18nProvider t={i18n.t}>` — the signature
6
- * already matches i18next (and the fleet's custom `t()` helpers).
7
- * Without a provider, `useT()` falls back to `defaultT`, which
8
- * humanizes catalog keys into readable English.
9
- *
10
- * Components consume only `useT()`; the key catalogs (i18nKeys.ts)
11
- * remain the documented string contract.
12
- */
13
-
14
- import { createContext, useContext, type ReactNode } from "react";
15
- import { defaultT } from "./defaultT";
16
- import type { TranslateFn } from "./types";
17
-
18
- export interface DashboardI18nProviderProps {
19
- /** Host translation function (e.g. i18next's `t`). */
20
- t: TranslateFn;
21
- children: ReactNode;
22
- }
23
-
24
- const I18nContext = createContext<TranslateFn | null>(null);
25
-
26
- export const DashboardI18nProvider = ({
27
- t,
28
- children,
29
- }: DashboardI18nProviderProps) => (
30
- <I18nContext.Provider value={t}>{children}</I18nContext.Provider>
31
- );
32
-
33
- export const useT = (): TranslateFn => useContext(I18nContext) ?? defaultT;
@@ -1,43 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { defaultT } from "./defaultT";
3
-
4
- describe("defaultT", () => {
5
- it("humanizes the last key segment", () => {
6
- expect(defaultT("auth.login.welcomeBack")).toBe("Welcome back");
7
- });
8
-
9
- it("splits camelCase into words", () => {
10
- expect(defaultT("auth.passwordStrength.passwordsDoNotMatch")).toBe(
11
- "Passwords do not match",
12
- );
13
- });
14
-
15
- it("splits snake_case and kebab-case segments", () => {
16
- expect(defaultT("common.error_code")).toBe("Error code");
17
- expect(defaultT("common.error-code")).toBe("Error code");
18
- });
19
-
20
- it("keeps digits attached to their word", () => {
21
- expect(defaultT("analytics.common.last7Days")).toBe("Last7 days");
22
- });
23
-
24
- it("interpolates {{param}} placeholders the text carries", () => {
25
- expect(defaultT("custom.{{brand}} console", { brand: "Acme" })).toBe(
26
- "Acme console",
27
- );
28
- });
29
-
30
- it("ignores params the humanized text cannot place", () => {
31
- expect(defaultT("auth.login.signInTo", { brand: "Acme" })).toBe(
32
- "Sign in to",
33
- );
34
- });
35
-
36
- it("returns the raw key when it has no segments", () => {
37
- expect(defaultT("")).toBe("");
38
- });
39
-
40
- it("handles single-segment keys", () => {
41
- expect(defaultT("settings")).toBe("Settings");
42
- });
43
- });
@@ -1,34 +0,0 @@
1
- /**
2
- * Default translator — the zero-config fallback when no host i18n
3
- * is provided. Humanizes catalog keys into sentence case
4
- * ("auth.login.welcomeBack" → "Welcome back") and interpolates
5
- * `{{param}}` placeholders when the humanized text carries one.
6
- * Params the text cannot place are ignored — real interpolation is
7
- * the host runtime's job via the DashboardI18nProvider bridge.
8
- */
9
-
10
- import type { TranslateFn } from "./types";
11
-
12
- /** Split a key segment into words: camelCase, snake_case, kebab-case. */
13
- const WORD_SPLIT = /[._\-]+|(?<=[a-z0-9])(?=[A-Z])/g;
14
-
15
- const humanizeSegment = (segment: string): string => {
16
- const words = segment.split(WORD_SPLIT).filter(Boolean);
17
- if (words.length === 0) return segment;
18
- const sentence = words.join(" ").toLowerCase();
19
- return sentence.charAt(0).toUpperCase() + sentence.slice(1);
20
- };
21
-
22
- /** Replace `{{param}}` placeholders with param values. */
23
- const interpolate = (text: string, params?: Record<string, unknown>): string => {
24
- if (!params) return text;
25
- return text.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, name: string) =>
26
- name in params ? String(params[name]) : match,
27
- );
28
- };
29
-
30
- export const defaultT: TranslateFn = (key, params) => {
31
- const segments = key.split(".").filter(Boolean);
32
- const source = segments[segments.length - 1] ?? key;
33
- return interpolate(humanizeSegment(source), params);
34
- };
package/src/i18n/index.ts DELETED
@@ -1,7 +0,0 @@
1
- export {
2
- DashboardI18nProvider,
3
- useT,
4
- type DashboardI18nProviderProps,
5
- } from "./I18nContext";
6
- export { defaultT } from "./defaultT";
7
- export type { TranslateFn } from "./types";
package/src/i18n/types.ts DELETED
@@ -1,13 +0,0 @@
1
- /**
2
- * Translation function contract.
3
- *
4
- * Keyed by the package's i18n catalog constants (e.g. AUTH_KEYS);
5
- * `params` feeds `{{placeholder}}` interpolation. The signature is
6
- * deliberately compatible with i18next's `t` so hosts can bridge
7
- * their own setup: `<DashboardI18nProvider t={i18n.t}>`.
8
- */
9
-
10
- export type TranslateFn = (
11
- key: string,
12
- params?: Record<string, unknown>,
13
- ) => string;
@@ -1,24 +0,0 @@
1
- /**
2
- * Button — delegates to the design system.
3
- * Dashboard surface: variants default/destructive/outline/ghost,
4
- * sizes default/sm/icon. Call-site API unchanged.
5
- */
6
-
7
- import { forwardRef } from 'react';
8
- import { Button as DSButton } from '@umituz/web-design-system/atoms';
9
-
10
- export type ButtonVariant = 'default' | 'destructive' | 'outline' | 'ghost';
11
- export type ButtonSize = 'default' | 'sm' | 'icon';
12
-
13
- export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
14
- variant?: ButtonVariant;
15
- size?: ButtonSize;
16
- }
17
-
18
- export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
19
- ({ variant = 'default', size = 'default', ...props }, ref) => (
20
- <DSButton ref={ref} variant={variant} size={size} {...props} />
21
- ),
22
- );
23
-
24
- Button.displayName = 'Button';
@@ -1,17 +0,0 @@
1
- /**
2
- * Checkbox — delegates to the design system (native + onCheckedChange).
3
- */
4
-
5
- import { forwardRef } from 'react';
6
- import { Checkbox as DSCheckbox } from '@umituz/web-design-system/atoms';
7
-
8
- export interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
9
- checked?: boolean;
10
- onCheckedChange?: (checked: boolean) => void;
11
- }
12
-
13
- export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => (
14
- <DSCheckbox ref={ref} {...props} />
15
- ));
16
-
17
- Checkbox.displayName = 'Checkbox';
@@ -1,23 +0,0 @@
1
- /**
2
- * Input — delegates to the design system.
3
- * Native `size` (character width) has no DS counterpart; it maps to a
4
- * `ch`-based width so the original prop API keeps working.
5
- */
6
-
7
- import { forwardRef } from 'react';
8
- import { Input as DSInput } from '@umituz/web-design-system/atoms';
9
-
10
- export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
11
-
12
- export const Input = forwardRef<HTMLInputElement, InputProps>(
13
- ({ type = 'text', size: nativeSize, style, ...props }, ref) => (
14
- <DSInput
15
- ref={ref}
16
- type={type}
17
- {...props}
18
- style={nativeSize != null ? { width: `${nativeSize}ch`, ...style } : style}
19
- />
20
- ),
21
- );
22
-
23
- Input.displayName = 'Input';
@@ -1,6 +0,0 @@
1
- /**
2
- * Skeleton — re-exported from the design system (variant API identical).
3
- */
4
-
5
- export { Skeleton } from '@umituz/web-design-system/atoms';
6
- export type { SkeletonProps } from '@umituz/web-design-system/atoms';
@@ -1,10 +0,0 @@
1
- /**
2
- * cn — conditional className join, delegated to the design system.
3
- *
4
- * The design system's `cn` (clsx + tailwind-merge) understands both the
5
- * `udb-*` vocabulary (unknown classes pass through untouched) and real
6
- * Tailwind utilities a host may pass, resolving conflicts by last-wins.
7
- */
8
-
9
- export { cn } from '@umituz/web-design-system/utils/cn';
10
- export type { ClassValue } from '@umituz/web-design-system/utils/cn';
@@ -1,23 +0,0 @@
1
- /**
2
- * Local primitives — the package's only UI atoms.
3
- *
4
- * Replaces the `@umituz/web-design-system` dependency (v3) with a
5
- * camp-agnostic set: `udb-*` classes + `--u-*` tokens, no utility
6
- * framework, no cva, no radix. Import as `@umituz/web-dashboard/primitives`
7
- * or relatively from inside the package.
8
- */
9
-
10
- export { cn } from './cn';
11
- export type { ClassValue } from './cn';
12
-
13
- export { Button } from './Button';
14
- export type { ButtonProps, ButtonVariant, ButtonSize } from './Button';
15
-
16
- export { Input } from './Input';
17
- export type { InputProps } from './Input';
18
-
19
- export { Checkbox } from './Checkbox';
20
- export type { CheckboxProps } from './Checkbox';
21
-
22
- export { Skeleton } from './Skeleton';
23
- export type { SkeletonProps } from './Skeleton';
@@ -1,91 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { describe, expect, it } from "vitest";
4
-
5
- /**
6
- * Stylesheet-token contract:
7
- * - every `--u-*` custom property referenced by dashboard.css must be
8
- * defined in tokens.css (catches token typos and dead references),
9
- * - the core token families (color/radius/space/font/shadow/layout)
10
- * must all be present in tokens.css.
11
- */
12
-
13
- const read = (file: string): string =>
14
- readFileSync(join(__dirname, file), "utf8");
15
-
16
- const tokensCss = read("tokens.css");
17
- const dashboardCss = read("dashboard.css");
18
-
19
- const DEFINED = new Set([...tokensCss.matchAll(/(--u-[a-z0-9-]+)\s*:/g)].map((m) => m[1]));
20
- const USED = new Set([...dashboardCss.matchAll(/var\((--u-[a-z0-9-]+)/g)].map((m) => m[1]));
21
-
22
- describe("dashboard stylesheet token contract", () => {
23
- it("defines every token the stylesheet references", () => {
24
- const undefinedTokens = [...USED].filter((token) => !DEFINED.has(token));
25
- expect(undefinedTokens).toEqual([]);
26
- });
27
-
28
- it("references the token system at all", () => {
29
- expect(USED.size).toBeGreaterThan(40);
30
- });
31
-
32
- it("ships the core token families", () => {
33
- const CORE = [
34
- "--u-color-bg",
35
- "--u-color-surface",
36
- "--u-color-text",
37
- "--u-color-muted",
38
- "--u-color-border",
39
- "--u-color-border-strong",
40
- "--u-color-primary",
41
- "--u-color-primary-hover",
42
- "--u-color-primary-contrast",
43
- "--u-color-primary-soft",
44
- "--u-color-danger",
45
- "--u-color-success",
46
- "--u-color-warning",
47
- "--u-color-ring",
48
- "--u-radius-sm",
49
- "--u-radius-lg",
50
- "--u-radius-pill",
51
- "--u-space-1",
52
- "--u-space-8",
53
- "--u-font-sans",
54
- "--u-font-size-md",
55
- "--u-font-weight-bold",
56
- "--u-shadow-sm",
57
- "--u-sidebar-width",
58
- "--u-header-height",
59
- ];
60
- expect(CORE.filter((token) => !DEFINED.has(token))).toEqual([]);
61
- });
62
-
63
- it("provides dark-mode overrides", () => {
64
- expect(tokensCss).toContain('[data-udb-theme="dark"]');
65
- });
66
-
67
- it("bridges core tokens to design-system variables with fallbacks", () => {
68
- const BRIDGED = [
69
- ["--u-color-primary", "var(--color-primary, #2563eb)"],
70
- ["--u-color-bg", "var(--color-background, #f8fafc)"],
71
- ["--u-color-danger", "var(--color-destructive, #dc2626)"],
72
- ["--u-radius-md", "var(--radius-md, 8px)"],
73
- ["--u-shadow-lg", "var(--shadow-lg,"],
74
- ["--u-font-sans", "var(--font-sans,"],
75
- ] as const;
76
- for (const [token, bridge] of BRIDGED) {
77
- expect(tokensCss).toContain(`${token}: ${bridge}`);
78
- }
79
- });
80
-
81
- it("derives hover/soft shades from the bridged base color", () => {
82
- expect(tokensCss).toMatch(/--u-color-primary-hover: var\(--color-primary-hover, color-mix\(/);
83
- expect(tokensCss).toMatch(/--u-color-primary-soft: var\(--color-primary-soft, color-mix\(/);
84
- });
85
-
86
- it("pulls the design-system stylesheet transitively", () => {
87
- const imports = [...dashboardCss.matchAll(/@import\s+"([^"]+)"/g)].map((m) => m[1]);
88
- expect(imports[0]).toBe("@umituz/web-design-system/styles.css");
89
- expect(imports).toContain("./tokens.css");
90
- });
91
- });
@@ -1,116 +0,0 @@
1
- /**
2
- * @umituz/web-dashboard — design tokens (`--u-*`)
3
- *
4
- * Colors, radii, fonts and shadows are bridged to the design system's
5
- * `--color-*` / `--radius-*` / `--font-*` / `--shadow-*` variables; each
6
- * bridge keeps its previous value as fallback. Dark mode: add
7
- * `data-udb-theme="dark"` on any ancestor — the dark block re-sets the
8
- * design-system variables and every bridge follows automatically.
9
- */
10
-
11
- :root {
12
- /* Surfaces & text */
13
- --u-color-bg: var(--color-background, #f8fafc);
14
- --u-color-surface: var(--color-card, #ffffff);
15
- --u-color-surface-muted: var(--color-muted, #f1f5f9);
16
- --u-color-text: var(--color-foreground, #0f172a);
17
- --u-color-muted: var(--color-muted-foreground, #64748b);
18
- --u-color-border: var(--color-border, #e2e8f0);
19
- --u-color-border-strong: #cbd5e1;
20
-
21
- /* Brand (hover/soft derive from the base color) */
22
- --u-color-primary: var(--color-primary, #2563eb);
23
- --u-color-primary-hover: var(--color-primary-hover, color-mix(in oklab, var(--color-primary, #2563eb) 86%, #000));
24
- --u-color-primary-contrast: var(--color-primary-foreground, #ffffff);
25
- --u-color-primary-soft: var(--color-primary-soft, color-mix(in srgb, var(--color-primary, #2563eb) 10%, transparent));
26
- --u-color-secondary: var(--color-secondary, #8b5cf6);
27
-
28
- /* Semantic */
29
- --u-color-danger: var(--color-destructive, #dc2626);
30
- --u-color-danger-hover: var(--color-destructive-hover, color-mix(in oklab, var(--color-destructive, #dc2626) 85%, #000));
31
- --u-color-danger-contrast: var(--color-destructive-foreground, #ffffff);
32
- --u-color-danger-soft: var(--color-destructive-soft, color-mix(in srgb, var(--color-destructive, #dc2626) 10%, transparent));
33
- --u-color-success: var(--color-success, #16a34a);
34
- --u-color-success-soft: var(--color-success-soft, color-mix(in srgb, var(--color-success, #16a34a) 12%, transparent));
35
- --u-color-warning: var(--color-warning, #d97706);
36
- --u-color-warning-soft: var(--color-warning-soft, color-mix(in srgb, var(--color-warning, #d97706) 15%, transparent));
37
-
38
- /* Focus */
39
- --u-color-ring: var(--color-ring, rgba(37, 99, 235, 0.45));
40
-
41
- /* Radii */
42
- --u-radius-sm: var(--radius-sm, 6px);
43
- --u-radius-md: var(--radius-md, 8px);
44
- --u-radius-lg: var(--radius-lg, 12px);
45
- --u-radius-xl: var(--radius-xl, 16px);
46
- --u-radius-pill: 9999px;
47
-
48
- /* Spacing (4px base) */
49
- --u-space-1: 4px;
50
- --u-space-2: 8px;
51
- --u-space-3: 12px;
52
- --u-space-4: 16px;
53
- --u-space-5: 20px;
54
- --u-space-6: 24px;
55
- --u-space-7: 32px;
56
- --u-space-8: 40px;
57
-
58
- /* Typography */
59
- --u-font-sans: var(--font-sans, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
60
- "Helvetica Neue", Arial, "Noto Sans", sans-serif);
61
- --u-font-size-xs: 11px;
62
- --u-font-size-sm: 12px;
63
- --u-font-size-md: 14px;
64
- --u-font-size-lg: 16px;
65
- --u-font-size-xl: 20px;
66
- --u-font-size-2xl: 24px;
67
- --u-font-size-3xl: 30px;
68
- --u-font-size-4xl: 36px;
69
- --u-font-weight-normal: 400;
70
- --u-font-weight-medium: 500;
71
- --u-font-weight-bold: 700;
72
-
73
- /* Layout */
74
- --u-sidebar-width: 240px;
75
- --u-sidebar-width-collapsed: 64px;
76
- --u-header-height: 56px;
77
-
78
- /* Elevation */
79
- --u-shadow-sm: var(--shadow-sm, 0 1px 2px rgba(15, 23, 42, 0.06));
80
- --u-shadow-md: var(--shadow-md, 0 4px 12px rgba(15, 23, 42, 0.08));
81
- --u-shadow-lg: var(--shadow-lg, 0 12px 32px rgba(15, 23, 42, 0.14));
82
-
83
- /* Sidebar palette */
84
- --u-sidebar-bg: var(--u-color-surface);
85
- --u-sidebar-text: var(--u-color-text);
86
- --u-sidebar-muted: var(--u-color-muted);
87
- --u-sidebar-border: var(--u-color-border);
88
- --u-sidebar-accent: var(--u-color-surface-muted);
89
- --u-sidebar-accent-text: var(--u-color-text);
90
- }
91
-
92
- [data-udb-theme="dark"] {
93
- --color-background: #0b1120;
94
- --color-card: #111827;
95
- --color-muted: #1e293b;
96
- --color-foreground: #f1f5f9;
97
- --color-muted-foreground: #94a3b8;
98
- --color-border: #1f2a3c;
99
-
100
- --color-primary: #3b82f6;
101
- --color-primary-foreground: #ffffff;
102
- --color-secondary: #a78bfa;
103
-
104
- --color-destructive: #ef4444;
105
- --color-destructive-foreground: #ffffff;
106
- --color-success: #22c55e;
107
- --color-warning: #f59e0b;
108
-
109
- --color-ring: rgba(96, 165, 250, 0.5);
110
-
111
- --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
112
- --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.45);
113
- --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.55);
114
-
115
- --u-color-border-strong: #334155;
116
- }