@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,20 +1,12 @@
1
- /**
2
- * Permissions context — provides the matrix + active role and a
3
- * `can(permission)` helper bound to them. Guard components
4
- * (<RequirePermission>, <Can>) consume this context.
5
- */
6
-
7
1
  import { createContext, useCallback, useContext, useMemo, type ReactNode } from "react";
8
2
  import { Lock } from "lucide-react";
9
- import { useT } from "../../i18n";
3
+ import { useT } from "@umituz/web-localization/react";
10
4
  import { hasPermission } from "./hasPermission";
11
5
  import { PERMISSIONS_KEYS } from "./utils/i18nKeys";
12
6
  import type { Permission, PermissionMatrix, RoleKey } from "./types";
13
7
 
14
8
  export interface PermissionsProviderProps {
15
- /** Role x permission matrix. */
16
9
  matrix: PermissionMatrix;
17
- /** Active user's role. */
18
10
  role: RoleKey | null | undefined;
19
11
  children: ReactNode;
20
12
  }
@@ -57,7 +49,6 @@ export const usePermissions = (): PermissionsContextValue => {
57
49
  return context;
58
50
  };
59
51
 
60
- /** Default lock screen rendered by RequirePermission when denied. */
61
52
  const ForbiddenFallback = () => {
62
53
  const t = useT();
63
54
  return (
@@ -72,17 +63,11 @@ const ForbiddenFallback = () => {
72
63
  };
73
64
 
74
65
  export interface RequirePermissionProps {
75
- /** Permission required to render the children. */
76
66
  permission: Permission;
77
67
  children: ReactNode;
78
- /** Rendered instead of the default lock screen when denied. */
79
68
  fallback?: ReactNode;
80
69
  }
81
70
 
82
- /**
83
- * Page-level permission gate — renders the fallback (lock screen by
84
- * default) instead of the children when `can(permission)` is false.
85
- */
86
71
  export const RequirePermission = ({
87
72
  permission,
88
73
  children,
@@ -95,7 +80,6 @@ export const RequirePermission = ({
95
80
 
96
81
  export interface CanProps extends RequirePermissionProps {}
97
82
 
98
- /** Declarative twin of RequirePermission for inline use. */
99
83
  export const Can = ({ permission, children, fallback }: CanProps) => (
100
84
  <RequirePermission permission={permission} fallback={fallback}>
101
85
  {children}
@@ -1,8 +1,3 @@
1
- /**
2
- * Permission domain i18n keys — the documented string contract.
3
- * Hosts translate these via <DashboardI18nProvider t={...}>.
4
- */
5
-
6
1
  export const PERMISSIONS_KEYS = {
7
2
  forbidden: {
8
3
  title: "permissions.forbidden.title",
@@ -1,18 +1,10 @@
1
- /**
2
- * DemoLogin — one-click demo entry (fleet pattern: log in as a demo
3
- * persona without a password). Controlled: the click delegates to
4
- * `onDemo` (typically `session.demoLogin`); the label defaults to
5
- * the i18n catalog.
6
- */
7
-
8
1
  import { UserRound } from "lucide-react";
9
- import { Button } from "../../primitives";
10
- import { useT } from "../../i18n";
2
+ import { Button } from "@umituz/web-design-system/atoms";
3
+ import { useT } from "@umituz/web-localization/react";
11
4
  import { SESSION_KEYS } from "./utils/i18nKeys";
12
5
 
13
6
  export interface DemoLoginProps {
14
7
  onDemo: (userId: string) => void | Promise<void>;
15
- /** Demo user id to log in as. */
16
8
  userId: string;
17
9
  label?: string;
18
10
  variant?: "default" | "outline" | "ghost";
@@ -1,15 +1,7 @@
1
- /**
2
- * OrgSwitcher — tenant switcher trigger + dropdown.
3
- *
4
- * Controlled component: tenants and the active id come in as props,
5
- * switches are delegated via onSwitch. Styled entirely with existing
6
- * udb-* classes (profile button, avatar, menu).
7
- */
8
-
9
1
  import { useState } from "react";
10
2
  import { Building2, Check, ChevronDown } from "lucide-react";
11
- import { cn } from "../../primitives";
12
- import { useT } from "../../i18n";
3
+ import { cn } from "@umituz/web-design-system/utils/cn";
4
+ import { useT } from "@umituz/web-localization/react";
13
5
  import { SESSION_KEYS } from "./utils/i18nKeys";
14
6
  import type { Tenant } from "./types";
15
7
 
@@ -1,21 +1,12 @@
1
- /**
2
- * AuditLogList — admin skeleton for the audit trail screen.
3
- * Entries arrive as props; the list is display-only.
4
- */
5
-
6
1
  import { ScrollText } from "lucide-react";
7
- import { useT } from "../../../i18n";
2
+ import { useT } from "@umituz/web-localization/react";
8
3
  import { SETTINGS_ADMIN_KEYS } from "../utils/i18nKeys";
9
4
 
10
5
  export interface AuditEntry {
11
6
  id: string;
12
- /** Who performed the action. */
13
7
  actor: string;
14
- /** What happened, e.g. "role.changed". */
15
8
  action: string;
16
- /** Optional target of the action (user, invoice...). */
17
9
  target?: string;
18
- /** ISO timestamp. */
19
10
  at: string;
20
11
  }
21
12
 
@@ -1,11 +1,5 @@
1
- /**
2
- * PermissionMatrixScreen — admin skeleton rendering the role x
3
- * permission matrix. Read-only by default; pass `onToggle` to make
4
- * cells interactive (delegated — persistence is the host's job).
5
- */
6
-
7
1
  import { Check, Minus } from "lucide-react";
8
- import { cn } from "../../../primitives";
2
+ import { cn } from "@umituz/web-design-system/utils/cn";
9
3
  import { hasPermission } from "../../permissions/hasPermission";
10
4
  import type {
11
5
  Permission,
@@ -16,7 +10,6 @@ import type {
16
10
  export interface PermissionMatrixScreenProps {
17
11
  matrix: PermissionMatrix;
18
12
  roles: RoleInfo[];
19
- /** Permission column keys, in display order. */
20
13
  permissions: Permission[];
21
14
  onToggle?: (role: string, permission: Permission, granted: boolean) => void;
22
15
  }
@@ -1,28 +1,15 @@
1
1
  import { useEffect, useState } from "react";
2
2
  import { Outlet, useLocation, useNavigate } from "react-router-dom";
3
- import { Skeleton, Button } from "../../../primitives";
3
+ import { Skeleton, Button } from "@umituz/web-design-system/atoms";
4
4
  import { ChevronLeft, Menu } from "lucide-react";
5
5
  import { SettingsSection } from "./SettingsSection";
6
6
  import type { SettingsConfig } from "../types/settings";
7
7
 
8
8
  interface SettingsLayoutProps {
9
- /** Settings configuration */
10
9
  config: SettingsConfig;
11
- /**
12
- * Artificial route-transition skeleton duration in ms (default: 0 = off).
13
- * Content renders immediately by default; set > 0 only if you want
14
- * a skeleton to mask route swaps.
15
- */
16
10
  routeTransitionMs?: number;
17
11
  }
18
12
 
19
- /**
20
- * Settings Layout Component
21
- *
22
- * Main layout wrapper for settings pages.
23
- * Provides sidebar navigation and content area. Styling is entirely
24
- * `udb-settings*` classes from the package stylesheet.
25
- */
26
13
  export const SettingsLayout = ({
27
14
  config,
28
15
  routeTransitionMs = 0,
@@ -33,9 +20,6 @@ export const SettingsLayout = ({
33
20
  const [mobileOpen, setMobileOpen] = useState(false);
34
21
  const [loading, setLoading] = useState(routeTransitionMs > 0);
35
22
 
36
- // Optional route-change skeleton. Cleanup is essential: setTimeout must be
37
- // cancelled on unmount/route change. With routeTransitionMs = 0 (default)
38
- // no artificial delay is applied at all.
39
23
  useEffect(() => {
40
24
  if (routeTransitionMs <= 0) {
41
25
  setLoading(false);
@@ -78,7 +62,6 @@ export const SettingsLayout = ({
78
62
 
79
63
  return (
80
64
  <div className="udb-root udb-settings">
81
- {/* Desktop Sidebar */}
82
65
  <aside className={`udb-settings__sidebar${collapsed ? " udb-settings__sidebar--collapsed" : ""}`}>
83
66
  {renderSidebarHeader(() => setCollapsed((prev) => !prev), false)}
84
67
  <nav className="udb-settings__nav">
@@ -94,7 +77,6 @@ export const SettingsLayout = ({
94
77
  </nav>
95
78
  </aside>
96
79
 
97
- {/* Mobile Menu Overlay */}
98
80
  {mobileOpen && (
99
81
  <div className="udb-settings__mobile">
100
82
  <div
@@ -118,9 +100,7 @@ export const SettingsLayout = ({
118
100
  </div>
119
101
  )}
120
102
 
121
- {/* Main Content Area */}
122
103
  <div className="udb-settings__main">
123
- {/* Mobile Header */}
124
104
  <header className="udb-settings__mobile-header">
125
105
  <div className="udb-settings__mobile-lead">
126
106
  <Button
@@ -1,27 +1,16 @@
1
1
  import { useMemo } from "react";
2
2
  import { Link } from "react-router-dom";
3
3
  import { ChevronRight } from "lucide-react";
4
- import { cn } from "../../../primitives";
4
+ import { cn } from "@umituz/web-design-system/utils/cn";
5
5
  import type { SettingsSection as SettingsSectionType } from "../types/settings";
6
6
 
7
7
  interface SettingsSectionProps {
8
- /** Section configuration */
9
8
  section: SettingsSectionType;
10
- /** Current path */
11
9
  currentPath?: string;
12
- /** On navigate callback */
13
10
  onNavigate?: (path: string) => void;
14
- /** Collapsed state */
15
11
  collapsed?: boolean;
16
12
  }
17
13
 
18
- /**
19
- * Settings Section Component
20
- *
21
- * Displays a section of settings items.
22
- * Items without a path and without an onClick handler are hidden
23
- * (an empty interactive element violates a11y and is dead code).
24
- */
25
14
  export const SettingsSection = ({
26
15
  section,
27
16
  currentPath,
@@ -48,7 +37,6 @@ export const SettingsSection = ({
48
37
  const isActive = currentPath === item.path;
49
38
  const hasNavigation = Boolean(item.path) || Boolean(item.onClick);
50
39
 
51
- // Skip rendering items that have neither a path nor a click handler.
52
40
  if (!hasNavigation) {
53
41
  return null;
54
42
  }
@@ -1,11 +1,5 @@
1
- /**
2
- * UserManagementTable — admin skeleton for the users screen.
3
- * Config-driven: rows and role catalog come in as props, role
4
- * changes delegate out.
5
- */
6
-
7
1
  import { DataTable, type Column } from "@umituz/web-design-system/organisms";
8
- import { useT } from "../../../i18n";
2
+ import { useT } from "@umituz/web-localization/react";
9
3
  import { SETTINGS_ADMIN_KEYS } from "../utils/i18nKeys";
10
4
  import type { RoleKey, RoleInfo } from "../../permissions/types";
11
5
 
@@ -20,7 +14,6 @@ export interface ManagedUser {
20
14
  export interface UserManagementTableProps {
21
15
  users: ManagedUser[];
22
16
  roles: RoleInfo[];
23
- /** Provide to render an editable role select per row. */
24
17
  onRoleChange?: (userId: string, role: RoleKey) => void;
25
18
  }
26
19
 
@@ -1,9 +1,3 @@
1
- /**
2
- * Settings domain i18n keys (admin skeleton screens) — the
3
- * documented string contract. Column titles and role labels come
4
- * from props; only empty/denied states fall back to these keys.
5
- */
6
-
7
1
  export const SETTINGS_ADMIN_KEYS = {
8
2
  users: {
9
3
  empty: "settings.users.empty",
package/src/index.ts CHANGED
@@ -1,13 +1,3 @@
1
- /**
2
- * @umituz/web-dashboard
3
- *
4
- * Dashboard Layout System - Customizable, themeable dashboard layouts
5
- * with comprehensive analytics services and config-based architecture.
6
- *
7
- * @version 3.0.0
8
- */
9
-
10
- // Core domains with complete public API
11
1
  export * from './domains/layouts';
12
2
  export * from './domains/settings';
13
3
  export * from './domains/calendar';
@@ -21,7 +11,6 @@ export {
21
11
  type User,
22
12
  } from './domains/auth';
23
13
 
24
- // Analytics with conflict resolution
25
14
  export {
26
15
  useAnalytics,
27
16
  MetricCard,
@@ -46,7 +35,6 @@ export {
46
35
  roundTo,
47
36
  generateColor,
48
37
  generateChartColors,
49
- // Types
50
38
  type Metric,
51
39
  type TimeSeriesData,
52
40
  type ChartData,
@@ -66,13 +54,10 @@ export {
66
54
  type AnalyticsExportOptions,
67
55
  } from './domains/analytics';
68
56
 
69
- // Calendar
70
57
  export {
71
58
  useCalendar,
72
59
  calendarService,
73
- // Services
74
60
  CalendarService,
75
- // Utils
76
61
  getWeekDates,
77
62
  getMonthDays,
78
63
  isDateInRange,
@@ -80,8 +65,8 @@ export {
80
65
  isSameDay,
81
66
  getViewRange,
82
67
  generateTimeSlots,
83
- // Types
84
68
  type ContentItem,
69
+ DEFAULT_CALENDAR_CONFIG,
85
70
  type CalendarEvent,
86
71
  type CalendarFilter,
87
72
  type CalendarConfig,
@@ -93,10 +78,8 @@ export {
93
78
  type ICalendarService,
94
79
  } from './domains/calendar';
95
80
 
96
- // Config with renamed exports to avoid conflicts
97
81
  export {
98
82
  DEFAULT_DASHBOARD_CONFIG,
99
- DEFAULT_CALENDAR_CONFIG,
100
83
  type AnalyticsServiceType,
101
84
  type ExportFormat as DashboardExportFormat,
102
85
  type AnalyticsConfig as DashboardAnalyticsConfig,
@@ -104,4 +87,4 @@ export {
104
87
  type ChartsConfig,
105
88
  type PerformanceConfig as DashboardPerformanceConfig,
106
89
  type DashboardConfig as DashboardConfigType,
107
- } from './domain/config';
90
+ } from './config';
@@ -0,0 +1,42 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { describe, expect, it } from "vitest";
3
+
4
+ const css = readFileSync(new URL("./dashboard.css", import.meta.url), "utf8");
5
+
6
+ const defined = new Set([...css.matchAll(/(--udb-[a-z0-9-]+)\s*:/g)].map((m) => m[1]));
7
+ const used = new Set([...css.matchAll(/var\((--udb-[a-z0-9-]+)\)/g)].map((m) => m[1]));
8
+
9
+ describe("dashboard stylesheet contract", () => {
10
+ it("defines every --udb variable it uses", () => {
11
+ expect([...used].filter((v) => !defined.has(v))).toEqual([]);
12
+ });
13
+
14
+ it("imports the design-system stylesheet", () => {
15
+ expect(css).toContain('@import "@umituz/web-design-system/styles.css";');
16
+ });
17
+
18
+ it("carries no legacy --u-* tokens", () => {
19
+ expect(css.match(/--u-[a-z0-9-]+/g) ?? []).toEqual([]);
20
+ });
21
+
22
+ it("reads only design-system variables that exist or are documented optional slots", () => {
23
+ const optional = new Set([
24
+ "--color-primary-hover",
25
+ "--color-primary-soft",
26
+ "--color-destructive-hover",
27
+ "--color-destructive-soft",
28
+ "--color-success-soft",
29
+ "--color-warning-soft",
30
+ ]);
31
+ const dsCss = readFileSync(
32
+ new URL("../../node_modules/@umituz/web-design-system/dist/styles.css", import.meta.url),
33
+ "utf8",
34
+ );
35
+ const dsDefined = new Set([...dsCss.matchAll(/(--[a-z][a-z0-9-]*)\s*:/g)].map((m) => m[1]));
36
+ const read = new Set([...css.matchAll(/var\((--[a-z][a-z0-9-]*)[,)]/g)].map((m) => m[1]));
37
+ const missing = [...read].filter(
38
+ (v) => !v.startsWith("--udb-") && !dsDefined.has(v) && !optional.has(v),
39
+ );
40
+ expect(missing).toEqual([]);
41
+ });
42
+ });