@umituz/web-dashboard 6.0.0 → 6.2.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 (133) hide show
  1. package/README.md +47 -73
  2. package/package.json +15 -7
  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 +173 -0
  45. package/src/domains/onboarding/calc/card-stack-motion-curve.ts +27 -0
  46. package/src/domains/onboarding/calc/count-with-locale.ts +8 -0
  47. package/src/domains/onboarding/calc/duration-equal-split.ts +12 -0
  48. package/src/domains/onboarding/calc/elapsed-millis.ts +6 -0
  49. package/src/domains/onboarding/calc/index.ts +36 -0
  50. package/src/domains/onboarding/calc/progress-percentage.ts +27 -0
  51. package/src/domains/onboarding/calc/slider-value-bounds.ts +34 -0
  52. package/src/domains/onboarding/calc/step-index-clamp.ts +19 -0
  53. package/src/domains/onboarding/calc/swipe-gesture-curve.ts +54 -0
  54. package/src/domains/onboarding/components/AnimationWrapper.tsx +69 -0
  55. package/src/domains/onboarding/components/OnboardingErrorBoundary.tsx +61 -0
  56. package/src/domains/onboarding/components/OnboardingFlow.test.tsx +459 -0
  57. package/src/domains/onboarding/components/OnboardingFlow.tsx +313 -0
  58. package/src/domains/onboarding/components/OnboardingIntl.tsx +98 -0
  59. package/src/domains/onboarding/components/PrimaryButton.tsx +40 -0
  60. package/src/domains/onboarding/components/ProgressBar.tsx +32 -0
  61. package/src/domains/onboarding/components/QuestionWrapper.tsx +28 -0
  62. package/src/domains/onboarding/components/ScreenContainer.tsx +19 -0
  63. package/src/domains/onboarding/components/SwipeCard.test.tsx +49 -0
  64. package/src/domains/onboarding/components/SwipeCard.tsx +88 -0
  65. package/src/domains/onboarding/components/renderBundledIcon.tsx +7 -0
  66. package/src/domains/onboarding/components/screens/AccountGateScreen.tsx +79 -0
  67. package/src/domains/onboarding/components/screens/AppDemoScreen.tsx +33 -0
  68. package/src/domains/onboarding/components/screens/ComparisonScreen.tsx +49 -0
  69. package/src/domains/onboarding/components/screens/GoalQuestionScreen.tsx +59 -0
  70. package/src/domains/onboarding/components/screens/PainPointsScreen.tsx +65 -0
  71. package/src/domains/onboarding/components/screens/PaywallScreen.tsx +122 -0
  72. package/src/domains/onboarding/components/screens/PermissionPrimingScreen.tsx +98 -0
  73. package/src/domains/onboarding/components/screens/PersonalizedSolutionScreen.tsx +40 -0
  74. package/src/domains/onboarding/components/screens/PreferencesScreen.tsx +180 -0
  75. package/src/domains/onboarding/components/screens/ProcessingScreen.tsx +63 -0
  76. package/src/domains/onboarding/components/screens/SocialProofScreen.tsx +87 -0
  77. package/src/domains/onboarding/components/screens/TinderCardsScreen.tsx +91 -0
  78. package/src/domains/onboarding/components/screens/ValueDeliveryScreen.tsx +69 -0
  79. package/src/domains/onboarding/components/screens/WelcomeScreen.tsx +32 -0
  80. package/src/domains/onboarding/components/screens/index.ts +28 -0
  81. package/src/domains/onboarding/hooks/index.ts +7 -0
  82. package/src/domains/onboarding/hooks/useOnboardingAnalytics.test.tsx +74 -0
  83. package/src/domains/onboarding/hooks/useOnboardingAnalytics.ts +49 -0
  84. package/src/domains/onboarding/hooks/useOnboardingProgress.test.tsx +150 -0
  85. package/src/domains/onboarding/hooks/useOnboardingProgress.ts +249 -0
  86. package/src/domains/onboarding/hooks/usePermissionPriming.ts +45 -0
  87. package/src/domains/onboarding/hooks/useReducedMotion.ts +19 -0
  88. package/src/domains/onboarding/index.ts +153 -0
  89. package/src/domains/onboarding/predicates/has-handler.ts +21 -0
  90. package/src/domains/onboarding/predicates/icon-name-resolves.ts +97 -0
  91. package/src/domains/onboarding/predicates/index.ts +26 -0
  92. package/src/domains/onboarding/predicates/permission-granted.ts +21 -0
  93. package/src/domains/onboarding/predicates/permission-supports.ts +56 -0
  94. package/src/domains/onboarding/predicates/predicates.test.ts +188 -0
  95. package/src/domains/onboarding/predicates/screen-emits-data.ts +13 -0
  96. package/src/domains/onboarding/predicates/storage-available.ts +14 -0
  97. package/src/domains/onboarding/templates/TemplateDemoFallback.tsx +92 -0
  98. package/src/domains/onboarding/templates/art-onboarding.ts +71 -0
  99. package/src/domains/onboarding/templates/health-onboarding.ts +92 -0
  100. package/src/domains/onboarding/templates/index.ts +6 -0
  101. package/src/domains/onboarding/templates/platform-onboarding.ts +201 -0
  102. package/src/domains/onboarding/templates/video-onboarding.ts +95 -0
  103. package/src/domains/onboarding/test/setup.ts +23 -0
  104. package/src/domains/onboarding/types/config.ts +57 -0
  105. package/src/domains/onboarding/types/index.ts +10 -0
  106. package/src/domains/onboarding/types/labels.ts +49 -0
  107. package/src/domains/onboarding/types/screens.ts +185 -0
  108. package/src/domains/onboarding/utils/analytics-dispatch.test.ts +93 -0
  109. package/src/domains/onboarding/utils/analytics-dispatch.ts +73 -0
  110. package/src/domains/onboarding/utils/default-storage-key.ts +1 -0
  111. package/src/domains/onboarding/utils/index.ts +12 -0
  112. package/src/domains/onboarding/utils/local-storage-bridge.test.ts +93 -0
  113. package/src/domains/onboarding/utils/local-storage-bridge.ts +84 -0
  114. package/src/domains/onboarding/utils/permission-bridge.test.ts +64 -0
  115. package/src/domains/onboarding/utils/permission-bridge.ts +87 -0
  116. package/src/domains/onboarding/validation/flow-structure-check.ts +22 -0
  117. package/src/domains/onboarding/validation/index.ts +9 -0
  118. package/src/domains/onboarding/validation/permission-status-check.ts +18 -0
  119. package/src/domains/onboarding/validation/screen-config-check.ts +100 -0
  120. package/src/domains/onboarding/validation/stored-progress-check.ts +59 -0
  121. package/src/domains/onboarding/validation/validation.test.ts +170 -0
  122. package/src/domains/permissions/hasPermission.ts +0 -8
  123. package/src/domains/permissions/types.ts +0 -20
  124. package/src/domains/settings/components/index.ts +0 -4
  125. package/src/domains/settings/hooks/index.ts +0 -4
  126. package/src/domains/settings/hooks/useSettings.ts +1 -26
  127. package/src/domains/settings/index.ts +0 -6
  128. package/src/domains/settings/types/index.ts +0 -4
  129. package/src/domains/settings/types/settings.ts +23 -67
  130. package/src/domains/settings/utils/index.ts +0 -4
  131. package/src/domains/settings/utils/settings.ts +0 -45
  132. package/src/styles/dashboard.contract.test.ts +1 -0
  133. package/src/styles/dashboard.css +818 -1
@@ -0,0 +1,59 @@
1
+ export interface StoredProgressShape {
2
+ version: 1;
3
+ currentStep: number;
4
+ completedSteps: string[];
5
+ collected: Record<string, unknown>;
6
+ startedAt: string; lastUpdated: string; }
7
+
8
+ export type StoredProgressOutcome =
9
+ | { ok: true; data: StoredProgressShape }
10
+ | { ok: false; reason: string };
11
+
12
+ const STORAGE_VERSION = 1;
13
+ const MIN_STEP_INDEX = 0;
14
+
15
+ function isObject(value: unknown): value is Record<string, unknown> {
16
+ return typeof value === 'object' && value !== null;
17
+ }
18
+
19
+ function isStringArray(value: unknown): value is string[] {
20
+ return Array.isArray(value) && value.every((v) => typeof v === 'string');
21
+ }
22
+
23
+ function isIsoDateString(value: unknown): value is string {
24
+ if (typeof value !== 'string') return false;
25
+ const ms = Date.parse(value);
26
+ return Number.isFinite(ms);
27
+ }
28
+
29
+ export function checkStoredProgress(raw: string | null): StoredProgressOutcome {
30
+ if (raw === null) return { ok: false, reason: 'no stored progress' };
31
+ let parsed: unknown;
32
+ try {
33
+ parsed = JSON.parse(raw);
34
+ } catch (e) {
35
+ return {
36
+ ok: false,
37
+ reason: e instanceof Error ? e.message : 'invalid JSON',
38
+ };
39
+ }
40
+ if (!isObject(parsed)) {
41
+ return { ok: false, reason: 'stored payload must be an object' };
42
+ }
43
+ if (parsed.version !== STORAGE_VERSION) {
44
+ return { ok: false, reason: `unsupported storage version: ${String(parsed.version)}` };
45
+ }
46
+ if (typeof parsed.currentStep !== 'number' || parsed.currentStep < MIN_STEP_INDEX) {
47
+ return { ok: false, reason: 'currentStep must be a non-negative number' };
48
+ }
49
+ if (!isStringArray(parsed.completedSteps)) {
50
+ return { ok: false, reason: 'completedSteps must be a string array' };
51
+ }
52
+ if (!isObject(parsed.collected)) {
53
+ return { ok: false, reason: 'collected must be an object' };
54
+ }
55
+ if (!isIsoDateString(parsed.startedAt) || !isIsoDateString(parsed.lastUpdated)) {
56
+ return { ok: false, reason: 'startedAt and lastUpdated must be ISO date strings' };
57
+ }
58
+ return { ok: true, data: parsed as unknown as StoredProgressShape };
59
+ }
@@ -0,0 +1,170 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { checkFlowStructure } from './flow-structure-check';
3
+ import { checkScreenConfig, getRecognizedScreenTypes } from './screen-config-check';
4
+ import { checkStoredProgress } from './stored-progress-check';
5
+ import {
6
+ isRecognizedPermissionStatus,
7
+ normalizePermissionStatus,
8
+ } from './permission-status-check';
9
+ import type { OnboardingScreen } from '../types/screens';
10
+
11
+ const welcome = (id = 'a'): OnboardingScreen => ({
12
+ id,
13
+ type: 'welcome',
14
+ title: 'T',
15
+ });
16
+
17
+ describe('flow-structure-check (§23.1)', () => {
18
+ it('rejects empty flows', () => {
19
+ expect(checkFlowStructure([])).toEqual({
20
+ ok: false,
21
+ reason: 'flow must contain at least one screen',
22
+ });
23
+ });
24
+
25
+ it('rejects duplicate step ids', () => {
26
+ const outcome = checkFlowStructure([welcome('dup'), welcome('dup')]);
27
+ expect(outcome.ok).toBe(false);
28
+ if (!outcome.ok) expect(outcome.reason).toMatch(/duplicate step id/);
29
+ });
30
+
31
+ it('accepts a valid flow and propagates per-screen failures', () => {
32
+ expect(checkFlowStructure([welcome('a'), welcome('b')])).toEqual({ ok: true });
33
+ const bad = checkFlowStructure([
34
+ welcome('a'),
35
+ { id: 'g', type: 'goal-question', question: 'Q', options: [], onChange: () => undefined },
36
+ ]);
37
+ expect(bad.ok).toBe(false);
38
+ });
39
+ });
40
+
41
+ describe('screen-config-check (§23.2)', () => {
42
+ it('rejects malformed screens', () => {
43
+ // @ts-expect-error -- deliberately malformed input (null is not a screen)
44
+ expect(checkScreenConfig(null).ok).toBe(false);
45
+ // @ts-expect-error -- deliberately malformed input (missing id)
46
+ expect(checkScreenConfig({ type: 'welcome' }).ok).toBe(false);
47
+ expect(checkScreenConfig({ id: '', type: 'welcome' } as OnboardingScreen).ok).toBe(false);
48
+ expect(
49
+ checkScreenConfig({ id: 'x', type: 'unknown' } as unknown as OnboardingScreen).ok,
50
+ ).toBe(false);
51
+ expect(
52
+ checkScreenConfig({ id: 'x', type: 'goal-question' } as OnboardingScreen).ok,
53
+ ).toBe(false);
54
+ });
55
+
56
+ it('requires content for content-driven screens', () => {
57
+ expect(
58
+ checkScreenConfig({
59
+ id: 'p',
60
+ type: 'pain-points',
61
+ painPoints: [],
62
+ } as unknown as OnboardingScreen).ok,
63
+ ).toBe(false);
64
+ expect(
65
+ checkScreenConfig({
66
+ id: 't',
67
+ type: 'tinder-cards',
68
+ statements: [],
69
+ } as unknown as OnboardingScreen).ok,
70
+ ).toBe(false);
71
+ expect(
72
+ checkScreenConfig({ id: 'pp', type: 'permission-priming' } as unknown as OnboardingScreen)
73
+ .ok,
74
+ ).toBe(false);
75
+ expect(
76
+ checkScreenConfig({ id: 'w', type: 'paywall', price: 9 } as unknown as OnboardingScreen).ok,
77
+ ).toBe(false);
78
+ });
79
+
80
+ it('accepts a well-formed screen', () => {
81
+ expect(checkScreenConfig(welcome('ok'))).toEqual({ ok: true });
82
+ });
83
+
84
+ it('knows every screen type the orchestrator can render', () => {
85
+ expect(getRecognizedScreenTypes().size).toBe(14);
86
+ });
87
+ });
88
+
89
+ describe('stored-progress-check (§23.3)', () => {
90
+ const validPayload = JSON.stringify({
91
+ version: 1,
92
+ currentStep: 2,
93
+ completedSteps: ['a', 'b'],
94
+ collected: { a: { selected: ['x'] } },
95
+ startedAt: '2026-01-01T10:00:00.000Z',
96
+ lastUpdated: '2026-01-01T10:01:00.000Z',
97
+ });
98
+
99
+ it('returns not-ok for missing, corrupt, or future-version payloads', () => {
100
+ expect(checkStoredProgress(null)).toEqual({ ok: false, reason: 'no stored progress' });
101
+ expect(checkStoredProgress('{ malformed').ok).toBe(false);
102
+ const v2 = checkStoredProgress(JSON.stringify({ version: 2 }));
103
+ expect(v2.ok).toBe(false);
104
+ if (!v2.ok) expect(v2.reason).toMatch(/unsupported storage version/);
105
+ });
106
+
107
+ it('rejects shape violations inside an otherwise-valid envelope', () => {
108
+ expect(
109
+ checkStoredProgress(
110
+ JSON.stringify({
111
+ version: 1,
112
+ currentStep: -1,
113
+ completedSteps: [],
114
+ collected: {},
115
+ startedAt: '2026-01-01T10:00:00.000Z',
116
+ lastUpdated: '2026-01-01T10:00:00.000Z',
117
+ }),
118
+ ).ok,
119
+ ).toBe(false);
120
+ expect(
121
+ checkStoredProgress(
122
+ JSON.stringify({
123
+ version: 1,
124
+ currentStep: 0,
125
+ completedSteps: 'nope',
126
+ collected: {},
127
+ startedAt: '2026-01-01T10:00:00.000Z',
128
+ lastUpdated: '2026-01-01T10:00:00.000Z',
129
+ }),
130
+ ).ok,
131
+ ).toBe(false);
132
+ expect(
133
+ checkStoredProgress(
134
+ JSON.stringify({
135
+ version: 1,
136
+ currentStep: 0,
137
+ completedSteps: [],
138
+ collected: {},
139
+ startedAt: 'not a date',
140
+ lastUpdated: '2026-01-01T10:00:00.000Z',
141
+ }),
142
+ ).ok,
143
+ ).toBe(false);
144
+ });
145
+
146
+ it('accepts a valid v1 payload and returns it typed', () => {
147
+ const outcome = checkStoredProgress(validPayload);
148
+ expect(outcome.ok).toBe(true);
149
+ if (outcome.ok) {
150
+ expect(outcome.data.currentStep).toBe(2);
151
+ expect(outcome.data.completedSteps).toEqual(['a', 'b']);
152
+ }
153
+ });
154
+ });
155
+
156
+ describe('permission-status-check (§23.4)', () => {
157
+ it('normalizes browser-level permission strings', () => {
158
+ expect(normalizePermissionStatus('granted')).toBe('granted');
159
+ expect(normalizePermissionStatus('denied')).toBe('denied');
160
+ expect(normalizePermissionStatus('default')).toBe('prompt');
161
+ expect(normalizePermissionStatus(null)).toBe('unsupported');
162
+ expect(normalizePermissionStatus('whatever')).toBe('unsupported');
163
+ });
164
+
165
+ it('recognizes exactly the four internal statuses', () => {
166
+ expect(isRecognizedPermissionStatus('prompt')).toBe(true);
167
+ expect(isRecognizedPermissionStatus('revoked')).toBe(false);
168
+ expect(isRecognizedPermissionStatus(42)).toBe(false);
169
+ });
170
+ });
@@ -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
  }
@@ -1,3 +1,4 @@
1
+ // @vitest-environment node
1
2
  import { readFileSync } from "node:fs";
2
3
  import { describe, expect, it } from "vitest";
3
4