@umituz/web-dashboard 3.2.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +202 -14
- package/package.json +21 -29
- package/src/domains/analytics/components/AnalyticsCard.tsx +23 -21
- package/src/domains/analytics/components/AnalyticsChart.tsx +345 -149
- package/src/domains/analytics/components/AnalyticsLayout.tsx +93 -112
- package/src/domains/analytics/components/MetricCard.tsx +42 -64
- package/src/domains/analytics/hooks/index.ts +7 -1
- package/src/domains/analytics/hooks/useAnalytics.ts +144 -139
- package/src/domains/analytics/index.ts +11 -0
- package/src/domains/analytics/services/AnalyticsEngineService.ts +103 -55
- package/src/domains/analytics/services/PerformanceService.ts +117 -69
- package/src/domains/analytics/types/analytics.ts +20 -6
- package/src/domains/analytics/types/index.ts +3 -0
- package/src/domains/analytics/utils/analytics.test.ts +183 -0
- package/src/domains/analytics/utils/analytics.ts +26 -9
- package/src/domains/analytics/utils/i18nKeys.ts +40 -0
- package/src/domains/analytics/utils/index.ts +2 -0
- package/src/domains/auth/components/AuthLayout.tsx +63 -54
- package/src/domains/auth/components/ForgotPasswordForm.tsx +96 -98
- package/src/domains/auth/components/LoginForm.tsx +167 -226
- package/src/domains/auth/components/RegisterForm.tsx +202 -272
- package/src/domains/auth/components/ResetPasswordForm.tsx +105 -151
- package/src/domains/auth/components/shared/AuthErrorBanner.tsx +35 -0
- package/src/domains/auth/components/shared/EmailInput.tsx +91 -0
- package/src/domains/auth/components/shared/PasswordInput.tsx +119 -0
- package/src/domains/auth/components/shared/PasswordStrengthIndicator.tsx +75 -0
- package/src/domains/auth/components/shared/SocialLoginButtons.tsx +58 -0
- package/src/domains/auth/components/shared/SocialProviderIcons.tsx +48 -0
- package/src/domains/auth/components/shared/index.ts +20 -0
- package/src/domains/auth/hooks/index.ts +1 -0
- package/src/domains/auth/hooks/useAuth.ts +151 -232
- package/src/domains/auth/index.ts +8 -4
- package/src/domains/auth/types/auth.ts +26 -5
- package/src/domains/auth/types/index.ts +1 -0
- package/src/domains/auth/utils/auth.test.ts +182 -0
- package/src/domains/auth/utils/auth.ts +71 -130
- package/src/domains/auth/utils/i18nKeys.ts +127 -0
- package/src/domains/auth/utils/index.ts +7 -1
- package/src/domains/auth/utils/secureToken.ts +33 -0
- package/src/domains/auth/utils/validationKeys.ts +23 -0
- package/src/domains/calendar/hooks/useCalendar.ts +145 -151
- package/src/domains/calendar/index.ts +0 -1
- package/src/domains/calendar/services/CalendarService.ts +122 -179
- package/src/domains/calendar/utils/contentItemMapper.test.ts +108 -0
- package/src/domains/calendar/utils/contentItemMapper.ts +99 -0
- package/src/domains/calendar/utils/index.ts +33 -30
- package/src/domains/layouts/components/BrandLogo.tsx +12 -9
- package/src/domains/layouts/components/DashboardHeader.tsx +78 -80
- package/src/domains/layouts/components/DashboardLayout.tsx +45 -21
- package/src/domains/layouts/components/DashboardSidebar.tsx +48 -51
- package/src/domains/layouts/types/layout.ts +7 -1
- package/src/domains/layouts/types/user.ts +3 -1
- package/src/domains/modules/build.test.tsx +97 -0
- package/src/domains/modules/build.tsx +71 -0
- package/src/domains/modules/index.ts +2 -0
- package/src/domains/modules/types.ts +29 -0
- package/src/domains/onboarding/components/AppFocusStep.tsx +99 -68
- package/src/domains/onboarding/components/OnboardingWizard.tsx +109 -183
- package/src/domains/onboarding/components/PlanStep.tsx +156 -123
- package/src/domains/onboarding/components/PlatformsStep.tsx +74 -54
- package/src/domains/onboarding/components/StepNavigation.tsx +108 -0
- package/src/domains/onboarding/components/StepProgress.tsx +62 -0
- package/src/domains/onboarding/components/UserTypeStep.tsx +59 -80
- package/src/domains/onboarding/components/index.ts +4 -0
- package/src/domains/onboarding/hooks/useOnboarding.ts +76 -66
- package/src/domains/onboarding/index.ts +1 -0
- package/src/domains/onboarding/types/onboarding.ts +57 -17
- package/src/domains/onboarding/utils/i18nKeys.ts +72 -0
- package/src/domains/onboarding/utils/index.ts +2 -0
- package/src/domains/permissions/PermissionsContext.tsx +103 -0
- package/src/domains/permissions/hasPermission.test.ts +50 -0
- package/src/domains/permissions/hasPermission.ts +38 -0
- package/src/domains/permissions/index.ts +19 -0
- package/src/domains/permissions/types.ts +35 -0
- package/src/domains/permissions/utils/i18nKeys.ts +11 -0
- package/src/domains/session/DemoLogin.tsx +50 -0
- package/src/domains/session/OrgSwitcher.tsx +110 -0
- package/src/domains/session/SessionContext.tsx +150 -0
- package/src/domains/session/inMemoryAuthAdapter.ts +53 -0
- package/src/domains/session/index.ts +18 -0
- package/src/domains/session/types.ts +47 -0
- package/src/domains/session/utils/i18nKeys.ts +12 -0
- package/src/domains/settings/components/AuditLogList.tsx +59 -0
- package/src/domains/settings/components/PermissionMatrixScreen.tsx +80 -0
- package/src/domains/settings/components/SettingsLayout.tsx +75 -60
- package/src/domains/settings/components/SettingsSection.tsx +35 -22
- package/src/domains/settings/components/UserManagementTable.tsx +87 -0
- package/src/domains/settings/components/index.ts +10 -0
- package/src/domains/settings/hooks/useSettings.ts +77 -52
- package/src/domains/settings/types/settings.ts +2 -0
- package/src/domains/settings/utils/i18nKeys.ts +18 -0
- package/src/domains/settings/utils/settings.ts +11 -4
- package/src/i18n/I18nContext.tsx +33 -0
- package/src/i18n/defaultT.test.ts +43 -0
- package/src/i18n/defaultT.ts +34 -0
- package/src/i18n/index.ts +7 -0
- package/src/i18n/types.ts +13 -0
- package/src/index.ts +1 -7
- package/src/primitives/Button.tsx +30 -0
- package/src/primitives/Checkbox.tsx +40 -0
- package/src/primitives/Input.tsx +19 -0
- package/src/primitives/Skeleton.tsx +26 -0
- package/src/primitives/cn.ts +15 -0
- package/src/primitives/index.ts +23 -0
- package/src/styles/dashboard.css +3045 -0
- package/src/styles/tokens.contract.test.ts +66 -0
- package/src/styles/tokens.css +124 -0
- package/src/domains/billing/components/BillingLayout.tsx +0 -63
- package/src/domains/billing/components/BillingPage.tsx +0 -49
- package/src/domains/billing/components/BillingPortal.tsx +0 -198
- package/src/domains/billing/components/InvoiceCard.tsx +0 -143
- package/src/domains/billing/components/PaymentMethodsList.tsx +0 -116
- package/src/domains/billing/components/PlanComparison.tsx +0 -199
- package/src/domains/billing/components/UsageCard.tsx +0 -103
- package/src/domains/billing/components/index.ts +0 -13
- package/src/domains/billing/constants/billing.ts +0 -69
- package/src/domains/billing/hooks/index.ts +0 -7
- package/src/domains/billing/hooks/useBilling.ts +0 -385
- package/src/domains/billing/index.ts +0 -73
- package/src/domains/billing/types/billing.ts +0 -342
- package/src/domains/billing/types/index.ts +0 -30
- package/src/domains/billing/utils/billing.ts +0 -360
- package/src/domains/billing/utils/index.ts +0 -31
- package/src/domains/billing/utils/polar.ts +0 -63
- package/src/domains/calendar/components/index.ts +0 -7
|
@@ -1,123 +1,26 @@
|
|
|
1
|
-
|
|
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 {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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
|
-
|
|
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
|
|
32
|
+
const t = useT();
|
|
130
33
|
const [saving, setSaving] = useState(false);
|
|
131
|
-
const [
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
currentStep
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
145
|
-
const canGoNext = useCallback(() => {
|
|
52
|
+
const validateCurrentStep = useCallback((): boolean => {
|
|
146
53
|
const currentStepConfig = config.steps[currentStep - 1];
|
|
147
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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 (
|
|
166
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
}, [
|
|
99
|
+
navigate(config.cancelRoute ?? "/");
|
|
100
|
+
}, [config.cancelRoute, navigate, onCancel]);
|
|
194
101
|
|
|
195
102
|
const handleSkip = useCallback(() => {
|
|
196
|
-
if (
|
|
197
|
-
|
|
103
|
+
if (!isLastStep) {
|
|
104
|
+
handleNext();
|
|
198
105
|
}
|
|
199
|
-
}, [
|
|
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="
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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="
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
) :
|
|
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={
|
|
176
|
+
canGoNext={validateCurrentStep()}
|
|
252
177
|
isSaving={saving}
|
|
253
|
-
onNext={
|
|
254
|
-
onPrev={
|
|
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
|
);
|