@umituz/web-dashboard 2.0.7 → 2.0.9

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 (171) hide show
  1. package/package.json +35 -77
  2. package/src/domains/layouts/components/BrandLogo.tsx +83 -0
  3. package/src/domains/layouts/components/DashboardHeader.tsx +240 -0
  4. package/src/domains/layouts/components/DashboardLayout.tsx +155 -0
  5. package/src/domains/layouts/components/DashboardSidebar.tsx +152 -0
  6. package/src/domains/layouts/components/index.ts +8 -0
  7. package/src/domains/layouts/hooks/dashboard.ts +81 -0
  8. package/src/domains/layouts/hooks/index.ts +8 -0
  9. package/src/domains/layouts/index.ts +11 -0
  10. package/{dist/layouts/theme/default.js → src/domains/layouts/theme/default.ts} +18 -11
  11. package/src/domains/layouts/theme/index.ts +18 -0
  12. package/src/domains/layouts/theme/presets.ts +96 -0
  13. package/src/domains/layouts/theme/utils.ts +67 -0
  14. package/src/domains/layouts/types/index.ts +9 -0
  15. package/src/domains/layouts/types/layout.ts +43 -0
  16. package/src/domains/layouts/types/notification.ts +19 -0
  17. package/src/domains/layouts/types/sidebar.ts +35 -0
  18. package/src/domains/layouts/types/theme.ts +64 -0
  19. package/src/domains/layouts/types/user.ts +35 -0
  20. package/src/domains/layouts/utils/dashboard.ts +96 -0
  21. package/src/domains/layouts/utils/index.ts +11 -0
  22. package/src/domains/onboarding/components/AppFocusStep.tsx +113 -0
  23. package/src/domains/onboarding/components/OnboardingWizard.tsx +262 -0
  24. package/src/domains/onboarding/components/PlanStep.tsx +208 -0
  25. package/src/domains/onboarding/components/PlatformsStep.tsx +109 -0
  26. package/src/domains/onboarding/components/UserTypeStep.tsx +135 -0
  27. package/src/domains/onboarding/components/index.ts +9 -0
  28. package/src/domains/onboarding/hooks/index.ts +5 -0
  29. package/{dist/onboarding/hooks/index.js → src/domains/onboarding/hooks/useOnboarding.ts} +65 -19
  30. package/src/domains/onboarding/index.ts +35 -0
  31. package/src/domains/onboarding/types/index.ts +16 -0
  32. package/src/domains/onboarding/types/onboarding.ts +214 -0
  33. package/src/domains/onboarding/utils/index.ts +15 -0
  34. package/src/domains/onboarding/utils/onboarding.ts +166 -0
  35. package/src/domains/settings/components/SettingsLayout.tsx +144 -0
  36. package/src/domains/settings/components/SettingsSection.tsx +106 -0
  37. package/src/domains/settings/components/index.ts +6 -0
  38. package/src/domains/settings/hooks/index.ts +7 -0
  39. package/src/domains/settings/hooks/useSettings.ts +80 -0
  40. package/src/domains/settings/index.ts +22 -0
  41. package/src/domains/settings/types/index.ts +11 -0
  42. package/src/domains/settings/types/settings.ts +81 -0
  43. package/src/domains/settings/utils/index.ts +11 -0
  44. package/src/domains/settings/utils/settings.ts +80 -0
  45. package/dist/layouts/components/BrandLogo.d.ts +0 -18
  46. package/dist/layouts/components/BrandLogo.js +0 -88
  47. package/dist/layouts/components/BrandLogo.js.map +0 -1
  48. package/dist/layouts/components/DashboardHeader.d.ts +0 -36
  49. package/dist/layouts/components/DashboardHeader.js +0 -225
  50. package/dist/layouts/components/DashboardHeader.js.map +0 -1
  51. package/dist/layouts/components/DashboardLayout.d.ts +0 -45
  52. package/dist/layouts/components/DashboardLayout.js +0 -501
  53. package/dist/layouts/components/DashboardLayout.js.map +0 -1
  54. package/dist/layouts/components/DashboardSidebar.d.ts +0 -29
  55. package/dist/layouts/components/DashboardSidebar.js +0 -189
  56. package/dist/layouts/components/DashboardSidebar.js.map +0 -1
  57. package/dist/layouts/components/index.d.ts +0 -10
  58. package/dist/layouts/components/index.js +0 -502
  59. package/dist/layouts/components/index.js.map +0 -1
  60. package/dist/layouts/hooks/dashboard.d.ts +0 -35
  61. package/dist/layouts/hooks/dashboard.js +0 -57
  62. package/dist/layouts/hooks/dashboard.js.map +0 -1
  63. package/dist/layouts/hooks/index.d.ts +0 -3
  64. package/dist/layouts/hooks/index.js +0 -57
  65. package/dist/layouts/hooks/index.js.map +0 -1
  66. package/dist/layouts/index.d.ts +0 -17
  67. package/dist/layouts/index.js +0 -756
  68. package/dist/layouts/index.js.map +0 -1
  69. package/dist/layouts/theme/default.d.ts +0 -18
  70. package/dist/layouts/theme/default.js.map +0 -1
  71. package/dist/layouts/theme/index.d.ts +0 -4
  72. package/dist/layouts/theme/index.js +0 -184
  73. package/dist/layouts/theme/index.js.map +0 -1
  74. package/dist/layouts/theme/presets.d.ts +0 -14
  75. package/dist/layouts/theme/presets.js +0 -137
  76. package/dist/layouts/theme/presets.js.map +0 -1
  77. package/dist/layouts/theme/utils.d.ts +0 -22
  78. package/dist/layouts/theme/utils.js +0 -181
  79. package/dist/layouts/theme/utils.js.map +0 -1
  80. package/dist/layouts/types/index.d.ts +0 -6
  81. package/dist/layouts/types/index.js +0 -2
  82. package/dist/layouts/types/index.js.map +0 -1
  83. package/dist/layouts/types/layout.d.ts +0 -45
  84. package/dist/layouts/types/layout.js +0 -2
  85. package/dist/layouts/types/layout.js.map +0 -1
  86. package/dist/layouts/types/notification.d.ts +0 -20
  87. package/dist/layouts/types/notification.js +0 -2
  88. package/dist/layouts/types/notification.js.map +0 -1
  89. package/dist/layouts/types/sidebar.d.ts +0 -36
  90. package/dist/layouts/types/sidebar.js +0 -2
  91. package/dist/layouts/types/sidebar.js.map +0 -1
  92. package/dist/layouts/types/theme.d.ts +0 -64
  93. package/dist/layouts/types/theme.js +0 -2
  94. package/dist/layouts/types/theme.js.map +0 -1
  95. package/dist/layouts/types/user.d.ts +0 -37
  96. package/dist/layouts/types/user.js +0 -2
  97. package/dist/layouts/types/user.js.map +0 -1
  98. package/dist/layouts/utils/dashboard.d.ts +0 -57
  99. package/dist/layouts/utils/dashboard.js +0 -44
  100. package/dist/layouts/utils/dashboard.js.map +0 -1
  101. package/dist/layouts/utils/index.d.ts +0 -1
  102. package/dist/layouts/utils/index.js +0 -44
  103. package/dist/layouts/utils/index.js.map +0 -1
  104. package/dist/onboarding/components/AppFocusStep.d.ts +0 -26
  105. package/dist/onboarding/components/AppFocusStep.js +0 -86
  106. package/dist/onboarding/components/AppFocusStep.js.map +0 -1
  107. package/dist/onboarding/components/OnboardingWizard.d.ts +0 -13
  108. package/dist/onboarding/components/OnboardingWizard.js +0 -332
  109. package/dist/onboarding/components/OnboardingWizard.js.map +0 -1
  110. package/dist/onboarding/components/PlanStep.d.ts +0 -21
  111. package/dist/onboarding/components/PlanStep.js +0 -167
  112. package/dist/onboarding/components/PlanStep.js.map +0 -1
  113. package/dist/onboarding/components/PlatformsStep.d.ts +0 -26
  114. package/dist/onboarding/components/PlatformsStep.js +0 -86
  115. package/dist/onboarding/components/PlatformsStep.js.map +0 -1
  116. package/dist/onboarding/components/UserTypeStep.d.ts +0 -30
  117. package/dist/onboarding/components/UserTypeStep.js +0 -93
  118. package/dist/onboarding/components/UserTypeStep.js.map +0 -1
  119. package/dist/onboarding/components/index.d.ts +0 -9
  120. package/dist/onboarding/components/index.js +0 -738
  121. package/dist/onboarding/components/index.js.map +0 -1
  122. package/dist/onboarding/hooks/index.d.ts +0 -4
  123. package/dist/onboarding/hooks/index.js.map +0 -1
  124. package/dist/onboarding/hooks/useOnboarding.d.ts +0 -50
  125. package/dist/onboarding/hooks/useOnboarding.js +0 -100
  126. package/dist/onboarding/hooks/useOnboarding.js.map +0 -1
  127. package/dist/onboarding/index.d.ts +0 -11
  128. package/dist/onboarding/index.js +0 -913
  129. package/dist/onboarding/index.js.map +0 -1
  130. package/dist/onboarding/types/index.d.ts +0 -3
  131. package/dist/onboarding/types/index.js +0 -2
  132. package/dist/onboarding/types/index.js.map +0 -1
  133. package/dist/onboarding/types/onboarding.d.ts +0 -209
  134. package/dist/onboarding/types/onboarding.js +0 -2
  135. package/dist/onboarding/types/onboarding.js.map +0 -1
  136. package/dist/onboarding/utils/index.d.ts +0 -4
  137. package/dist/onboarding/utils/index.js +0 -83
  138. package/dist/onboarding/utils/index.js.map +0 -1
  139. package/dist/onboarding/utils/onboarding.d.ts +0 -106
  140. package/dist/onboarding/utils/onboarding.js +0 -83
  141. package/dist/onboarding/utils/onboarding.js.map +0 -1
  142. package/dist/settings/components/SettingsLayout.d.ts +0 -19
  143. package/dist/settings/components/SettingsLayout.js +0 -170
  144. package/dist/settings/components/SettingsLayout.js.map +0 -1
  145. package/dist/settings/components/SettingsSection.d.ts +0 -24
  146. package/dist/settings/components/SettingsSection.js +0 -73
  147. package/dist/settings/components/SettingsSection.js.map +0 -1
  148. package/dist/settings/components/index.d.ts +0 -5
  149. package/dist/settings/components/index.js +0 -169
  150. package/dist/settings/components/index.js.map +0 -1
  151. package/dist/settings/hooks/index.d.ts +0 -3
  152. package/dist/settings/hooks/index.js +0 -59
  153. package/dist/settings/hooks/index.js.map +0 -1
  154. package/dist/settings/hooks/useSettings.d.ts +0 -25
  155. package/dist/settings/hooks/useSettings.js +0 -59
  156. package/dist/settings/hooks/useSettings.js.map +0 -1
  157. package/dist/settings/index.d.ts +0 -7
  158. package/dist/settings/index.js +0 -259
  159. package/dist/settings/index.js.map +0 -1
  160. package/dist/settings/types/index.d.ts +0 -2
  161. package/dist/settings/types/index.js +0 -2
  162. package/dist/settings/types/index.js.map +0 -1
  163. package/dist/settings/types/settings.d.ts +0 -79
  164. package/dist/settings/types/settings.js +0 -2
  165. package/dist/settings/types/settings.js.map +0 -1
  166. package/dist/settings/utils/index.d.ts +0 -3
  167. package/dist/settings/utils/index.js +0 -39
  168. package/dist/settings/utils/index.js.map +0 -1
  169. package/dist/settings/utils/settings.d.ts +0 -50
  170. package/dist/settings/utils/settings.js +0 -39
  171. package/dist/settings/utils/settings.js.map +0 -1
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Onboarding Types
3
+ *
4
+ * Type definitions for onboarding system
5
+ */
6
+
7
+ import type { LucideIcon } from "lucide-react";
8
+ import type { ComponentType, ReactElement, ReactNode } from "react";
9
+
10
+ /**
11
+ * User type option for first step
12
+ */
13
+ export interface UserTypeOption {
14
+ /** Unique identifier */
15
+ id: string;
16
+ /** Display label */
17
+ label: string;
18
+ /** Description text */
19
+ description: string;
20
+ /** Icon component */
21
+ icon?: LucideIcon;
22
+ /** Badge text */
23
+ badge?: string;
24
+ }
25
+
26
+ /**
27
+ * Platform connection option
28
+ */
29
+ export interface PlatformOption {
30
+ /** Unique identifier */
31
+ id: string;
32
+ /** Display name */
33
+ name: string;
34
+ /** Icon/emoji */
35
+ icon: string;
36
+ /** Color theme */
37
+ color?: string;
38
+ /** Connection status */
39
+ connected?: boolean;
40
+ }
41
+
42
+ /**
43
+ * Plan option for billing step
44
+ */
45
+ export interface PlanOption {
46
+ /** Unique identifier */
47
+ id: string;
48
+ /** Plan name */
49
+ name: string;
50
+ /** Badge text */
51
+ badge?: string;
52
+ /** Badge color class */
53
+ badgeColor?: string;
54
+ /** Description text */
55
+ description: string;
56
+ /** Monthly price */
57
+ price: number;
58
+ /** Features list */
59
+ features: (string | { text: string; bold?: boolean })[];
60
+ /** Highlight style */
61
+ highlight?: boolean;
62
+ }
63
+
64
+ /**
65
+ * Props for onboarding step components
66
+ */
67
+ export interface OnboardingStepContentProps {
68
+ /** Current onboarding state */
69
+ state: OnboardingState;
70
+ /** Update state function */
71
+ updateState: (updates: Partial<OnboardingState>) => void;
72
+ /** Go to next step */
73
+ goToNext: () => void;
74
+ /** Go to previous step */
75
+ goToPrev: () => void;
76
+ /** Go to specific step */
77
+ goToStep: (step: number) => void;
78
+ /** Onboarding configuration */
79
+ config: OnboardingConfig;
80
+ }
81
+
82
+ /**
83
+ * Onboarding step configuration
84
+ */
85
+ export interface OnboardingStep {
86
+ /** Step identifier */
87
+ id: string;
88
+ /** Step number */
89
+ order: number;
90
+ /** Step title */
91
+ title: string;
92
+ /** Step description */
93
+ description?: string;
94
+ /** Custom component - either a function component with props or a direct React node */
95
+ component?: ComponentType<OnboardingStepContentProps> | ReactElement;
96
+ /** Whether this step can be skipped */
97
+ skippable?: boolean;
98
+ /** Validation function */
99
+ validate?: (data: OnboardingState) => boolean;
100
+ }
101
+
102
+ /**
103
+ * Onboarding configuration
104
+ */
105
+ export interface OnboardingConfig {
106
+ /** Brand/application name */
107
+ brandName: string;
108
+ /** Brand tagline */
109
+ brandTagline?: string;
110
+ /** Onboarding steps */
111
+ steps: OnboardingStep[];
112
+ /** Route to navigate after completion */
113
+ completeRoute: string;
114
+ /** Route to navigate on cancel */
115
+ cancelRoute?: string;
116
+ /** Allow skipping steps */
117
+ allowSkip?: boolean;
118
+ /** Show progress indicator */
119
+ showProgress?: boolean;
120
+ /** Enable user menu in header */
121
+ showUserMenu?: boolean;
122
+ }
123
+
124
+ /**
125
+ * Onboarding state
126
+ */
127
+ export interface OnboardingState extends Record<string, unknown> {
128
+ /** Current step number */
129
+ currentStep: number;
130
+ /** Selected user type */
131
+ selectedUserType?: string;
132
+ /** Has mobile app */
133
+ hasMobileApp?: boolean;
134
+ /** Has web app */
135
+ hasWebApp?: boolean;
136
+ /** Connected platform IDs */
137
+ connectedPlatforms: string[];
138
+ /** Selected plan ID */
139
+ selectedPlan?: string;
140
+ /** Billing cycle */
141
+ billingCycle: "monthly" | "yearly";
142
+ /** Additional step data */
143
+ stepData: Record<string, unknown>;
144
+ }
145
+
146
+ /**
147
+ * Onboarding actions
148
+ */
149
+ export interface OnboardingActions {
150
+ /** Go to next step */
151
+ goToNext: () => void;
152
+ /** Go to previous step */
153
+ goToPrev: () => void;
154
+ /** Go to specific step */
155
+ goToStep: (step: number) => void;
156
+ /** Update state */
157
+ updateState: (updates: Partial<OnboardingState>) => void;
158
+ /** Complete onboarding */
159
+ complete: () => Promise<void>;
160
+ /** Cancel onboarding */
161
+ cancel: () => void;
162
+ }
163
+
164
+ /**
165
+ * Onboarding wizard props
166
+ */
167
+ export interface OnboardingWizardProps {
168
+ /** Onboarding configuration */
169
+ config: OnboardingConfig;
170
+ /** Initial state */
171
+ initialState?: Partial<OnboardingState>;
172
+ /** Completion callback */
173
+ onComplete?: (data: OnboardingState) => Promise<void>;
174
+ /** Cancel callback */
175
+ onCancel?: () => void;
176
+ }
177
+
178
+ /**
179
+ * Step progress props
180
+ */
181
+ export interface StepProgressProps {
182
+ /** Current step number */
183
+ currentStep: number;
184
+ /** Total number of steps */
185
+ totalSteps: number;
186
+ /** Completed steps */
187
+ completedSteps?: number[];
188
+ }
189
+
190
+ /**
191
+ * Step navigation props
192
+ */
193
+ export interface StepNavigationProps {
194
+ /** Current step number */
195
+ currentStep: number;
196
+ /** Total number of steps */
197
+ totalSteps: number;
198
+ /** Can proceed to next step */
199
+ canGoNext: boolean;
200
+ /** Is saving/completing */
201
+ isSaving?: boolean;
202
+ /** Next button label */
203
+ nextLabel?: string;
204
+ /** Previous button label */
205
+ prevLabel?: string;
206
+ /** On next */
207
+ onNext: () => void;
208
+ /** On previous */
209
+ onPrev: () => void;
210
+ /** Allow skipping */
211
+ allowSkip?: boolean;
212
+ /** On skip */
213
+ onSkip?: () => void;
214
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Onboarding Utils Export
3
+ */
4
+
5
+ export {
6
+ validateStep,
7
+ getStepTitle,
8
+ getStepDescription,
9
+ calculateProgress,
10
+ getCompletedSteps,
11
+ formatOnboardingData,
12
+ generateOnboardingEvent,
13
+ isValidEmail,
14
+ isValidPassword,
15
+ } from './onboarding';
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Onboarding Utilities
3
+ *
4
+ * Utility functions for onboarding operations
5
+ */
6
+
7
+ import type { OnboardingState, OnboardingConfig } from "../types/onboarding";
8
+
9
+ /**
10
+ * Validate onboarding step
11
+ *
12
+ * @param state - Current onboarding state
13
+ * @param stepNumber - Step number to validate
14
+ * @param config - Onboarding configuration
15
+ * @returns Whether the step is valid
16
+ */
17
+ export function validateStep(
18
+ state: OnboardingState,
19
+ stepNumber: number,
20
+ config: OnboardingConfig
21
+ ): boolean {
22
+ const stepConfig = config.steps[stepNumber - 1];
23
+
24
+ if (stepConfig?.validate) {
25
+ return stepConfig.validate(state);
26
+ }
27
+
28
+ // Default validations
29
+ switch (stepNumber) {
30
+ case 1:
31
+ // User type step
32
+ return !!state.selectedUserType;
33
+
34
+ case 2:
35
+ // App focus step
36
+ return !!(state.hasMobileApp || state.hasWebApp);
37
+
38
+ case 3:
39
+ // Platforms step
40
+ return state.connectedPlatforms.length > 0;
41
+
42
+ case 4:
43
+ // Plan step
44
+ return !!state.selectedPlan;
45
+
46
+ default:
47
+ return true;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Get step title
53
+ *
54
+ * @param stepNumber - Step number
55
+ * @param config - Onboarding configuration
56
+ * @returns Step title
57
+ */
58
+ export function getStepTitle(stepNumber: number, config: OnboardingConfig): string {
59
+ const stepConfig = config.steps[stepNumber - 1];
60
+ return stepConfig?.title || `Step ${stepNumber}`;
61
+ }
62
+
63
+ /**
64
+ * Get step description
65
+ *
66
+ * @param stepNumber - Step number
67
+ * @param config - Onboarding configuration
68
+ * @returns Step description or undefined
69
+ */
70
+ export function getStepDescription(stepNumber: number, config: OnboardingConfig): string | undefined {
71
+ const stepConfig = config.steps[stepNumber - 1];
72
+ return stepConfig?.description;
73
+ }
74
+
75
+ /**
76
+ * Calculate onboarding progress
77
+ *
78
+ * @param currentStep - Current step number
79
+ * @param totalSteps - Total number of steps
80
+ * @returns Progress percentage (0-100)
81
+ */
82
+ export function calculateProgress(currentStep: number, totalSteps: number): number {
83
+ return Math.min((currentStep / totalSteps) * 100, 100);
84
+ }
85
+
86
+ /**
87
+ * Get completed steps
88
+ *
89
+ * @param currentStep - Current step number
90
+ * @returns Array of completed step numbers
91
+ */
92
+ export function getCompletedSteps(currentStep: number): number[] {
93
+ return Array.from({ length: currentStep - 1 }, (_, i) => i + 1);
94
+ }
95
+
96
+ /**
97
+ * Format onboarding data for API submission
98
+ *
99
+ * @param state - Onboarding state
100
+ * @param userId - User ID
101
+ * @returns Formatted data object
102
+ */
103
+ export function formatOnboardingData(state: OnboardingState, userId?: string) {
104
+ return {
105
+ userId,
106
+ userType: state.selectedUserType,
107
+ hasMobileApp: state.hasMobileApp,
108
+ hasWebApp: state.hasWebApp,
109
+ connectedPlatforms: state.connectedPlatforms,
110
+ selectedPlan: state.selectedPlan,
111
+ billingCycle: state.billingCycle,
112
+ stepData: state.stepData,
113
+ completedAt: new Date().toISOString(),
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Generate onboarding analytics event
119
+ *
120
+ * @param event - Event name
121
+ * @param state - Onboarding state
122
+ * @param additionalData - Additional event data
123
+ * @returns Analytics event object
124
+ */
125
+ export function generateOnboardingEvent(
126
+ event: string,
127
+ state: OnboardingState,
128
+ additionalData?: Record<string, unknown>
129
+ ) {
130
+ return {
131
+ event,
132
+ properties: {
133
+ currentStep: state.currentStep,
134
+ userType: state.selectedUserType,
135
+ hasMobileApp: state.hasMobileApp,
136
+ hasWebApp: state.hasWebApp,
137
+ platformCount: state.connectedPlatforms.length,
138
+ selectedPlan: state.selectedPlan,
139
+ billingCycle: state.billingCycle,
140
+ ...additionalData,
141
+ },
142
+ timestamp: new Date().toISOString(),
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Validate email format
148
+ *
149
+ * @param email - Email address
150
+ * @returns Whether email is valid
151
+ */
152
+ export function isValidEmail(email: string): boolean {
153
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
154
+ return emailRegex.test(email);
155
+ }
156
+
157
+ /**
158
+ * Validate password strength
159
+ *
160
+ * @param password - Password
161
+ * @param minLength - Minimum length (default: 8)
162
+ * @returns Whether password meets requirements
163
+ */
164
+ export function isValidPassword(password: string, minLength: number = 8): boolean {
165
+ return password.length >= minLength;
166
+ }
@@ -0,0 +1,144 @@
1
+ import { useState } from "react";
2
+ import { Outlet, useLocation, useNavigate } from "react-router-dom";
3
+ import { SettingsSection } from "./SettingsSection";
4
+ import type { SettingsConfig } from "../types/settings";
5
+ import { Skeleton } from "@umituz/web-design-system/atoms";
6
+ import { ChevronLeft, Menu } from "lucide-react";
7
+ import { Button } from "@umituz/web-design-system/atoms";
8
+
9
+ interface SettingsLayoutProps {
10
+ /** Settings configuration */
11
+ config: SettingsConfig;
12
+ }
13
+
14
+ /**
15
+ * Settings Layout Component
16
+ *
17
+ * Main layout wrapper for settings pages.
18
+ * Provides sidebar navigation and content area.
19
+ *
20
+ * @param props - Settings layout props
21
+ */
22
+ export const SettingsLayout = ({
23
+ config,
24
+ }: SettingsLayoutProps) => {
25
+ const location = useLocation();
26
+ const navigate = useNavigate();
27
+ const [collapsed, setCollapsed] = useState(false);
28
+ const [mobileOpen, setMobileOpen] = useState(false);
29
+ const [loading, setLoading] = useState(true);
30
+
31
+ // Simulate loading on route change
32
+ useState(() => {
33
+ setLoading(true);
34
+ const timer = setTimeout(() => setLoading(false), 200);
35
+ return () => clearTimeout(timer);
36
+ });
37
+
38
+ const handleNavigate = (path: string) => {
39
+ navigate(path);
40
+ setMobileOpen(false);
41
+ };
42
+
43
+ return (
44
+ <div className="flex h-screen w-full bg-background font-sans">
45
+ {/* Desktop Sidebar */}
46
+ <aside
47
+ className={`hidden md:flex flex-col shrink-0 border-r border-border bg-card transition-all duration-300 ${
48
+ collapsed ? "w-16" : "w-64"
49
+ }`}
50
+ >
51
+ <div className="flex h-14 items-center justify-between border-b border-border px-4">
52
+ {!collapsed && (
53
+ <h2 className="text-lg font-semibold text-foreground">
54
+ {config.brandName || "Settings"}
55
+ </h2>
56
+ )}
57
+ <Button
58
+ variant="ghost"
59
+ size="icon"
60
+ onClick={() => setCollapsed(!collapsed)}
61
+ className="ml-auto"
62
+ >
63
+ {collapsed ? <Menu className="h-4 w-4" /> : <ChevronLeft className="h-4 w-4" />}
64
+ </Button>
65
+ </div>
66
+
67
+ <nav className="flex-1 overflow-y-auto p-2">
68
+ {config.sections.map((section) => (
69
+ <SettingsSection
70
+ key={section.key}
71
+ section={section}
72
+ currentPath={location.pathname}
73
+ onNavigate={handleNavigate}
74
+ collapsed={collapsed}
75
+ />
76
+ ))}
77
+ </nav>
78
+ </aside>
79
+
80
+ {/* Mobile Menu Overlay */}
81
+ {mobileOpen && (
82
+ <div className="fixed inset-0 z-50 md:hidden">
83
+ <div
84
+ className="absolute inset-0 bg-background/80 backdrop-blur-sm"
85
+ onClick={() => setMobileOpen(false)}
86
+ />
87
+ <aside className="absolute left-0 top-0 h-full w-64 border-r border-border bg-card shadow-xl">
88
+ <div className="flex h-14 items-center justify-between border-b border-border px-4">
89
+ <h2 className="text-lg font-semibold text-foreground">
90
+ {config.brandName || "Settings"}
91
+ </h2>
92
+ <Button variant="ghost" size="icon" onClick={() => setMobileOpen(false)}>
93
+ <Menu className="h-4 w-4" />
94
+ </Button>
95
+ </div>
96
+
97
+ <nav className="flex-1 overflow-y-auto p-2">
98
+ {config.sections.map((section) => (
99
+ <SettingsSection
100
+ key={section.key}
101
+ section={section}
102
+ currentPath={location.pathname}
103
+ onNavigate={handleNavigate}
104
+ />
105
+ ))}
106
+ </nav>
107
+ </aside>
108
+ </div>
109
+ )}
110
+
111
+ {/* Main Content Area */}
112
+ <div className="flex flex-1 flex-col overflow-hidden min-w-0">
113
+ {/* Mobile Header */}
114
+ <header className="flex h-14 items-center justify-between border-b border-border bg-card/50 backdrop-blur-md px-4 shrink-0 md:hidden">
115
+ <div className="flex items-center gap-3">
116
+ <Button variant="ghost" size="icon" onClick={() => setMobileOpen(true)}>
117
+ <Menu className="h-5 w-5" />
118
+ </Button>
119
+ <h2 className="text-sm font-semibold text-foreground">
120
+ {config.brandName || "Settings"}
121
+ </h2>
122
+ </div>
123
+ </header>
124
+
125
+ <main className="flex-1 overflow-y-auto p-4 md:p-8">
126
+ {loading ? (
127
+ <div className="mx-auto w-full max-w-4xl space-y-6">
128
+ <Skeleton className="h-8 w-1/3 rounded-xl" />
129
+ <div className="grid gap-4">
130
+ {Array.from({ length: 3 }).map((_, i) => (
131
+ <Skeleton key={i} className="h-24 rounded-xl" />
132
+ ))}
133
+ </div>
134
+ </div>
135
+ ) : (
136
+ <Outlet />
137
+ )}
138
+ </main>
139
+ </div>
140
+ </div>
141
+ );
142
+ };
143
+
144
+ export default SettingsLayout;
@@ -0,0 +1,106 @@
1
+ import { Link } from "react-router-dom";
2
+ import { ChevronRight } from "lucide-react";
3
+ import { cn } from "@umituz/web-design-system/utils";
4
+ import type { SettingsSection as SettingsSectionType } from "../types/settings";
5
+
6
+ interface SettingsSectionProps {
7
+ /** Section configuration */
8
+ section: SettingsSectionType;
9
+ /** Current path */
10
+ currentPath?: string;
11
+ /** On navigate callback */
12
+ onNavigate?: (path: string) => void;
13
+ /** Collapsed state */
14
+ collapsed?: boolean;
15
+ }
16
+
17
+ /**
18
+ * Settings Section Component
19
+ *
20
+ * Displays a section of settings items.
21
+ *
22
+ * @param props - Settings section props
23
+ */
24
+ export const SettingsSection = ({
25
+ section,
26
+ currentPath,
27
+ onNavigate,
28
+ collapsed = false,
29
+ }: SettingsSectionProps) => {
30
+ const filteredItems = section.items.filter((item) => item.enabled !== false);
31
+
32
+ if (filteredItems.length === 0) return null;
33
+
34
+ return (
35
+ <div className="mb-6 last:mb-0">
36
+ {!collapsed && (
37
+ <h3 className="px-2 mb-2 text-xs font-bold uppercase tracking-wider text-muted-foreground">
38
+ {section.title}
39
+ </h3>
40
+ )}
41
+
42
+ <div className="space-y-1">
43
+ {filteredItems.map((item) => {
44
+ const isActive = currentPath === item.path;
45
+
46
+ const itemContent = (
47
+ <>
48
+ {item.icon && (
49
+ <item.icon
50
+ className={cn(
51
+ "h-4 w-4 shrink-0",
52
+ isActive && "scale-110"
53
+ )}
54
+ />
55
+ )}
56
+ {!collapsed && (
57
+ <>
58
+ <span className="flex-1 text-left">{item.label}</span>
59
+ {item.badge !== undefined && item.badge > 0 && (
60
+ <span className="ml-auto flex h-5 min-w-5 items-center justify-center rounded-full bg-destructive px-1.5 text-[10px] font-bold text-destructive-foreground">
61
+ {item.badge > 99 ? "99+" : item.badge}
62
+ </span>
63
+ )}
64
+ {item.path && <ChevronRight className="h-4 w-4 text-muted-foreground" />}
65
+ </>
66
+ )}
67
+ </>
68
+ );
69
+
70
+ const itemClassName = cn(
71
+ "flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-all duration-200 w-full",
72
+ "hover:bg-accent hover:text-accent-foreground",
73
+ isActive && "bg-accent text-accent-foreground",
74
+ collapsed ? "justify-center" : "justify-start"
75
+ );
76
+
77
+ if (item.path) {
78
+ return (
79
+ <Link
80
+ key={item.key}
81
+ to={item.path}
82
+ onClick={() => onNavigate?.(item.path!)}
83
+ className={itemClassName}
84
+ title={collapsed ? item.label : undefined}
85
+ >
86
+ {itemContent}
87
+ </Link>
88
+ );
89
+ }
90
+
91
+ return (
92
+ <button
93
+ key={item.key}
94
+ type="button"
95
+ onClick={() => {}}
96
+ className={itemClassName}
97
+ title={collapsed ? item.label : undefined}
98
+ >
99
+ {itemContent}
100
+ </button>
101
+ );
102
+ })}
103
+ </div>
104
+ </div>
105
+ );
106
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Settings Components Export
3
+ */
4
+
5
+ export { SettingsLayout } from './SettingsLayout';
6
+ export { SettingsSection } from './SettingsSection';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Settings Hooks Export
3
+ */
4
+
5
+ export {
6
+ useSettings,
7
+ } from './useSettings';