@umituz/web-dashboard 3.2.4 → 4.1.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 (126) hide show
  1. package/README.md +228 -14
  2. package/package.json +23 -30
  3. package/src/domains/analytics/components/AnalyticsCard.tsx +43 -42
  4. package/src/domains/analytics/components/AnalyticsChart.tsx +345 -149
  5. package/src/domains/analytics/components/AnalyticsLayout.tsx +93 -112
  6. package/src/domains/analytics/components/MetricCard.tsx +29 -87
  7. package/src/domains/analytics/hooks/index.ts +7 -1
  8. package/src/domains/analytics/hooks/useAnalytics.ts +144 -139
  9. package/src/domains/analytics/index.ts +11 -0
  10. package/src/domains/analytics/services/AnalyticsEngineService.ts +103 -55
  11. package/src/domains/analytics/services/PerformanceService.ts +117 -69
  12. package/src/domains/analytics/types/analytics.ts +20 -6
  13. package/src/domains/analytics/types/index.ts +3 -0
  14. package/src/domains/analytics/utils/analytics.test.ts +183 -0
  15. package/src/domains/analytics/utils/analytics.ts +26 -9
  16. package/src/domains/analytics/utils/i18nKeys.ts +40 -0
  17. package/src/domains/analytics/utils/index.ts +2 -0
  18. package/src/domains/auth/components/AuthLayout.tsx +63 -54
  19. package/src/domains/auth/components/ForgotPasswordForm.tsx +96 -98
  20. package/src/domains/auth/components/LoginForm.tsx +167 -226
  21. package/src/domains/auth/components/RegisterForm.tsx +202 -272
  22. package/src/domains/auth/components/ResetPasswordForm.tsx +105 -151
  23. package/src/domains/auth/components/shared/AuthErrorBanner.tsx +35 -0
  24. package/src/domains/auth/components/shared/EmailInput.tsx +91 -0
  25. package/src/domains/auth/components/shared/PasswordInput.tsx +119 -0
  26. package/src/domains/auth/components/shared/PasswordStrengthIndicator.tsx +75 -0
  27. package/src/domains/auth/components/shared/SocialLoginButtons.tsx +58 -0
  28. package/src/domains/auth/components/shared/SocialProviderIcons.tsx +48 -0
  29. package/src/domains/auth/components/shared/index.ts +20 -0
  30. package/src/domains/auth/hooks/index.ts +1 -0
  31. package/src/domains/auth/hooks/useAuth.ts +151 -232
  32. package/src/domains/auth/index.ts +8 -4
  33. package/src/domains/auth/types/auth.ts +26 -5
  34. package/src/domains/auth/types/index.ts +1 -0
  35. package/src/domains/auth/utils/auth.test.ts +182 -0
  36. package/src/domains/auth/utils/auth.ts +71 -130
  37. package/src/domains/auth/utils/i18nKeys.ts +127 -0
  38. package/src/domains/auth/utils/index.ts +7 -1
  39. package/src/domains/auth/utils/secureToken.ts +33 -0
  40. package/src/domains/auth/utils/validationKeys.ts +23 -0
  41. package/src/domains/calendar/hooks/useCalendar.ts +145 -151
  42. package/src/domains/calendar/index.ts +0 -1
  43. package/src/domains/calendar/services/CalendarService.ts +122 -179
  44. package/src/domains/calendar/utils/contentItemMapper.test.ts +108 -0
  45. package/src/domains/calendar/utils/contentItemMapper.ts +99 -0
  46. package/src/domains/calendar/utils/index.ts +33 -30
  47. package/src/domains/layouts/components/BrandLogo.tsx +12 -9
  48. package/src/domains/layouts/components/DashboardHeader.tsx +78 -80
  49. package/src/domains/layouts/components/DashboardLayout.tsx +45 -21
  50. package/src/domains/layouts/components/DashboardSidebar.tsx +48 -51
  51. package/src/domains/layouts/types/layout.ts +7 -1
  52. package/src/domains/layouts/types/user.ts +3 -1
  53. package/src/domains/modules/build.test.tsx +97 -0
  54. package/src/domains/modules/build.tsx +71 -0
  55. package/src/domains/modules/index.ts +2 -0
  56. package/src/domains/modules/types.ts +29 -0
  57. package/src/domains/onboarding/components/AppFocusStep.tsx +99 -68
  58. package/src/domains/onboarding/components/OnboardingWizard.tsx +109 -183
  59. package/src/domains/onboarding/components/PlanStep.tsx +156 -123
  60. package/src/domains/onboarding/components/PlatformsStep.tsx +74 -54
  61. package/src/domains/onboarding/components/StepNavigation.tsx +108 -0
  62. package/src/domains/onboarding/components/StepProgress.tsx +62 -0
  63. package/src/domains/onboarding/components/UserTypeStep.tsx +59 -80
  64. package/src/domains/onboarding/components/index.ts +4 -0
  65. package/src/domains/onboarding/hooks/useOnboarding.ts +76 -66
  66. package/src/domains/onboarding/index.ts +1 -0
  67. package/src/domains/onboarding/types/onboarding.ts +57 -17
  68. package/src/domains/onboarding/utils/i18nKeys.ts +72 -0
  69. package/src/domains/onboarding/utils/index.ts +2 -0
  70. package/src/domains/permissions/PermissionsContext.tsx +103 -0
  71. package/src/domains/permissions/hasPermission.test.ts +50 -0
  72. package/src/domains/permissions/hasPermission.ts +38 -0
  73. package/src/domains/permissions/index.ts +19 -0
  74. package/src/domains/permissions/types.ts +35 -0
  75. package/src/domains/permissions/utils/i18nKeys.ts +11 -0
  76. package/src/domains/session/DemoLogin.tsx +50 -0
  77. package/src/domains/session/OrgSwitcher.tsx +110 -0
  78. package/src/domains/session/SessionContext.tsx +150 -0
  79. package/src/domains/session/inMemoryAuthAdapter.ts +53 -0
  80. package/src/domains/session/index.ts +18 -0
  81. package/src/domains/session/types.ts +47 -0
  82. package/src/domains/session/utils/i18nKeys.ts +12 -0
  83. package/src/domains/settings/components/AuditLogList.tsx +59 -0
  84. package/src/domains/settings/components/PermissionMatrixScreen.tsx +80 -0
  85. package/src/domains/settings/components/SettingsLayout.tsx +75 -60
  86. package/src/domains/settings/components/SettingsSection.tsx +35 -22
  87. package/src/domains/settings/components/UserManagementTable.tsx +69 -0
  88. package/src/domains/settings/components/index.ts +10 -0
  89. package/src/domains/settings/hooks/useSettings.ts +77 -52
  90. package/src/domains/settings/types/settings.ts +2 -0
  91. package/src/domains/settings/utils/i18nKeys.ts +18 -0
  92. package/src/domains/settings/utils/index.ts +1 -0
  93. package/src/domains/settings/utils/settings.ts +11 -4
  94. package/src/i18n/I18nContext.tsx +33 -0
  95. package/src/i18n/defaultT.test.ts +43 -0
  96. package/src/i18n/defaultT.ts +34 -0
  97. package/src/i18n/index.ts +7 -0
  98. package/src/i18n/types.ts +13 -0
  99. package/src/index.ts +1 -7
  100. package/src/primitives/Button.tsx +24 -0
  101. package/src/primitives/Checkbox.tsx +17 -0
  102. package/src/primitives/Input.tsx +23 -0
  103. package/src/primitives/Skeleton.tsx +6 -0
  104. package/src/primitives/cn.ts +10 -0
  105. package/src/primitives/index.ts +23 -0
  106. package/src/styles/dashboard.css +2731 -0
  107. package/src/styles/tokens.contract.test.ts +91 -0
  108. package/src/styles/tokens.css +116 -0
  109. package/src/domains/billing/components/BillingLayout.tsx +0 -63
  110. package/src/domains/billing/components/BillingPage.tsx +0 -49
  111. package/src/domains/billing/components/BillingPortal.tsx +0 -198
  112. package/src/domains/billing/components/InvoiceCard.tsx +0 -143
  113. package/src/domains/billing/components/PaymentMethodsList.tsx +0 -116
  114. package/src/domains/billing/components/PlanComparison.tsx +0 -199
  115. package/src/domains/billing/components/UsageCard.tsx +0 -103
  116. package/src/domains/billing/components/index.ts +0 -13
  117. package/src/domains/billing/constants/billing.ts +0 -69
  118. package/src/domains/billing/hooks/index.ts +0 -7
  119. package/src/domains/billing/hooks/useBilling.ts +0 -385
  120. package/src/domains/billing/index.ts +0 -73
  121. package/src/domains/billing/types/billing.ts +0 -342
  122. package/src/domains/billing/types/index.ts +0 -30
  123. package/src/domains/billing/utils/billing.ts +0 -360
  124. package/src/domains/billing/utils/index.ts +0 -31
  125. package/src/domains/billing/utils/polar.ts +0 -63
  126. package/src/domains/calendar/components/index.ts +0 -7
@@ -1,123 +1,26 @@
1
- import { useState, useCallback } from "react";
1
+ /**
2
+ * Onboarding Wizard
3
+ *
4
+ * Multi-step onboarding container. Delegates state, navigation, and
5
+ * progress to `useOnboarding` so there's a single source of truth
6
+ * for the wizard's lifecycle. Styling is entirely `udb-onboarding*`
7
+ * classes from the package stylesheet.
8
+ */
9
+
10
+ import { useCallback, useMemo, useState } from "react";
2
11
  import { useNavigate } from "react-router-dom";
3
- import { Check, Loader2 } from "lucide-react";
4
- import { cn } from "@umituz/web-design-system/utils";
5
- import { Button } from "@umituz/web-design-system/atoms";
6
- import { BrandLogo } from "../../layouts/components";
12
+ import { useT } from "../../../i18n";
13
+ import { Button } from "../../../primitives";
14
+ import { useOnboarding } from "../hooks/useOnboarding";
15
+ import { StepProgress } from "./StepProgress";
16
+ import { StepNavigation } from "./StepNavigation";
7
17
  import type {
8
- OnboardingConfig,
9
- OnboardingState,
10
18
  OnboardingWizardProps,
11
- StepProgressProps,
12
- StepNavigationProps,
19
+ OnboardingError,
13
20
  } from "../types/onboarding";
14
21
 
15
- /**
16
- * Step Progress Component
17
- */
18
- const StepProgress = ({ currentStep, totalSteps, completedSteps = [] }: StepProgressProps) => {
19
- return (
20
- <div className="flex items-center gap-0 flex-1 justify-center">
21
- {Array.from({ length: totalSteps }, (_, i) => i + 1).map((step) => {
22
- const isCompleted = completedSteps.includes(step) || step < currentStep;
23
- const isCurrent = step === currentStep;
24
-
25
- return (
26
- <div key={step} className="flex items-center">
27
- <div
28
- className={cn(
29
- "w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold transition-colors",
30
- isCompleted || isCurrent
31
- ? "bg-primary text-primary-foreground"
32
- : "bg-muted text-muted-foreground"
33
- )}
34
- >
35
- {isCompleted ? <Check className="h-4 w-4" /> : step}
36
- </div>
37
- {step < totalSteps && (
38
- <div
39
- className={cn(
40
- "w-12 h-0.5 mx-1",
41
- step < currentStep ? "bg-primary" : "bg-border"
42
- )}
43
- />
44
- )}
45
- </div>
46
- );
47
- })}
48
- </div>
49
- );
50
- };
51
-
52
- /**
53
- * Step Navigation Component
54
- */
55
- const StepNavigation = ({
56
- currentStep,
57
- totalSteps,
58
- canGoNext,
59
- isSaving = false,
60
- nextLabel,
61
- prevLabel,
62
- onNext,
63
- onPrev,
64
- allowSkip = false,
65
- onSkip,
66
- }: StepNavigationProps) => {
67
- const t = (key: string) => key; // Simple i18n fallback
68
-
69
- return (
70
- <footer className="bg-background border-t border-border px-8 py-6 flex items-center justify-between">
71
- {currentStep > 1 ? (
72
- <Button
73
- variant="ghost"
74
- onClick={onPrev}
75
- className="rounded-full px-6"
76
- disabled={isSaving}
77
- >
78
- ← {prevLabel || t("onboarding.buttons.back")}
79
- </Button>
80
- ) : (
81
- <div />
82
- )}
83
-
84
- <div className="flex items-center gap-3">
85
- {allowSkip && onSkip && currentStep < totalSteps && (
86
- <Button
87
- variant="ghost"
88
- onClick={onSkip}
89
- className="rounded-full px-6"
90
- disabled={isSaving}
91
- >
92
- {t("onboarding.buttons.skip")}
93
- </Button>
94
- )}
95
-
96
- <Button
97
- onClick={onNext}
98
- className="rounded-full px-12 h-12 text-base font-bold"
99
- disabled={!canGoNext || isSaving}
100
- >
101
- {isSaving ? (
102
- <>
103
- <Loader2 className="h-4 w-4 mr-2 animate-spin" />
104
- {t("onboarding.buttons.finalizing")}
105
- </>
106
- ) : currentStep === totalSteps ? (
107
- nextLabel || t("onboarding.buttons.getStarted")
108
- ) : (
109
- t("onboarding.buttons.next")
110
- )}
111
- </Button>
112
- </div>
113
- </footer>
114
- );
115
- };
116
-
117
22
  /**
118
23
  * Onboarding Wizard Component
119
- *
120
- * Main onboarding container with step management
121
24
  */
122
25
  export const OnboardingWizard = ({
123
26
  config,
@@ -126,89 +29,90 @@ export const OnboardingWizard = ({
126
29
  onCancel,
127
30
  }: OnboardingWizardProps) => {
128
31
  const navigate = useNavigate();
129
- const [currentStep, setCurrentStep] = useState(1);
32
+ const t = useT();
130
33
  const [saving, setSaving] = useState(false);
131
- const [completedSteps, setCompletedSteps] = useState<number[]>([]);
132
-
133
- // State management
134
- const [state, setState] = useState<OnboardingState>({
135
- currentStep: 1,
136
- connectedPlatforms: [],
137
- billingCycle: "monthly",
138
- stepData: {},
139
- ...initialState,
140
- });
141
-
142
- const totalSteps = config.steps.length;
34
+ const [error, setError] = useState<OnboardingError | null>(null);
35
+
36
+ const {
37
+ state,
38
+ currentStep,
39
+ totalSteps,
40
+ isLastStep,
41
+ goToNext,
42
+ goToPrev,
43
+ goToStep,
44
+ updateState,
45
+ } = useOnboarding(config, initialState);
46
+
47
+ const completedSteps = useMemo(() => {
48
+ if (currentStep <= 1) return [] as number[];
49
+ return Array.from({ length: currentStep - 1 }, (_, i) => i + 1);
50
+ }, [currentStep]);
143
51
 
144
- // Validate current step
145
- const canGoNext = useCallback(() => {
52
+ const validateCurrentStep = useCallback((): boolean => {
146
53
  const currentStepConfig = config.steps[currentStep - 1];
147
- if (currentStepConfig?.validate) {
148
- return currentStepConfig.validate(state);
149
- }
150
- return true;
54
+ return currentStepConfig?.validate ? currentStepConfig.validate(state) : true;
151
55
  }, [config.steps, currentStep, state]);
152
56
 
153
- // Navigation handlers
154
- const goToNext = useCallback(async () => {
155
- if (currentStep < totalSteps) {
156
- setCompletedSteps((prev) => [...prev, currentStep]);
157
- setCurrentStep((prev) => prev + 1);
158
- setState((prev) => ({ ...prev, currentStep: prev.currentStep + 1 }));
159
- } else {
160
- // Complete onboarding
57
+ const handleNext = useCallback(async () => {
58
+ if (!validateCurrentStep()) {
59
+ return;
60
+ }
61
+
62
+ if (isLastStep) {
161
63
  setSaving(true);
64
+ setError(null);
162
65
  try {
163
66
  await onComplete?.(state);
164
67
  navigate(config.completeRoute);
165
- } catch (error) {
166
- console.error("Onboarding completion error:", error);
68
+ } catch (err) {
69
+ // Surface the failure to the user — silently logging is
70
+ // forbidden on critical paths.
71
+ setError({
72
+ step: currentStep,
73
+ message: err instanceof Error ? err.message : 'onboarding.errors.completionFailed',
74
+ });
75
+ } finally {
167
76
  setSaving(false);
168
77
  }
78
+ return;
169
79
  }
170
- }, [currentStep, totalSteps, state, onComplete, navigate, config.completeRoute]);
171
-
172
- const goToPrev = useCallback(() => {
173
- if (currentStep > 1) {
174
- setCurrentStep((prev) => prev - 1);
175
- setState((prev) => ({ ...prev, currentStep: prev.currentStep - 1 }));
176
- }
177
- }, [currentStep]);
178
80
 
179
- const goToStep = useCallback((step: number) => {
180
- if (step >= 1 && step <= totalSteps) {
181
- setCurrentStep(step);
182
- setState((prev) => ({ ...prev, currentStep: step }));
183
- }
184
- }, [totalSteps]);
185
-
186
- const updateState = useCallback((updates: Partial<OnboardingState>) => {
187
- setState((prev) => ({ ...prev, ...updates }));
188
- }, []);
81
+ goToNext();
82
+ }, [
83
+ config.completeRoute,
84
+ currentStep,
85
+ goToNext,
86
+ isLastStep,
87
+ navigate,
88
+ onComplete,
89
+ state,
90
+ validateCurrentStep,
91
+ ]);
92
+
93
+ const handlePrev = useCallback(() => {
94
+ goToPrev();
95
+ }, [goToPrev]);
189
96
 
190
97
  const handleCancel = useCallback(() => {
191
98
  onCancel?.();
192
- navigate(config.cancelRoute || "/");
193
- }, [onCancel, navigate, config.cancelRoute]);
99
+ navigate(config.cancelRoute ?? "/");
100
+ }, [config.cancelRoute, navigate, onCancel]);
194
101
 
195
102
  const handleSkip = useCallback(() => {
196
- if (currentStep < totalSteps) {
197
- goToNext();
103
+ if (!isLastStep) {
104
+ handleNext();
198
105
  }
199
- }, [currentStep, totalSteps, goToNext]);
106
+ }, [handleNext, isLastStep]);
200
107
 
201
- // Render current step content
202
108
  const currentStepConfig = config.steps[currentStep - 1];
203
109
  const StepContent = currentStepConfig?.component;
204
110
 
205
111
  return (
206
- <div className="min-h-screen bg-secondary flex flex-col">
207
- {/* Header */}
208
- <header className="bg-background border-b border-border px-6 py-3 flex items-center">
209
- <div className="flex items-center gap-2 mr-8">
210
- <BrandLogo size={28} />
211
- <span className="font-bold text-foreground">{config.brandName}</span>
112
+ <div className="udb-root udb-onboarding">
113
+ <header className="udb-onboarding__header">
114
+ <div className="udb-onboarding__brand">
115
+ <span className="udb-onboarding__brand-name">{config.brandName}</span>
212
116
  </div>
213
117
 
214
118
  {config.showProgress !== false && (
@@ -219,14 +123,33 @@ export const OnboardingWizard = ({
219
123
  />
220
124
  )}
221
125
 
222
- <div className="w-24" />
126
+ <div className="udb-onboarding__header-actions">
127
+ {config.allowCancel !== false && (
128
+ <Button
129
+ variant="ghost"
130
+ size="sm"
131
+ onClick={handleCancel}
132
+ disabled={saving}
133
+ >
134
+ {t('onboarding.buttons.cancel')}
135
+ </Button>
136
+ )}
137
+ </div>
223
138
  </header>
224
139
 
225
- {/* Main Content */}
226
- <main className="flex-1 flex flex-col items-center justify-center px-4 py-12">
227
- <div className="w-full max-w-4xl">
140
+ <main className="udb-onboarding__main">
141
+ <div className="udb-onboarding__content">
142
+ {error && (
143
+ <div
144
+ role="alert"
145
+ aria-live="polite"
146
+ className="udb-onboarding__error"
147
+ >
148
+ {t(error.message)}
149
+ </div>
150
+ )}
151
+
228
152
  {StepContent ? (
229
- // Check if it's a function component (has call signature)
230
153
  typeof StepContent === 'function' ? (
231
154
  <StepContent
232
155
  state={state}
@@ -237,23 +160,26 @@ export const OnboardingWizard = ({
237
160
  config={config}
238
161
  />
239
162
  ) : (
240
- // Otherwise it's a ReactElement
241
163
  StepContent
242
164
  )
243
- ) : null}
165
+ ) : (
166
+ <div className="udb-onboarding__empty">
167
+ {t('onboarding.errors.stepNotConfigured')}
168
+ </div>
169
+ )}
244
170
  </div>
245
171
  </main>
246
172
 
247
- {/* Footer Navigation */}
248
173
  <StepNavigation
249
174
  currentStep={currentStep}
250
175
  totalSteps={totalSteps}
251
- canGoNext={canGoNext()}
176
+ canGoNext={validateCurrentStep()}
252
177
  isSaving={saving}
253
- onNext={goToNext}
254
- onPrev={goToPrev}
178
+ onNext={handleNext}
179
+ onPrev={handlePrev}
255
180
  allowSkip={config.allowSkip}
256
181
  onSkip={handleSkip}
182
+ translate={t}
257
183
  />
258
184
  </div>
259
185
  );