@umituz/web-dashboard 6.1.0 → 6.2.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.
Files changed (135) hide show
  1. package/README.md +47 -73
  2. package/package.json +5 -9
  3. package/src/config/DashboardConfig.ts +19 -68
  4. package/src/domains/analytics/components/index.ts +0 -6
  5. package/src/domains/analytics/hooks/index.ts +0 -6
  6. package/src/domains/analytics/hooks/useAnalytics.ts +2 -20
  7. package/src/domains/analytics/index.ts +0 -10
  8. package/src/domains/analytics/services/AnalyticsEngineService.ts +7 -74
  9. package/src/domains/analytics/services/PerformanceService.ts +27 -138
  10. package/src/domains/analytics/services/index.ts +0 -4
  11. package/src/domains/analytics/types/analytics.ts +100 -270
  12. package/src/domains/analytics/types/index.ts +0 -6
  13. package/src/domains/analytics/utils/analytics.test.ts +2 -9
  14. package/src/domains/analytics/utils/analytics.ts +1 -22
  15. package/src/domains/analytics/utils/index.ts +0 -6
  16. package/src/domains/calendar/config.ts +0 -7
  17. package/src/domains/calendar/hooks/index.ts +0 -4
  18. package/src/domains/calendar/hooks/useCalendar.ts +7 -27
  19. package/src/domains/calendar/index.ts +0 -4
  20. package/src/domains/calendar/services/CalendarService.ts +0 -21
  21. package/src/domains/calendar/services/index.ts +0 -4
  22. package/src/domains/calendar/types/calendar.types.ts +9 -53
  23. package/src/domains/calendar/utils/contentItemMapper.test.ts +0 -4
  24. package/src/domains/calendar/utils/contentItemMapper.ts +0 -31
  25. package/src/domains/calendar/utils/index.ts +0 -26
  26. package/src/domains/layouts/components/index.ts +0 -4
  27. package/src/domains/layouts/hooks/dashboard.ts +0 -21
  28. package/src/domains/layouts/hooks/index.ts +0 -4
  29. package/src/domains/layouts/index.ts +0 -6
  30. package/src/domains/layouts/theme/default.ts +0 -12
  31. package/src/domains/layouts/theme/index.ts +0 -4
  32. package/src/domains/layouts/theme/presets.ts +0 -9
  33. package/src/domains/layouts/theme/utils.ts +0 -15
  34. package/src/domains/layouts/types/index.ts +0 -4
  35. package/src/domains/layouts/types/layout.ts +18 -56
  36. package/src/domains/layouts/types/notification.ts +4 -17
  37. package/src/domains/layouts/types/sidebar.ts +8 -28
  38. package/src/domains/layouts/types/theme.ts +23 -59
  39. package/src/domains/layouts/types/user.ts +9 -30
  40. package/src/domains/layouts/utils/dashboard.ts +4 -48
  41. package/src/domains/layouts/utils/index.ts +0 -4
  42. package/src/domains/modules/build.tsx +2 -16
  43. package/src/domains/modules/types.ts +8 -23
  44. package/src/domains/onboarding/calc/calc.test.ts +1 -9
  45. package/src/domains/onboarding/calc/card-stack-motion-curve.ts +0 -6
  46. package/src/domains/onboarding/calc/count-with-locale.ts +0 -8
  47. package/src/domains/onboarding/calc/duration-equal-split.ts +0 -12
  48. package/src/domains/onboarding/calc/elapsed-millis.ts +0 -6
  49. package/src/domains/onboarding/calc/progress-percentage.ts +1 -11
  50. package/src/domains/onboarding/calc/slider-value-bounds.ts +1 -11
  51. package/src/domains/onboarding/calc/step-index-clamp.ts +0 -9
  52. package/src/domains/onboarding/calc/swipe-gesture-curve.ts +0 -13
  53. package/src/domains/onboarding/components/{shared/AnimationWrapper.tsx → AnimationWrapper.tsx} +0 -4
  54. package/src/domains/onboarding/components/{shared/OnboardingErrorBoundary.tsx → OnboardingErrorBoundary.tsx} +7 -27
  55. package/src/domains/onboarding/components/OnboardingFlow.test.tsx +8 -30
  56. package/src/domains/onboarding/components/OnboardingFlow.tsx +13 -61
  57. package/src/domains/onboarding/components/OnboardingIntl.tsx +1 -11
  58. package/src/domains/onboarding/components/PrimaryButton.tsx +40 -0
  59. package/src/domains/onboarding/components/ProgressBar.tsx +32 -0
  60. package/src/domains/onboarding/components/QuestionWrapper.tsx +28 -0
  61. package/src/domains/onboarding/components/ScreenContainer.tsx +19 -0
  62. package/src/domains/onboarding/components/{shared/SwipeCard.test.tsx → SwipeCard.test.tsx} +1 -3
  63. package/src/domains/onboarding/components/{shared/SwipeCard.tsx → SwipeCard.tsx} +10 -28
  64. package/src/domains/onboarding/components/renderBundledIcon.tsx +7 -0
  65. package/src/domains/onboarding/components/screens/AccountGateScreen.tsx +7 -17
  66. package/src/domains/onboarding/components/screens/AppDemoScreen.tsx +4 -10
  67. package/src/domains/onboarding/components/screens/ComparisonScreen.tsx +12 -22
  68. package/src/domains/onboarding/components/screens/GoalQuestionScreen.tsx +9 -6
  69. package/src/domains/onboarding/components/screens/PainPointsScreen.tsx +10 -10
  70. package/src/domains/onboarding/components/screens/PaywallScreen.tsx +22 -38
  71. package/src/domains/onboarding/components/screens/PermissionPrimingScreen.tsx +14 -29
  72. package/src/domains/onboarding/components/screens/PersonalizedSolutionScreen.tsx +10 -23
  73. package/src/domains/onboarding/components/screens/PreferencesScreen.tsx +13 -27
  74. package/src/domains/onboarding/components/screens/ProcessingScreen.tsx +11 -27
  75. package/src/domains/onboarding/components/screens/SocialProofScreen.tsx +22 -51
  76. package/src/domains/onboarding/components/screens/TinderCardsScreen.tsx +13 -30
  77. package/src/domains/onboarding/components/screens/ValueDeliveryScreen.tsx +13 -29
  78. package/src/domains/onboarding/components/screens/WelcomeScreen.tsx +12 -32
  79. package/src/domains/onboarding/{components/hooks → hooks}/useOnboardingAnalytics.test.tsx +1 -1
  80. package/src/domains/onboarding/{components/hooks → hooks}/useOnboardingAnalytics.ts +3 -16
  81. package/src/domains/onboarding/{components/hooks → hooks}/useOnboardingProgress.test.tsx +2 -2
  82. package/src/domains/onboarding/{components/hooks → hooks}/useOnboardingProgress.ts +10 -21
  83. package/src/domains/onboarding/{components/hooks → hooks}/usePermissionPriming.ts +3 -10
  84. package/src/domains/onboarding/{components/hooks → hooks}/useReducedMotion.ts +0 -5
  85. package/src/domains/onboarding/index.ts +16 -45
  86. package/src/domains/onboarding/predicates/has-handler.ts +0 -4
  87. package/src/domains/onboarding/predicates/icon-name-resolves.ts +1 -17
  88. package/src/domains/onboarding/predicates/permission-granted.ts +0 -5
  89. package/src/domains/onboarding/predicates/permission-supports.ts +1 -11
  90. package/src/domains/onboarding/predicates/predicates.test.ts +6 -20
  91. package/src/domains/onboarding/predicates/screen-emits-data.ts +0 -4
  92. package/src/domains/onboarding/predicates/storage-available.ts +0 -8
  93. package/src/domains/onboarding/templates/TemplateDemoFallback.tsx +19 -39
  94. package/src/domains/onboarding/templates/art-onboarding.ts +1 -11
  95. package/src/domains/onboarding/templates/health-onboarding.ts +0 -5
  96. package/src/domains/onboarding/templates/platform-onboarding.ts +0 -9
  97. package/src/domains/onboarding/templates/video-onboarding.ts +0 -5
  98. package/src/domains/onboarding/test/setup.ts +1 -8
  99. package/src/domains/onboarding/types/config.ts +7 -18
  100. package/src/domains/onboarding/types/labels.ts +25 -59
  101. package/src/domains/onboarding/types/screens.ts +3 -11
  102. package/src/domains/onboarding/utils/analytics-dispatch.ts +2 -21
  103. package/src/domains/onboarding/utils/default-storage-key.ts +0 -1
  104. package/src/domains/onboarding/utils/index.ts +0 -1
  105. package/src/domains/onboarding/utils/local-storage-bridge.ts +1 -21
  106. package/src/domains/onboarding/utils/permission-bridge.ts +0 -8
  107. package/src/domains/onboarding/validation/flow-structure-check.ts +0 -6
  108. package/src/domains/onboarding/validation/permission-status-check.ts +0 -5
  109. package/src/domains/onboarding/validation/screen-config-check.ts +0 -5
  110. package/src/domains/onboarding/validation/stored-progress-check.ts +1 -10
  111. package/src/domains/onboarding/validation/validation.test.ts +1 -6
  112. package/src/domains/permissions/hasPermission.ts +0 -8
  113. package/src/domains/permissions/types.ts +0 -20
  114. package/src/domains/settings/components/index.ts +0 -4
  115. package/src/domains/settings/hooks/index.ts +0 -4
  116. package/src/domains/settings/hooks/useSettings.ts +1 -26
  117. package/src/domains/settings/index.ts +0 -6
  118. package/src/domains/settings/types/index.ts +0 -4
  119. package/src/domains/settings/types/settings.ts +23 -67
  120. package/src/domains/settings/utils/index.ts +0 -4
  121. package/src/domains/settings/utils/settings.ts +0 -45
  122. package/src/styles/dashboard.css +818 -2
  123. package/src/domains/onboarding/components/shared/OptionCard.tsx +0 -73
  124. package/src/domains/onboarding/components/shared/PrimaryButton.tsx +0 -58
  125. package/src/domains/onboarding/components/shared/ProgressBar.tsx +0 -51
  126. package/src/domains/onboarding/components/shared/QuestionWrapper.tsx +0 -36
  127. package/src/domains/onboarding/components/shared/ScreenContainer.tsx +0 -29
  128. package/src/domains/onboarding/components/shared/index.ts +0 -16
  129. package/src/domains/onboarding/components/shared/renderBundledIcon.tsx +0 -20
  130. package/src/domains/onboarding/onboarding.css +0 -1
  131. package/src/domains/onboarding/theme/cn.ts +0 -10
  132. package/src/domains/onboarding/theme/index.css +0 -131
  133. package/src/domains/onboarding/theme/index.ts +0 -12
  134. package/src/domains/onboarding/theme/tokens.ts +0 -122
  135. /package/src/domains/onboarding/{components/hooks → hooks}/index.ts +0 -0
@@ -1,10 +1,5 @@
1
1
  import type { ReactNode } from 'react';
2
2
 
3
- /**
4
- * Discriminated union: every screen is one of 14 strictly-typed shapes.
5
- * Eliminates `Record<string, any>` masking (project rule: no `any`).
6
- */
7
-
8
3
  export interface BaseScreen<T extends string, D = unknown> {
9
4
  id: string;
10
5
  type: T;
@@ -19,8 +14,7 @@ export interface OnboardingOption {
19
14
  id: string;
20
15
  label: string;
21
16
  description?: string;
22
- iconName?: string; // lucide-react icon name; resolved by UI layer
23
- image?: string;
17
+ iconName?: string; image?: string;
24
18
  }
25
19
 
26
20
  export type WelcomeScreenConfig = BaseScreen<'welcome'> & {
@@ -131,8 +125,7 @@ export type ValueDeliveryScreenConfig<TResult = unknown> = BaseScreen<
131
125
  > & {
132
126
  title: string;
133
127
  result: TResult;
134
- /** Optional share affordance. Uses the Web Share API with a clipboard fallback. */
135
- shareConfig?: {
128
+ shareConfig?: {
136
129
  text: string;
137
130
  url?: string;
138
131
  };
@@ -148,8 +141,7 @@ export type AccountGateScreenConfig = BaseScreen<'account-gate', { signedIn: boo
148
141
  };
149
142
 
150
143
  export type PaywallFeature = {
151
- iconName: string; // lucide-react
152
- text: string;
144
+ iconName: string; text: string;
153
145
  highlighted?: boolean;
154
146
  };
155
147
 
@@ -1,21 +1,8 @@
1
- /**
2
- * Analytics dispatch service.
3
- * Routes a typed onboarding event to the configured provider.
4
- * Provider-specific dispatch is isolated here; callers stay provider-agnostic.
5
- *
6
- * Contract: dispatch NEVER throws. Analytics is an observation channel — a
7
- * broken tracker must not take down the onboarding funnel. Failures are
8
- * surfaced to the console (the library's documented error channel) and
9
- * dropped.
10
- */
11
-
12
1
  import type {
13
2
  AnalyticsProvider,
14
3
  OnboardingAnalyticsEvent,
15
4
  } from '../types/config';
16
5
 
17
- /** Provider globals are read through a LOCAL view of `window` — this package
18
- * must never augment the global `Window` type for its consumers. */
19
6
  type AnalyticsWindow = Window & {
20
7
  gtag?: (...args: unknown[]) => void;
21
8
  amplitude?: {
@@ -25,8 +12,7 @@ type AnalyticsWindow = Window & {
25
12
  };
26
13
 
27
14
  function dispatchToProvider(event: OnboardingAnalyticsEvent, provider: AnalyticsProvider): void {
28
- if (typeof window === 'undefined') return; // SSR / non-browser: nothing to do.
29
- const win = window as AnalyticsWindow;
15
+ if (typeof window === 'undefined') return; const win = window as AnalyticsWindow;
30
16
  switch (provider) {
31
17
  case 'google-analytics':
32
18
  win.gtag?.('event', event.name, event as unknown as Record<string, unknown>);
@@ -40,8 +26,7 @@ function dispatchToProvider(event: OnboardingAnalyticsEvent, provider: Analytics
40
26
  win.mixpanel?.track(event.name, event as unknown as Record<string, unknown>);
41
27
  return;
42
28
  case 'custom':
43
- return; // Handled by the caller via the customTracker path.
44
- }
29
+ return; }
45
30
  }
46
31
 
47
32
  function dispatchToCustomTracker(
@@ -59,10 +44,6 @@ function dispatchToCustomTracker(
59
44
  customTracker(event);
60
45
  }
61
46
 
62
- /**
63
- * Routes an event to the configured provider. Never throws: provider failures
64
- * are logged and dropped so an analytics outage cannot break the flow UI.
65
- */
66
47
  export function dispatchAnalyticsEvent(
67
48
  event: OnboardingAnalyticsEvent,
68
49
  provider: AnalyticsProvider | undefined,
@@ -1,2 +1 @@
1
- /** Default localStorage key for onboarding progress. */
2
1
  export const defaultStorageKey = '@umituz/web-onboarding:progress';
@@ -9,5 +9,4 @@ export type { RehydratedProgress } from './local-storage-bridge';
9
9
  export { queryPermissionStatus, requestPermission } from './permission-bridge';
10
10
  export { defaultStorageKey } from './default-storage-key';
11
11
 
12
- // Re-export validation for convenience (the public API treats validation as a util).
13
12
  export { checkFlowStructure, checkScreenConfig, checkStoredProgress } from '../validation';
@@ -1,9 +1,3 @@
1
- /**
2
- * localStorage bridge for onboarding progress.
3
- * Owns the I/O — validation, parsing, and the schema version are delegated
4
- * to the validation module so this file is a thin persistence layer.
5
- */
6
-
7
1
  import { elapsedMillis } from '../calc/elapsed-millis';
8
2
  import { isLocalStorageAvailable } from '../predicates/storage-available';
9
3
  import { checkStoredProgress } from '../validation/stored-progress-check';
@@ -22,10 +16,6 @@ function buildKey(customKey: string | undefined): string {
22
16
  return customKey ?? defaultStorageKey;
23
17
  }
24
18
 
25
- /**
26
- * Save the current progress to localStorage. Returns true on success.
27
- * Surfaces persistence failures to the console; never silently masks them.
28
- */
29
19
  export function saveOnboardingProgress(
30
20
  key: string | undefined,
31
21
  payload: {
@@ -55,18 +45,13 @@ export function saveOnboardingProgress(
55
45
  }
56
46
  }
57
47
 
58
- /**
59
- * Load and validate a previously saved progress payload.
60
- * Returns null if storage is unavailable, nothing is stored, or the payload is corrupt.
61
- */
62
48
  export function loadOnboardingProgress(key: string | undefined): RehydratedProgress | null {
63
49
  if (!isLocalStorageAvailable()) return null;
64
50
  const raw = window.localStorage.getItem(buildKey(key));
65
51
  const checked = checkStoredProgress(raw);
66
52
  if (!checked.ok) {
67
53
  if (raw !== null) {
68
- // Corrupt payload — discard so future saves start clean.
69
- window.localStorage.removeItem(buildKey(key));
54
+ window.localStorage.removeItem(buildKey(key));
70
55
  }
71
56
  return null;
72
57
  }
@@ -79,16 +64,11 @@ export function loadOnboardingProgress(key: string | undefined): RehydratedProgr
79
64
  };
80
65
  }
81
66
 
82
- /** Remove the stored payload. Safe to call when nothing is stored. */
83
67
  export function clearOnboardingProgress(key: string | undefined): void {
84
68
  if (!isLocalStorageAvailable()) return;
85
69
  window.localStorage.removeItem(buildKey(key));
86
70
  }
87
71
 
88
- /**
89
- * Build the final `OnboardingResult` value the consumer receives via
90
- * `onComplete`. Owns the duration calculation; no magic numbers here.
91
- */
92
72
  export function buildOnboardingResult(input: {
93
73
  completedSteps: ReadonlyArray<string>;
94
74
  collected: Record<string, unknown>;
@@ -1,9 +1,3 @@
1
- /**
2
- * Browser permission API bridge.
3
- * Owns the I/O — the predicates and the validation module decide whether
4
- * the runtime supports the call, and how to normalize the raw status.
5
- */
6
-
7
1
  import {
8
2
  doesBrowserSupportMediaCapture,
9
3
  doesBrowserSupportGeolocation,
@@ -14,7 +8,6 @@ import type { PermissionStatus, PermissionType } from '../types/screens';
14
8
 
15
9
  const GEOLOCATION_TIMEOUT_MS = 5000;
16
10
 
17
- /** Read the current permission status without prompting the user. */
18
11
  export async function queryPermissionStatus(permission: PermissionType): Promise<PermissionStatus> {
19
12
  if (typeof window === 'undefined' || typeof navigator === 'undefined') return 'unsupported';
20
13
 
@@ -51,7 +44,6 @@ export async function queryPermissionStatus(permission: PermissionType): Promise
51
44
  }
52
45
  }
53
46
 
54
- /** Prompt the user for the given permission. Never throws — failures map to a status. */
55
47
  export async function requestPermission(permission: PermissionType): Promise<PermissionStatus> {
56
48
  if (typeof window === 'undefined' || typeof navigator === 'undefined') return 'unsupported';
57
49
 
@@ -5,12 +5,6 @@ export type ValidationOutcome = { ok: true } | { ok: false; reason: string };
5
5
 
6
6
  const MIN_FLOW_LENGTH = 1;
7
7
 
8
- /**
9
- * Validates the structure of an entire onboarding flow:
10
- * - At least one screen exists
11
- * - Every screen passes its own check
12
- * - Step IDs are unique
13
- */
14
8
  export function checkFlowStructure(screens: ReadonlyArray<OnboardingScreen>): ValidationOutcome {
15
9
  if (!Array.isArray(screens) || screens.length < MIN_FLOW_LENGTH) {
16
10
  return { ok: false, reason: 'flow must contain at least one screen' };
@@ -7,15 +7,10 @@ const RECOGNIZED_STATUSES: ReadonlySet<PermissionStatus> = new Set<PermissionSta
7
7
  'unsupported',
8
8
  ]);
9
9
 
10
- /** Is the given string one of the recognized permission statuses? */
11
10
  export function isRecognizedPermissionStatus(value: unknown): value is PermissionStatus {
12
11
  return typeof value === 'string' && RECOGNIZED_STATUSES.has(value as PermissionStatus);
13
12
  }
14
13
 
15
- /**
16
- * Normalize a browser-level permission string (which may be "default")
17
- * to our internal PermissionStatus enum.
18
- */
19
14
  export function normalizePermissionStatus(rawValue: string | null | undefined): PermissionStatus {
20
15
  if (rawValue === 'granted' || rawValue === 'denied') return rawValue;
21
16
  if (rawValue === 'default' || rawValue === 'prompt') return 'prompt';
@@ -66,10 +66,6 @@ function checkPaywall(screen: OnboardingScreen): ValidationOutcome {
66
66
  return { ok: true };
67
67
  }
68
68
 
69
- /**
70
- * Validates a single screen config. Per-screen rules live here so the file
71
- * is the single place to look when a new screen type is added.
72
- */
73
69
  export function checkScreenConfig(screen: OnboardingScreen): ValidationOutcome {
74
70
  if (!isPlainObject(screen)) {
75
71
  return { ok: false, reason: 'screen must be an object' };
@@ -99,7 +95,6 @@ export function checkScreenConfig(screen: OnboardingScreen): ValidationOutcome {
99
95
  }
100
96
  }
101
97
 
102
- /** The set of screen types the orchestrator knows how to render. */
103
98
  export function getRecognizedScreenTypes(): ReadonlySet<OnboardingScreenType> {
104
99
  return RECOGNIZED_SCREEN_TYPES;
105
100
  }
@@ -1,17 +1,9 @@
1
- /**
2
- * Storage payload integrity check.
3
- * Decides whether a localStorage string is something we can safely rehydrate
4
- * from. The shape is versioned so future migrations stay clean.
5
- */
6
-
7
1
  export interface StoredProgressShape {
8
2
  version: 1;
9
3
  currentStep: number;
10
4
  completedSteps: string[];
11
5
  collected: Record<string, unknown>;
12
- startedAt: string; // ISO
13
- lastUpdated: string; // ISO
14
- }
6
+ startedAt: string; lastUpdated: string; }
15
7
 
16
8
  export type StoredProgressOutcome =
17
9
  | { ok: true; data: StoredProgressShape }
@@ -34,7 +26,6 @@ function isIsoDateString(value: unknown): value is string {
34
26
  return Number.isFinite(ms);
35
27
  }
36
28
 
37
- /** Is the raw string a valid, current-version stored progress payload? */
38
29
  export function checkStoredProgress(raw: string | null): StoredProgressOutcome {
39
30
  if (raw === null) return { ok: false, reason: 'no stored progress' };
40
31
  let parsed: unknown;
@@ -8,7 +8,6 @@ import {
8
8
  } from './permission-status-check';
9
9
  import type { OnboardingScreen } from '../types/screens';
10
10
 
11
- // TEST_SCENARIOS.md §23
12
11
  const welcome = (id = 'a'): OnboardingScreen => ({
13
12
  id,
14
13
  type: 'welcome',
@@ -31,8 +30,7 @@ describe('flow-structure-check (§23.1)', () => {
31
30
 
32
31
  it('accepts a valid flow and propagates per-screen failures', () => {
33
32
  expect(checkFlowStructure([welcome('a'), welcome('b')])).toEqual({ ok: true });
34
- // goal-question without options fails at the flow level
35
- const bad = checkFlowStructure([
33
+ const bad = checkFlowStructure([
36
34
  welcome('a'),
37
35
  { id: 'g', type: 'goal-question', question: 'Q', options: [], onChange: () => undefined },
38
36
  ]);
@@ -40,7 +38,6 @@ describe('flow-structure-check (§23.1)', () => {
40
38
  });
41
39
  });
42
40
 
43
- // TEST_SCENARIOS.md §23.2
44
41
  describe('screen-config-check (§23.2)', () => {
45
42
  it('rejects malformed screens', () => {
46
43
  // @ts-expect-error -- deliberately malformed input (null is not a screen)
@@ -89,7 +86,6 @@ describe('screen-config-check (§23.2)', () => {
89
86
  });
90
87
  });
91
88
 
92
- // TEST_SCENARIOS.md §23.3
93
89
  describe('stored-progress-check (§23.3)', () => {
94
90
  const validPayload = JSON.stringify({
95
91
  version: 1,
@@ -157,7 +153,6 @@ describe('stored-progress-check (§23.3)', () => {
157
153
  });
158
154
  });
159
155
 
160
- // TEST_SCENARIOS.md §23.4
161
156
  describe('permission-status-check (§23.4)', () => {
162
157
  it('normalizes browser-level permission strings', () => {
163
158
  expect(normalizePermissionStatus('granted')).toBe('granted');
@@ -1,11 +1,3 @@
1
- /**
2
- * hasPermission — pure permission lookup against a matrix.
3
- *
4
- * Deny-by-default: unknown roles, unknown permissions and empty
5
- * grants all resolve to `false`. Only an exact entry, a module
6
- * wildcard ("sales.*") or the full wildcard ("*") grants access.
7
- */
8
-
9
1
  import {
10
2
  PERMISSION_WILDCARD,
11
3
  type Permission,
@@ -1,33 +1,13 @@
1
- /**
2
- * Permission model — generalized from the texora/visora fleet pattern.
3
- *
4
- * Hosts keep their own role and permission vocabularies (string unions
5
- * in the app); the package treats them as opaque strings and stays
6
- * camp-agnostic. A matrix maps each role to either the full-grant
7
- * wildcard `"*"` or a list of permission strings, which may themselves
8
- * end in `.*` to grant every permission of a module
9
- * (e.g. `"sales.*"` grants `"sales.view"`).
10
- */
11
-
12
- /** A single permission, e.g. "sales.view". Opaque to the package. */
13
1
  export type Permission = string;
14
2
 
15
- /** A role key, e.g. "owner", "operator". Opaque to the package. */
16
3
  export type RoleKey = string;
17
4
 
18
- /** Full-grant wildcard — grants every permission. */
19
5
  export const PERMISSION_WILDCARD = "*" as const;
20
6
 
21
- /**
22
- * What a role grants: the wildcard or an explicit permission list
23
- * (module wildcards like `"sales.*"` allowed).
24
- */
25
7
  export type RoleGrant = typeof PERMISSION_WILDCARD | readonly string[];
26
8
 
27
- /** Role x permission matrix. */
28
9
  export type PermissionMatrix = Readonly<Record<RoleKey, RoleGrant>>;
29
10
 
30
- /** Display metadata for a role (matrix screens). */
31
11
  export interface RoleInfo {
32
12
  key: RoleKey;
33
13
  label: string;
@@ -1,7 +1,3 @@
1
- /**
2
- * Settings Components Export
3
- */
4
-
5
1
  export { SettingsLayout } from './SettingsLayout';
6
2
  export { SettingsSection } from './SettingsSection';
7
3
  export {
@@ -1,7 +1,3 @@
1
- /**
2
- * Settings Hooks Export
3
- */
4
-
5
1
  export {
6
2
  useSettings,
7
3
  } from './useSettings';
@@ -1,26 +1,11 @@
1
- /**
2
- * Settings Hooks
3
- *
4
- * Custom React hooks for settings functionality.
5
- * Uses a single `useReducer` so the three update paths share one
6
- * well-tested tree-walking helper instead of duplicating it.
7
- */
8
-
9
1
  import { useReducer, useCallback } from "react";
10
2
  import type { SettingsConfig, SettingsItem } from "../types/settings";
11
3
 
12
- /**
13
- * Action discriminated union for the settings reducer.
14
- */
15
4
  type SettingsAction =
16
5
  | { type: 'updateItem'; sectionKey: string; itemKey: string; updates: Partial<SettingsItem> }
17
6
  | { type: 'toggleItem'; sectionKey: string; itemKey: string }
18
7
  | { type: 'setItemBadge'; sectionKey: string; itemKey: string; badge: number | undefined };
19
8
 
20
- /**
21
- * Apply a transform to the item identified by (sectionKey, itemKey).
22
- * Single source of truth for the tree-walking logic.
23
- */
24
9
  const mapItemInTree = (
25
10
  config: SettingsConfig,
26
11
  sectionKey: string,
@@ -48,8 +33,7 @@ const settingsReducer = (state: SettingsConfig, action: SettingsAction): Setting
48
33
  case 'toggleItem':
49
34
  return mapItemInTree(state, action.sectionKey, action.itemKey, (item) => ({
50
35
  ...item,
51
- // Tri-state semantics: undefined is treated as enabled by default.
52
- enabled: item.enabled === undefined ? false : !item.enabled,
36
+ enabled: item.enabled === undefined ? false : !item.enabled,
53
37
  }));
54
38
  case 'setItemBadge':
55
39
  return mapItemInTree(state, action.sectionKey, action.itemKey, (item) => ({
@@ -59,10 +43,6 @@ const settingsReducer = (state: SettingsConfig, action: SettingsAction): Setting
59
43
  }
60
44
  };
61
45
 
62
- /**
63
- * Public surface of the hook. Returned callbacks are stable
64
- * (no deps that change between renders).
65
- */
66
46
  export interface UseSettingsReturn {
67
47
  config: SettingsConfig;
68
48
  updateItem: (sectionKey: string, itemKey: string, updates: Partial<SettingsItem>) => void;
@@ -70,11 +50,6 @@ export interface UseSettingsReturn {
70
50
  setItemBadge: (sectionKey: string, itemKey: string, badge: number | undefined) => void;
71
51
  }
72
52
 
73
- /**
74
- * Use Settings Hook
75
- *
76
- * Manages settings configuration state and provides update actions.
77
- */
78
53
  export function useSettings(initialConfig: SettingsConfig): UseSettingsReturn {
79
54
  const [config, dispatch] = useReducer(settingsReducer, initialConfig);
80
55
 
@@ -1,9 +1,3 @@
1
- /**
2
- * @umituz/web-dashboard - Settings Domain
3
- *
4
- * Config-driven Settings System
5
- */
6
-
7
1
  export { SettingsLayout, SettingsSection } from './components';
8
2
  export { useSettings } from './hooks';
9
3
  export {
@@ -1,7 +1,3 @@
1
- /**
2
- * Settings Types Export
3
- */
4
-
5
1
  export type {
6
2
  SettingsItem,
7
3
  SettingsSection,
@@ -1,83 +1,39 @@
1
- /**
2
- * Settings Types
3
- *
4
- * Type definitions for settings system
5
- */
6
-
7
1
  import type { LucideIcon } from "lucide-react";
8
2
 
9
- /**
10
- * Settings item configuration
11
- */
12
3
  export interface SettingsItem {
13
- /** Unique key for the setting */
14
- key: string;
15
- /** Display label (can be i18n key) */
16
- label: string;
17
- /** Description text (optional) */
18
- description?: string;
19
- /** Icon component */
20
- icon?: LucideIcon;
21
- /** Route path for this setting */
22
- path?: string;
23
- /** Click handler when no route is available */
24
- onClick?: () => void;
25
- /** Whether this item is enabled */
26
- enabled?: boolean;
27
- /** Required permission to access */
28
- permission?: string;
29
- /** Badge count (for notifications) */
30
- badge?: number;
4
+ key: string;
5
+ label: string;
6
+ description?: string;
7
+ icon?: LucideIcon;
8
+ path?: string;
9
+ onClick?: () => void;
10
+ enabled?: boolean;
11
+ permission?: string;
12
+ badge?: number;
31
13
  }
32
14
 
33
- /**
34
- * Settings section configuration
35
- */
36
15
  export interface SettingsSection {
37
- /** Section title */
38
- title: string;
39
- /** Section key */
40
- key: string;
41
- /** Items in this section */
42
- items: SettingsItem[];
43
- /** Optional: Section icon */
44
- icon?: LucideIcon;
16
+ title: string;
17
+ key: string;
18
+ items: SettingsItem[];
19
+ icon?: LucideIcon;
45
20
  }
46
21
 
47
- /**
48
- * Settings layout configuration
49
- */
50
22
  export interface SettingsConfig {
51
- /** Settings sections */
52
- sections: SettingsSection[];
53
- /** Brand name */
54
- brandName?: string;
55
- /** Default route */
56
- defaultRoute?: string;
23
+ sections: SettingsSection[];
24
+ brandName?: string;
25
+ defaultRoute?: string;
57
26
  }
58
27
 
59
- /**
60
- * Settings layout props
61
- */
62
28
  export interface SettingsLayoutProps {
63
- /** Settings configuration */
64
- config: SettingsConfig;
65
- /** Current path */
66
- currentPath?: string;
67
- /** On navigate callback */
68
- onNavigate?: (path: string) => void;
29
+ config: SettingsConfig;
30
+ currentPath?: string;
31
+ onNavigate?: (path: string) => void;
69
32
  }
70
33
 
71
- /**
72
- * Settings section component props
73
- */
74
34
  export interface SettingsSectionProps {
75
- /** Section configuration */
76
- section: SettingsSection;
77
- /** Current path */
78
- currentPath?: string;
79
- /** On navigate callback */
80
- onNavigate?: (path: string) => void;
81
- /** Collapsed state */
82
- collapsed?: boolean;
35
+ section: SettingsSection;
36
+ currentPath?: string;
37
+ onNavigate?: (path: string) => void;
38
+ collapsed?: boolean;
83
39
  }
@@ -1,7 +1,3 @@
1
- /**
2
- * Settings Utils Export
3
- */
4
-
5
1
  export {
6
2
  findSettingsItem,
7
3
  findSettingsSection,
@@ -1,18 +1,5 @@
1
- /**
2
- * Settings Utilities
3
- *
4
- * Utility functions for settings operations
5
- */
6
-
7
1
  import type { SettingsConfig, SettingsItem, SettingsSection } from "../types/settings";
8
2
 
9
- /**
10
- * Find settings item by key
11
- *
12
- * @param config - Settings configuration
13
- * @param itemKey - Item key to find
14
- * @returns Item or undefined
15
- */
16
3
  export function findSettingsItem(config: SettingsConfig, itemKey: string): SettingsItem | undefined {
17
4
  for (const section of config.sections) {
18
5
  const item = section.items.find((i) => i.key === itemKey);
@@ -21,24 +8,10 @@ export function findSettingsItem(config: SettingsConfig, itemKey: string): Setti
21
8
  return undefined;
22
9
  }
23
10
 
24
- /**
25
- * Find settings section by key
26
- *
27
- * @param config - Settings configuration
28
- * @param sectionKey - Section key to find
29
- * @returns Section or undefined
30
- */
31
11
  export function findSettingsSection(config: SettingsConfig, sectionKey: string): SettingsSection | undefined {
32
12
  return config.sections.find((s) => s.key === sectionKey);
33
13
  }
34
14
 
35
- /**
36
- * Filter settings items by permission
37
- *
38
- * @param items - Settings items to filter
39
- * @param userPermissions - User permissions
40
- * @returns Filtered items
41
- */
42
15
  export function filterSettingsByPermission<T extends SettingsItem>(
43
16
  items: T[],
44
17
  userPermissions?: string[]
@@ -51,19 +24,8 @@ export function filterSettingsByPermission<T extends SettingsItem>(
51
24
  });
52
25
  }
53
26
 
54
- /**
55
- * Fallback route when no default or first available route can be resolved.
56
- * Caller should treat this as an explicit "no route configured" signal
57
- * rather than a silent success.
58
- */
59
27
  export const SETTINGS_FALLBACK_ROUTE = "/settings";
60
28
 
61
- /**
62
- * Get default route from settings config
63
- *
64
- * Returns null when no default route is resolvable. The caller is
65
- * responsible for handling that case (e.g. redirect, error).
66
- */
67
29
  export function getSettingsDefaultRoute(config: SettingsConfig): string | null {
68
30
  if (config.defaultRoute) return config.defaultRoute;
69
31
 
@@ -75,13 +37,6 @@ export function getSettingsDefaultRoute(config: SettingsConfig): string | null {
75
37
  return null;
76
38
  }
77
39
 
78
- /**
79
- * Generate settings item key
80
- *
81
- * @param prefix - Key prefix
82
- * @param identifier - Unique identifier
83
- * @returns Generated key
84
- */
85
40
  export function generateSettingsKey(prefix: string, identifier: string): string {
86
41
  return `${prefix}_${identifier}`;
87
42
  }