saafe-redirection-flow 2.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/.github/workflows/build-and-deploy.yml +41 -0
- package/.gitlab-ci.yml +108 -0
- package/.releaserc.json +18 -0
- package/.storybook/main.ts +28 -0
- package/.storybook/preview.ts +16 -0
- package/.storybook/vitest.setup.ts +9 -0
- package/.vite/deps/@radix-ui_react-avatar.js +230 -0
- package/.vite/deps/@radix-ui_react-avatar.js.map +7 -0
- package/.vite/deps/@radix-ui_react-slot.js +12 -0
- package/.vite/deps/@radix-ui_react-slot.js.map +7 -0
- package/.vite/deps/_metadata.json +79 -0
- package/.vite/deps/chunk-5VGQBUCU.js +597 -0
- package/.vite/deps/chunk-5VGQBUCU.js.map +7 -0
- package/.vite/deps/chunk-DC5AMYBS.js +38 -0
- package/.vite/deps/chunk-DC5AMYBS.js.map +7 -0
- package/.vite/deps/chunk-HUIEPYH7.js +11265 -0
- package/.vite/deps/chunk-HUIEPYH7.js.map +7 -0
- package/.vite/deps/chunk-TKHB4QMX.js +281 -0
- package/.vite/deps/chunk-TKHB4QMX.js.map +7 -0
- package/.vite/deps/chunk-YLDSBLSF.js +1139 -0
- package/.vite/deps/chunk-YLDSBLSF.js.map +7 -0
- package/.vite/deps/class-variance-authority.js +63 -0
- package/.vite/deps/class-variance-authority.js.map +7 -0
- package/.vite/deps/lucide-react.js +36984 -0
- package/.vite/deps/lucide-react.js.map +7 -0
- package/.vite/deps/package.json +3 -0
- package/.vite/deps/react-dom_client.js +17917 -0
- package/.vite/deps/react-dom_client.js.map +7 -0
- package/.vite/deps/react-router-dom.js +452 -0
- package/.vite/deps/react-router-dom.js.map +7 -0
- package/.vite/deps/react-router.js +234 -0
- package/.vite/deps/react-router.js.map +7 -0
- package/.vite/deps/react.js +5 -0
- package/.vite/deps/react.js.map +7 -0
- package/.vite/deps/react_jsx-dev-runtime.js +470 -0
- package/.vite/deps/react_jsx-dev-runtime.js.map +7 -0
- package/CHANGELOG.md +420 -0
- package/LICENSE +21 -0
- package/README.md +129 -0
- package/RELEASE_CHEATSHEET.md +93 -0
- package/RELEASE_NOTES.md +120 -0
- package/components.json +21 -0
- package/docs/DEPLOYMENT_WORKFLOW.md +262 -0
- package/docs/RELEASE_GUIDE.md +591 -0
- package/docs/architecture.md +432 -0
- package/docs/components.md +199 -0
- package/docs/index.md +69 -0
- package/docs/local-release-workflow.md +234 -0
- package/docs/routes.md +118 -0
- package/docs/sdk-integration.md +325 -0
- package/docs/semantic-release.md +124 -0
- package/docs/user-flow.md +206 -0
- package/eslint.config.js +28 -0
- package/index.html +19 -0
- package/install.sh +198 -0
- package/package.json +115 -0
- package/public/images/bank-logo.png +0 -0
- package/public/saafe-icon.svg +9 -0
- package/src/App.tsx +171 -0
- package/src/__tests__/url-parameters.test.ts +82 -0
- package/src/assets/brand/applestore.svg +13 -0
- package/src/assets/brand/playstore.svg +23 -0
- package/src/assets/brand/saafe-color-white-logo.svg +14 -0
- package/src/assets/brand/saafe-icon.svg +9 -0
- package/src/assets/brand/saafe-logo.svg +18 -0
- package/src/assets/icons/check-icon-dark.svg +27 -0
- package/src/assets/icons/check-icon.svg +23 -0
- package/src/components/ErrorBoundary.tsx +132 -0
- package/src/components/alert/alert.tsx +27 -0
- package/src/components/auth/AuthGuard.tsx +76 -0
- package/src/components/cards/BankCard.stories.tsx +69 -0
- package/src/components/cards/BankCard.tsx +227 -0
- package/src/components/cards/OuterCard.tsx +109 -0
- package/src/components/cards/WrapperCard.tsx +64 -0
- package/src/components/documents/PrivacyContent.tsx +1 -0
- package/src/components/dummyFooter.tsx +29 -0
- package/src/components/icons/github.tsx +12 -0
- package/src/components/language/LanguageSwitcher.tsx +44 -0
- package/src/components/layouts/FrostedLayout.stories.tsx +42 -0
- package/src/components/layouts/FrostedLayout.tsx +333 -0
- package/src/components/layouts/MobileLayout.tsx +403 -0
- package/src/components/mobile-background.tsx +136 -0
- package/src/components/mobileAppDownload.tsx +30 -0
- package/src/components/modal/ModalComp.tsx +27 -0
- package/src/components/mode-toggle.tsx +36 -0
- package/src/components/page-header.tsx +50 -0
- package/src/components/session/SessionTimeoutScreen.tsx +134 -0
- package/src/components/session/SessionTimer.tsx +173 -0
- package/src/components/step-navigation.tsx +87 -0
- package/src/components/title/AppBar.stories.tsx +50 -0
- package/src/components/title/AppBar.tsx +150 -0
- package/src/components/title/SectionTitle.tsx +31 -0
- package/src/components/ui/AnimatedButton.module.css +13 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/animatedButton.tsx +111 -0
- package/src/components/ui/avatar.tsx +51 -0
- package/src/components/ui/badge.tsx +36 -0
- package/src/components/ui/bottom-sheet.tsx +122 -0
- package/src/components/ui/button.tsx +59 -0
- package/src/components/ui/calendar.tsx +86 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox.stories.tsx +49 -0
- package/src/components/ui/checkbox.tsx +67 -0
- package/src/components/ui/collapsible.tsx +45 -0
- package/src/components/ui/dialog.tsx +134 -0
- package/src/components/ui/document-link.tsx +26 -0
- package/src/components/ui/dot-stepper.tsx +57 -0
- package/src/components/ui/dropdown-menu.tsx +255 -0
- package/src/components/ui/form.tsx +165 -0
- package/src/components/ui/frosted-panel.stories.tsx +86 -0
- package/src/components/ui/frosted-panel.tsx +276 -0
- package/src/components/ui/input.tsx +39 -0
- package/src/components/ui/label.stories.tsx +67 -0
- package/src/components/ui/label.tsx +23 -0
- package/src/components/ui/mobile-footer.tsx +54 -0
- package/src/components/ui/modal.tsx +90 -0
- package/src/components/ui/otp-input.stories.tsx +62 -0
- package/src/components/ui/otp-input.tsx +221 -0
- package/src/components/ui/platform-specific-behavior.tsx +28 -0
- package/src/components/ui/popover.tsx +46 -0
- package/src/components/ui/progress.tsx +103 -0
- package/src/components/ui/radio-group.tsx +45 -0
- package/src/components/ui/scroll-area.tsx +56 -0
- package/src/components/ui/sdk-params-docs.tsx +53 -0
- package/src/components/ui/select.tsx +159 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +137 -0
- package/src/components/ui/sidebar.tsx +724 -0
- package/src/components/ui/skeleton.stories.tsx +50 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/ui/sonner.tsx +23 -0
- package/src/components/ui/step.stories.tsx +132 -0
- package/src/components/ui/step.tsx +234 -0
- package/src/components/ui/stepper-progress.tsx +136 -0
- package/src/components/ui/stepper.tsx +259 -0
- package/src/components/ui/tabs.tsx +55 -0
- package/src/components/ui/tooltip.tsx +61 -0
- package/src/components/ui/url-decode-loader.tsx +36 -0
- package/src/components/ui/version-display.tsx +104 -0
- package/src/components/ui/web-footer.tsx +36 -0
- package/src/config/environments.ts +99 -0
- package/src/config/urls.ts +53 -0
- package/src/const/fiTypeCategoryMap.ts +19 -0
- package/src/contexts/LanguageContext.tsx +41 -0
- package/src/contexts/RTLContext.tsx +42 -0
- package/src/contexts/ThemeContext.tsx +93 -0
- package/src/hooks/use-account-discovery.ts +205 -0
- package/src/hooks/use-auth-query.ts +141 -0
- package/src/hooks/use-fip-query.ts +72 -0
- package/src/hooks/use-media-query.ts +32 -0
- package/src/hooks/use-mobile.ts +24 -0
- package/src/hooks/use-page-title.tsx +48 -0
- package/src/hooks/use-platform.ts +52 -0
- package/src/hooks/use-trusted-count.ts +21 -0
- package/src/hooks/use-url-decode.ts +90 -0
- package/src/hooks/useStep.ts +170 -0
- package/src/index.css +154 -0
- package/src/interfaces/app.interfaces.ts +39 -0
- package/src/interfaces/services.interfaces.ts +65 -0
- package/src/lib/i18n.ts +68 -0
- package/src/lib/utils.ts +6 -0
- package/src/locales/en/common.json +167 -0
- package/src/locales/hi/common.json +137 -0
- package/src/locales/kn/common.json +137 -0
- package/src/locales/ml/common.json +137 -0
- package/src/locales/ta/common.json +137 -0
- package/src/locales/te/common.json +137 -0
- package/src/locales/ur/common.json +138 -0
- package/src/main.tsx +46 -0
- package/src/pages/Login.tsx +363 -0
- package/src/pages/accounts/AccountsToProceed.tsx +396 -0
- package/src/pages/accounts/Discover.tsx +76 -0
- package/src/pages/accounts/DiscoverAccount.tsx +751 -0
- package/src/pages/accounts/LinkSelectedAccounts.tsx +638 -0
- package/src/pages/accounts/OldUser.tsx +329 -0
- package/src/pages/accounts/link-accounts.tsx +913 -0
- package/src/pages/consent/ReviewConsent.tsx +836 -0
- package/src/pages/consent/rejected.tsx +253 -0
- package/src/pages/consent/success.tsx +220 -0
- package/src/providers/query-provider.tsx +24 -0
- package/src/providers/toast-provider.tsx +26 -0
- package/src/services/api/account.service.ts +296 -0
- package/src/services/api/auth.service.ts +206 -0
- package/src/services/api/axios.ts +138 -0
- package/src/services/api/consent.service.ts +142 -0
- package/src/services/api/decode.service.ts +53 -0
- package/src/services/api/feedback.service.ts +34 -0
- package/src/services/api/fip.service.ts +187 -0
- package/src/services/api/index.ts +9 -0
- package/src/services/api/public.service.ts +18 -0
- package/src/services/api.ts +2 -0
- package/src/services/postMessage.service.ts +179 -0
- package/src/store/NavigationBlockContext.tsx +34 -0
- package/src/store/auth.store.ts +79 -0
- package/src/store/fip.store.ts +396 -0
- package/src/store/mandatoryConsent.store.ts +24 -0
- package/src/store/redirect.store.ts +73 -0
- package/src/store/step.store.ts +124 -0
- package/src/stories/Button.stories.ts +53 -0
- package/src/stories/Button.tsx +37 -0
- package/src/stories/Configure.mdx +364 -0
- package/src/stories/Header.stories.ts +33 -0
- package/src/stories/Header.tsx +56 -0
- package/src/stories/Page.stories.ts +32 -0
- package/src/stories/Page.tsx +73 -0
- package/src/stories/button.css +30 -0
- package/src/stories/header.css +32 -0
- package/src/stories/page.css +68 -0
- package/src/styles/rtl-utils.css +90 -0
- package/src/styles/rtl.css +105 -0
- package/src/utils/api-error.ts +26 -0
- package/src/utils/cn.ts +10 -0
- package/src/utils/error-callback.ts +116 -0
- package/src/utils/formatAccountNumber.ts +9 -0
- package/src/utils/handleIdentifiers.ts +90 -0
- package/src/utils/posthog.ts +67 -0
- package/src/utils/toast-helpers.ts +61 -0
- package/src/vite-env.d.ts +1 -0
- package/stage-aa-2506251021.zip +0 -0
- package/tsconfig.app.json +33 -0
- package/tsconfig.json +13 -0
- package/tsconfig.node.json +24 -0
- package/vite.config.ts +45 -0
- package/vitest.shims.d.ts +1 -0
- package/vitest.workspace.ts +46 -0
@@ -0,0 +1,333 @@
|
|
1
|
+
"use client";
|
2
|
+
|
3
|
+
import React, { useEffect, useState } from "react";
|
4
|
+
import { FrostedPanel } from "@/components/ui/frosted-panel";
|
5
|
+
import { Step, StepGroup, StepConnector } from "@/components/ui/step";
|
6
|
+
import { Link, FileText, Shield, Building2, Receipt, ShieldCheck, Info } from "lucide-react";
|
7
|
+
import { useLocation } from "react-router-dom";
|
8
|
+
import { motion, AnimatePresence } from "framer-motion";
|
9
|
+
import AppBar from "../title/AppBar";
|
10
|
+
import { useIsMobile } from "@/hooks/use-mobile";
|
11
|
+
import { MobileLayout } from "./MobileLayout";
|
12
|
+
import { useFipStore } from "@/store/fip.store";
|
13
|
+
import { useTranslation } from "react-i18next";
|
14
|
+
import { useStepStore } from "@/store/step.store";
|
15
|
+
import { useRTL } from "@/contexts/RTLContext";
|
16
|
+
import { useRedirectStore } from "@/store/redirect.store";
|
17
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
|
18
|
+
import { VersionDisplay } from "../ui/version-display";
|
19
|
+
// Define step interfaces
|
20
|
+
interface BaseStep {
|
21
|
+
step: number;
|
22
|
+
title: string;
|
23
|
+
description: string;
|
24
|
+
icon: React.ReactNode;
|
25
|
+
path: string;
|
26
|
+
parentStep?: number;
|
27
|
+
}
|
28
|
+
|
29
|
+
// Base steps without dynamic child steps
|
30
|
+
const baseSteps: BaseStep[] = [
|
31
|
+
{
|
32
|
+
step: 1,
|
33
|
+
title: "Login/Signup",
|
34
|
+
description: "Enter the OTP sent to your mobile",
|
35
|
+
icon: <Shield className="w-4 h-4" />,
|
36
|
+
path: "/login",
|
37
|
+
},
|
38
|
+
{
|
39
|
+
step: 2,
|
40
|
+
title: "Link Accounts",
|
41
|
+
description: "Select & Link the accounts you'd like to share",
|
42
|
+
icon: <Link className="w-4 h-4" />,
|
43
|
+
path: "/link-accounts/discover",
|
44
|
+
},
|
45
|
+
{
|
46
|
+
step: 3,
|
47
|
+
title: "Review Consent",
|
48
|
+
description: "Approve/Reject the consent after reviewing the details",
|
49
|
+
icon: <FileText className="w-4 h-4" />,
|
50
|
+
path: "/review",
|
51
|
+
},
|
52
|
+
];
|
53
|
+
|
54
|
+
// Map of category icons
|
55
|
+
const categoryIcons = {
|
56
|
+
"BANK": <Building2 className="w-4 h-4" />,
|
57
|
+
"GST": <Receipt className="w-4 h-4" />,
|
58
|
+
"INSURANCE": <ShieldCheck className="w-4 h-4" />,
|
59
|
+
"INVESTMENTS": <ShieldCheck className="w-4 h-4" />
|
60
|
+
};
|
61
|
+
|
62
|
+
interface FrostedLayoutProps {
|
63
|
+
children: React.ReactNode;
|
64
|
+
footer?: React.ReactNode;
|
65
|
+
title?: string;
|
66
|
+
notAuthenticated?: boolean;
|
67
|
+
}
|
68
|
+
|
69
|
+
export function FrostedLayout({ footer, children, title, notAuthenticated }: FrostedLayoutProps) {
|
70
|
+
const location = useLocation();
|
71
|
+
const isMobile = useIsMobile();
|
72
|
+
const currentPath = location.pathname;
|
73
|
+
const [steps, setSteps] = useState(baseSteps);
|
74
|
+
const { t } = useTranslation();
|
75
|
+
const { decodedInfo } = useRedirectStore()
|
76
|
+
const { categories, activeCategory, currentChildStep } = useFipStore();
|
77
|
+
const { activeStep, completedSteps, setActiveStep, setCompletedSteps, determineActiveStep } = useStepStore();
|
78
|
+
const [expandedParents, setExpandedParents] = useState<number[]>([]);
|
79
|
+
const { isRTL } = useRTL();
|
80
|
+
|
81
|
+
// Dynamically create steps whenever categories change
|
82
|
+
useEffect(() => {
|
83
|
+
if (categories && categories.length > 0) {
|
84
|
+
// Create child steps for Link Accounts
|
85
|
+
const childSteps = categories.map((category, index) => ({
|
86
|
+
step: 3 + index, // Start numbering after Link Accounts
|
87
|
+
title: t(`categories.${category.toLowerCase()}`),
|
88
|
+
description: t(`categories.${category.toLowerCase()}Description`),
|
89
|
+
icon: categoryIcons[category as keyof typeof categoryIcons] || <Link className="w-4 h-4" />,
|
90
|
+
path: `/link-accounts/${category.toLowerCase()}`,
|
91
|
+
parentStep: 2, // Link Accounts is step 2,
|
92
|
+
isActive: category === activeCategory
|
93
|
+
}));
|
94
|
+
|
95
|
+
// Combine base steps with child steps
|
96
|
+
const newSteps = [
|
97
|
+
...baseSteps.slice(0, 2), // Login and Link Accounts
|
98
|
+
...childSteps,
|
99
|
+
baseSteps[2], // Review Consent, update the step number
|
100
|
+
];
|
101
|
+
|
102
|
+
// Update Review Consent step number
|
103
|
+
newSteps[newSteps.length - 1] = {
|
104
|
+
...newSteps[newSteps.length - 1],
|
105
|
+
step: 3 + childSteps.length
|
106
|
+
};
|
107
|
+
|
108
|
+
setSteps(newSteps);
|
109
|
+
|
110
|
+
// Update totalSteps in the store
|
111
|
+
useStepStore.setState({ totalSteps: newSteps.length });
|
112
|
+
} else {
|
113
|
+
setSteps(baseSteps);
|
114
|
+
|
115
|
+
// Reset to base steps count
|
116
|
+
useStepStore.setState({ totalSteps: baseSteps.length });
|
117
|
+
}
|
118
|
+
}, [categories, activeCategory]);
|
119
|
+
|
120
|
+
// Find the app title from the current step
|
121
|
+
const currentStepData = steps.find(step => step.path === currentPath);
|
122
|
+
const appTitle = title || t("keywords.select") + " " + `${currentStepData?.title || t("categories.bank")}` + " & " + t("keywords.discover") + " " + t("keywords.your") + " " + t("keywords.accounts");
|
123
|
+
|
124
|
+
|
125
|
+
// Determine the current active step using the store's determineActiveStep function
|
126
|
+
const currentStep = determineActiveStep(currentPath, steps);
|
127
|
+
const [hoverIButton, setHoverIButton] = useState()
|
128
|
+
// Update the store's active step whenever it changes
|
129
|
+
useEffect(() => {
|
130
|
+
setActiveStep(currentStep);
|
131
|
+
|
132
|
+
// Mark previous steps as completed
|
133
|
+
if (currentStep > 1) {
|
134
|
+
const newCompletedSteps = new Set(completedSteps);
|
135
|
+
|
136
|
+
// Add all steps from 1 to current-1 as completed
|
137
|
+
for (let i = 1; i < currentStep; i++) {
|
138
|
+
newCompletedSteps.add(i);
|
139
|
+
}
|
140
|
+
|
141
|
+
// For parent steps, check if all their children are completed
|
142
|
+
steps.forEach(step => {
|
143
|
+
if (step.parentStep) {
|
144
|
+
const childSteps = steps.filter(s => s.parentStep === step.parentStep);
|
145
|
+
const allChildrenCompleted = childSteps.every(s => newCompletedSteps.has(s.step));
|
146
|
+
if (allChildrenCompleted) {
|
147
|
+
newCompletedSteps.add(step.parentStep);
|
148
|
+
}
|
149
|
+
}
|
150
|
+
});
|
151
|
+
|
152
|
+
setCompletedSteps(Array.from(newCompletedSteps));
|
153
|
+
}
|
154
|
+
}, [currentStep, setActiveStep, setCompletedSteps, steps]);
|
155
|
+
|
156
|
+
// Check if all child steps of a parent are completed
|
157
|
+
const areAllChildStepsCompleted = (parentStep: number) => {
|
158
|
+
const childSteps = steps.filter(step => step.parentStep === parentStep);
|
159
|
+
return childSteps.length > 0 && childSteps.every(step => completedSteps.includes(step.step));
|
160
|
+
};
|
161
|
+
|
162
|
+
// Toggle expansion of a parent step
|
163
|
+
const toggleParentExpansion = (parentStep: number) => {
|
164
|
+
setExpandedParents(prev =>
|
165
|
+
prev.includes(parentStep)
|
166
|
+
? prev.filter(p => p !== parentStep)
|
167
|
+
: [...prev, parentStep]
|
168
|
+
);
|
169
|
+
};
|
170
|
+
|
171
|
+
// On completion of all child steps, automatically fold them
|
172
|
+
useEffect(() => {
|
173
|
+
// Get all parent steps that have children
|
174
|
+
const parentSteps = [...new Set(steps.filter(step => step.parentStep).map(step => step.parentStep))];
|
175
|
+
|
176
|
+
// Initial setup - expand the parent of the active step if it exists
|
177
|
+
const activeStepParent = steps.find(s => s.step === activeStep)?.parentStep;
|
178
|
+
|
179
|
+
// Check each parent step
|
180
|
+
parentSteps.forEach(parentStep => {
|
181
|
+
if (parentStep) {
|
182
|
+
if (areAllChildStepsCompleted(parentStep)) {
|
183
|
+
// If all children completed and parent is expanded and not the active parent, collapse it
|
184
|
+
if (expandedParents.includes(parentStep) && parentStep !== activeStepParent) {
|
185
|
+
setExpandedParents(prev => prev.filter(p => p !== parentStep));
|
186
|
+
}
|
187
|
+
} else {
|
188
|
+
// If not all completed, ensure it's expanded
|
189
|
+
if (!expandedParents.includes(parentStep)) {
|
190
|
+
setExpandedParents(prev => [...prev, parentStep]);
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
// Always expand the active step's parent
|
195
|
+
if (activeStepParent && !expandedParents.includes(activeStepParent)) {
|
196
|
+
setExpandedParents(prev => [...prev, activeStepParent]);
|
197
|
+
}
|
198
|
+
}
|
199
|
+
});
|
200
|
+
}, [completedSteps, steps, activeStep]);
|
201
|
+
|
202
|
+
// Use mobile layout for mobile devices
|
203
|
+
if (isMobile) {
|
204
|
+
return <MobileLayout title={appTitle}>{children}</MobileLayout>;
|
205
|
+
}
|
206
|
+
|
207
|
+
// Tablet and desktop layout
|
208
|
+
return (
|
209
|
+
<FrostedPanel
|
210
|
+
blobConfig={{
|
211
|
+
speed: 0.9,
|
212
|
+
count: 4,
|
213
|
+
color: "rgba(255, 255, 255, 0.4)",
|
214
|
+
minSize: 100,
|
215
|
+
maxSize: 250,
|
216
|
+
}}
|
217
|
+
gradientColors={{
|
218
|
+
top: "#004d4d",
|
219
|
+
bottom: "#00b2c1",
|
220
|
+
}}
|
221
|
+
>
|
222
|
+
<FrostedPanel.Aside className="px-4 py-6">
|
223
|
+
<div className="flex justify-between items-center cursor-default">
|
224
|
+
<h1 className="text-2xl font-semibold text-white">
|
225
|
+
{t('layout.title')}
|
226
|
+
</h1>
|
227
|
+
</div>
|
228
|
+
|
229
|
+
<StepGroup
|
230
|
+
activeStep={activeStep}
|
231
|
+
completedSteps={completedSteps}
|
232
|
+
baseColor="#FFF"
|
233
|
+
className="mt-20"
|
234
|
+
spacing={32}
|
235
|
+
>
|
236
|
+
<AnimatePresence mode="wait">
|
237
|
+
{steps.map(({ step, title, icon, description, parentStep }, index) => {
|
238
|
+
|
239
|
+
// Skip child steps of completed parents that aren't expanded
|
240
|
+
|
241
|
+
if (parentStep &&
|
242
|
+
areAllChildStepsCompleted(parentStep) &&
|
243
|
+
!expandedParents.includes(parentStep) &&
|
244
|
+
activeStep !== step) {
|
245
|
+
|
246
|
+
return null;
|
247
|
+
}
|
248
|
+
|
249
|
+
// For parent steps that have children
|
250
|
+
const hasChildren = steps.some(s => s.parentStep === step);
|
251
|
+
|
252
|
+
return (
|
253
|
+
<motion.div
|
254
|
+
key={`step-${step}-${title}`}
|
255
|
+
className="relative group" // group for hover
|
256
|
+
initial={{ opacity: 0, x: -10 }}
|
257
|
+
animate={{ opacity: 1, x: 0 }}
|
258
|
+
exit={{ opacity: 0, x: 10 }}
|
259
|
+
transition={{
|
260
|
+
duration: 0.3,
|
261
|
+
delay: parentStep ? 0.1 * (index % categories.length) : 0
|
262
|
+
}}
|
263
|
+
>
|
264
|
+
<div className="relative flex items-center">
|
265
|
+
<Step
|
266
|
+
step={step}
|
267
|
+
title={parentStep ? title : t(`stepper.${title.toLowerCase().replace('/', '')}`)}
|
268
|
+
icon={icon}
|
269
|
+
isActive={parentStep ? activeCategory == title?.toUpperCase() : activeStep == step}
|
270
|
+
isCompleted={!parentStep && steps[step]?.parentStep ?
|
271
|
+
steps.filter(s => s.parentStep === step).every(s => completedSteps.includes(s.step)) :
|
272
|
+
completedSteps.includes(step)
|
273
|
+
}
|
274
|
+
isParentPending={Boolean(!parentStep && steps[step]?.parentStep &&
|
275
|
+
!steps.filter(s => s.parentStep === step).every(s => completedSteps.includes(s.step)))
|
276
|
+
}
|
277
|
+
isLast={index === steps.length - 1}
|
278
|
+
height={parentStep ? 90 : 80}
|
279
|
+
isChildStep={Boolean(parentStep)}
|
280
|
+
onStepClick={hasChildren ? () => toggleParentExpansion(step) : undefined}
|
281
|
+
notAuthenticated={notAuthenticated}
|
282
|
+
isRTL={isRTL}
|
283
|
+
/>
|
284
|
+
|
285
|
+
{index < steps.length - 1 && (
|
286
|
+
<StepConnector
|
287
|
+
isActive={true}
|
288
|
+
isCompleted={completedSteps.includes(step)}
|
289
|
+
/>
|
290
|
+
)}
|
291
|
+
|
292
|
+
{index == 1 && (
|
293
|
+
<Tooltip>
|
294
|
+
<TooltipTrigger asChild>
|
295
|
+
<Info className={`${'w-3 h-3 mb-13 ml-2'} text-muted cursor-pointer ml-1`} />
|
296
|
+
</TooltipTrigger>
|
297
|
+
<TooltipContent side="right" align="center" className="max-w-[280px] whitespace-normal text-sm bg-gray-700" >
|
298
|
+
{t("stepper.linkAccountsDescription")}
|
299
|
+
</TooltipContent>
|
300
|
+
</Tooltip>
|
301
|
+
)}
|
302
|
+
</div>
|
303
|
+
</motion.div>
|
304
|
+
)
|
305
|
+
})}
|
306
|
+
</AnimatePresence>
|
307
|
+
</StepGroup>
|
308
|
+
</FrostedPanel.Aside>
|
309
|
+
|
310
|
+
<FrostedPanel.Main className="flex flex-col justify-between">
|
311
|
+
<div className="px-14 sticky top-0 z-10">
|
312
|
+
|
313
|
+
<AppBar title={appTitle} className="mt-6" />
|
314
|
+
</div>
|
315
|
+
<div className="overflow-y-auto h-full my-1">
|
316
|
+
<AnimatePresence mode="wait">
|
317
|
+
<motion.div
|
318
|
+
key={location.pathname}
|
319
|
+
initial={{ opacity: 0, y: 20 }}
|
320
|
+
animate={{ opacity: 1, y: 0 }}
|
321
|
+
exit={{ opacity: 0, y: -20 }}
|
322
|
+
transition={{ duration: 0.3 }}
|
323
|
+
className="flex h-full w-full flex-col"
|
324
|
+
>
|
325
|
+
{children}
|
326
|
+
</motion.div>
|
327
|
+
</AnimatePresence>
|
328
|
+
</div>
|
329
|
+
{footer && <div className="w-full px-14 py-4">{footer}</div>}
|
330
|
+
</FrostedPanel.Main>
|
331
|
+
</FrostedPanel>
|
332
|
+
);
|
333
|
+
}
|