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,124 @@
|
|
1
|
+
# Semantic Release Setup
|
2
|
+
|
3
|
+
This project uses [semantic-release](https://github.com/semantic-release/semantic-release) for automated version management and package publishing.
|
4
|
+
|
5
|
+
## Commit Message Format
|
6
|
+
|
7
|
+
We use the [Angular Commit Message Convention](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-format). Each commit message consists of a **header**, a **body**, and a **footer**.
|
8
|
+
|
9
|
+
### Header
|
10
|
+
|
11
|
+
The header has a special format that includes a **type**, a **scope**, and a **subject**:
|
12
|
+
|
13
|
+
```
|
14
|
+
<type>(<scope>): <subject>
|
15
|
+
```
|
16
|
+
|
17
|
+
#### Type
|
18
|
+
|
19
|
+
Must be one of the following:
|
20
|
+
|
21
|
+
- **feat**: A new feature (correlates with MINOR in semantic versioning)
|
22
|
+
- **fix**: A bug fix (correlates with PATCH in semantic versioning)
|
23
|
+
- **docs**: Documentation only changes
|
24
|
+
- **style**: Changes that do not affect the meaning of the code
|
25
|
+
- **refactor**: A code change that neither fixes a bug nor adds a feature
|
26
|
+
- **perf**: A code change that improves performance
|
27
|
+
- **test**: Adding missing tests or correcting existing tests
|
28
|
+
- **chore**: Changes to the build process or auxiliary tools
|
29
|
+
|
30
|
+
#### Scope
|
31
|
+
|
32
|
+
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
|
33
|
+
|
34
|
+
Examples:
|
35
|
+
- `ui`
|
36
|
+
- `auth`
|
37
|
+
- `api`
|
38
|
+
- `components`
|
39
|
+
|
40
|
+
#### Subject
|
41
|
+
|
42
|
+
The subject contains a succinct description of the change:
|
43
|
+
|
44
|
+
- Use the imperative, present tense: "change" not "changed" nor "changes"
|
45
|
+
- Don't capitalize the first letter
|
46
|
+
- No dot (.) at the end
|
47
|
+
|
48
|
+
### Breaking Changes
|
49
|
+
|
50
|
+
To indicate a breaking change, add `BREAKING CHANGE:` in the footer of the commit message, or append `!` after the type/scope:
|
51
|
+
|
52
|
+
```
|
53
|
+
feat!: drop support for Node 12
|
54
|
+
```
|
55
|
+
|
56
|
+
or
|
57
|
+
|
58
|
+
```
|
59
|
+
feat(api): add new authentication method
|
60
|
+
|
61
|
+
BREAKING CHANGE: The old authentication method is no longer supported.
|
62
|
+
```
|
63
|
+
|
64
|
+
## Release Types
|
65
|
+
|
66
|
+
| Commit message | Release type |
|
67
|
+
|---|---|
|
68
|
+
| `fix(pencil): stop graphite breaking when too much pressure applied` | Patch Release |
|
69
|
+
| `feat(pencil): add 'graphiteWidth' option` | Feature Release |
|
70
|
+
| `perf(pencil): remove graphiteWidth option`<br><br>`BREAKING CHANGE: The graphiteWidth option has been removed.` | Breaking Release |
|
71
|
+
|
72
|
+
## Examples
|
73
|
+
|
74
|
+
### Feature
|
75
|
+
```
|
76
|
+
feat(ui): add version display component
|
77
|
+
|
78
|
+
- Add responsive version display component
|
79
|
+
- Include build date information
|
80
|
+
- Support multiple display variants
|
81
|
+
```
|
82
|
+
|
83
|
+
### Bug Fix
|
84
|
+
```
|
85
|
+
fix(auth): resolve login timeout issue
|
86
|
+
|
87
|
+
- Increase timeout duration for mobile networks
|
88
|
+
- Add better error handling for network failures
|
89
|
+
```
|
90
|
+
|
91
|
+
### Breaking Change
|
92
|
+
```
|
93
|
+
feat(api)!: update authentication flow
|
94
|
+
|
95
|
+
BREAKING CHANGE: Authentication now requires OAuth 2.0 instead of API keys
|
96
|
+
```
|
97
|
+
|
98
|
+
## Running Semantic Release
|
99
|
+
|
100
|
+
### Locally (Dry Run)
|
101
|
+
```bash
|
102
|
+
npm run semantic-release:dry-run
|
103
|
+
```
|
104
|
+
|
105
|
+
### Production
|
106
|
+
Semantic release runs automatically on the CI/CD pipeline when code is pushed to the `main` branch.
|
107
|
+
|
108
|
+
## Configuration
|
109
|
+
|
110
|
+
The semantic release configuration is defined in `.releaserc.json`:
|
111
|
+
|
112
|
+
- **Branches**: `main` for production releases, `develop` for beta releases
|
113
|
+
- **Plugins**: Commit analyzer, release notes generator, changelog, git, and GitHub releases
|
114
|
+
- **Assets**: Updates `package.json`, `package-lock.json`, and `CHANGELOG.md`
|
115
|
+
|
116
|
+
## Version Display
|
117
|
+
|
118
|
+
The application automatically displays the current version in:
|
119
|
+
|
120
|
+
1. **Mobile Footer**: Compact version display
|
121
|
+
2. **Desktop Footer**: Version with build date
|
122
|
+
3. **Desktop Sidebar**: Minimal version display
|
123
|
+
|
124
|
+
The version information is automatically injected during the build process using Vite environment variables.
|
@@ -0,0 +1,206 @@
|
|
1
|
+
# SAAFE Redirection Flow - User Journey
|
2
|
+
|
3
|
+
This document outlines the typical user journey through the SAAFE Redirection Flow application, from initial loading to successful account linking or rejection.
|
4
|
+
|
5
|
+
## User Flow Overview
|
6
|
+
|
7
|
+
The SAAFE Redirection Flow guides users through a structured process to securely link their financial accounts. The typical flow consists of these key stages:
|
8
|
+
|
9
|
+
1. **Application Loading**
|
10
|
+
2. **Authentication**
|
11
|
+
3. **Account Discovery**
|
12
|
+
4. **Account Selection**
|
13
|
+
5. **Consent Review**
|
14
|
+
6. **Completion (Success or Rejection)**
|
15
|
+
|
16
|
+
Below is a detailed explanation of each stage, including screenshots, user interactions, and technical considerations.
|
17
|
+
|
18
|
+
## 1. Application Loading
|
19
|
+
|
20
|
+
### Initial Experience
|
21
|
+
|
22
|
+
When users first access the SAAFE Redirection Flow (typically through a partner app's WebView or iframe), they see:
|
23
|
+
|
24
|
+
- Loading indicator while URL parameters are decoded
|
25
|
+
- Branded experience with partner's logo and styling (if configured)
|
26
|
+
- Platform-specific UI optimizations based on the `platform` parameter
|
27
|
+
|
28
|
+
### Technical Details
|
29
|
+
|
30
|
+
- URL parameters (`fi`, `ecreq`, `reqdate`) are validated and decoded
|
31
|
+
- Theme is initialized based on the `theme` parameter or system preference
|
32
|
+
- Custom styling is applied if provided
|
33
|
+
- Analytics event `APP_LOAD` is triggered
|
34
|
+
|
35
|
+
## 2. Authentication
|
36
|
+
|
37
|
+
### User Actions
|
38
|
+
|
39
|
+
Users authenticate themselves with their financial institution:
|
40
|
+
|
41
|
+
1. Enter login credentials (username/password)
|
42
|
+
2. Verify identity with One-Time Password (OTP) if required
|
43
|
+
3. View and accept the financial institution's terms and conditions
|
44
|
+
|
45
|
+
### UI Components
|
46
|
+
|
47
|
+
- Login form with validation
|
48
|
+
- OTP input with auto-focus and clipboard support
|
49
|
+
- Terms and conditions modal (platform-specific display)
|
50
|
+
- Error messages for invalid credentials
|
51
|
+
|
52
|
+
### Technical Details
|
53
|
+
|
54
|
+
- Secure credential transmission
|
55
|
+
- Session token management
|
56
|
+
- Authentication state management in Zustand store
|
57
|
+
- Platform-specific document handling
|
58
|
+
|
59
|
+
## 3. Account Discovery
|
60
|
+
|
61
|
+
### User Actions
|
62
|
+
|
63
|
+
After successful authentication, users can:
|
64
|
+
|
65
|
+
1. View financial institutions available for linking
|
66
|
+
2. Browse by category (Banks, NBFCs, Insurance, etc.)
|
67
|
+
3. Search for specific institutions
|
68
|
+
4. Select their institution
|
69
|
+
|
70
|
+
### UI Components
|
71
|
+
|
72
|
+
- Category tabs or filters
|
73
|
+
- Search bar with autocomplete
|
74
|
+
- Institution cards with logos
|
75
|
+
- Skeleton loaders during data fetching
|
76
|
+
|
77
|
+
### Technical Details
|
78
|
+
|
79
|
+
- Data fetching with React Query
|
80
|
+
- Caching of institution list
|
81
|
+
- Search functionality with debounce
|
82
|
+
- Analytics events for tracking selections
|
83
|
+
|
84
|
+
## 4. Account Selection
|
85
|
+
|
86
|
+
### User Actions
|
87
|
+
|
88
|
+
After selecting an institution, users can:
|
89
|
+
|
90
|
+
1. View their accounts at the selected institution
|
91
|
+
2. Select which accounts to link
|
92
|
+
3. View account details (type, balance, etc.)
|
93
|
+
4. Proceed to consent review
|
94
|
+
|
95
|
+
### UI Components
|
96
|
+
|
97
|
+
- Account cards with details
|
98
|
+
- Checkbox selection for multiple accounts
|
99
|
+
- Continue button with disabled state when no accounts selected
|
100
|
+
- Information tooltips for account types
|
101
|
+
|
102
|
+
### Technical Details
|
103
|
+
|
104
|
+
- Account data fetching and state management
|
105
|
+
- Selection tracking in local state
|
106
|
+
- Validation before proceeding to next step
|
107
|
+
- Progressive disclosure of information
|
108
|
+
|
109
|
+
## 5. Consent Review
|
110
|
+
|
111
|
+
### User Actions
|
112
|
+
|
113
|
+
Before finalizing the account linking, users:
|
114
|
+
|
115
|
+
1. Review what data will be shared
|
116
|
+
2. See who will receive the data (third-party application)
|
117
|
+
3. Understand how the data will be used
|
118
|
+
4. Choose to approve or reject the consent request
|
119
|
+
|
120
|
+
### UI Components
|
121
|
+
|
122
|
+
- Detailed consent information
|
123
|
+
- Data sharing scope visualization
|
124
|
+
- Clear accept and reject buttons
|
125
|
+
- Terms and policies links
|
126
|
+
|
127
|
+
### Technical Details
|
128
|
+
|
129
|
+
- Consent data preparation
|
130
|
+
- Tracking consent review time
|
131
|
+
- Preparing for submission
|
132
|
+
- Navigation protection to prevent accidental exits
|
133
|
+
|
134
|
+
## 6. Completion
|
135
|
+
|
136
|
+
### Success Path
|
137
|
+
|
138
|
+
If the user approves the consent:
|
139
|
+
|
140
|
+
1. Success screen confirms account linking
|
141
|
+
2. Confirmation of which accounts were linked
|
142
|
+
3. Next steps or return to partner application
|
143
|
+
4. Optional promotional content
|
144
|
+
|
145
|
+
### Rejection Path
|
146
|
+
|
147
|
+
If the user rejects the consent:
|
148
|
+
|
149
|
+
1. Rejection confirmation screen
|
150
|
+
2. Explanation of implications
|
151
|
+
3. Option to try again or return to partner application
|
152
|
+
|
153
|
+
### Technical Details
|
154
|
+
|
155
|
+
- Final API submission of user selections
|
156
|
+
- Success/failure handling
|
157
|
+
- Communication back to parent application
|
158
|
+
- Session cleanup
|
159
|
+
- Final analytics events
|
160
|
+
|
161
|
+
## Special Flows
|
162
|
+
|
163
|
+
### Returning Users
|
164
|
+
|
165
|
+
For users who have previously linked accounts:
|
166
|
+
|
167
|
+
1. View currently linked accounts
|
168
|
+
2. Option to link additional accounts
|
169
|
+
3. Option to unlink existing accounts
|
170
|
+
|
171
|
+
### Error Handling
|
172
|
+
|
173
|
+
When errors occur during the flow:
|
174
|
+
|
175
|
+
1. User-friendly error messages
|
176
|
+
2. Retry options where appropriate
|
177
|
+
3. Fallback paths for recoverable errors
|
178
|
+
4. Contact support options for unrecoverable errors
|
179
|
+
|
180
|
+
## Platform-Specific Differences
|
181
|
+
|
182
|
+
### Mobile vs Web
|
183
|
+
|
184
|
+
The user experience adjusts based on platform:
|
185
|
+
|
186
|
+
- **Mobile**: Full-screen optimized UI, bottom sheet dialogs, mobile-specific gestures
|
187
|
+
- **Web**: More spacious layout, hover states, keyboard shortcuts
|
188
|
+
|
189
|
+
### iOS vs Android
|
190
|
+
|
191
|
+
Platform-specific optimizations:
|
192
|
+
|
193
|
+
- **iOS**: iOS-style UI components, respects safe areas, iOS-specific gestures
|
194
|
+
- **Android**: Material Design influenced components, Android back button handling
|
195
|
+
|
196
|
+
## Analytics Touchpoints
|
197
|
+
|
198
|
+
The application tracks user progress through the journey:
|
199
|
+
|
200
|
+
- Page views for each step
|
201
|
+
- Time spent on each step
|
202
|
+
- Drop-off points
|
203
|
+
- Success/failure rates
|
204
|
+
- Error encounters
|
205
|
+
|
206
|
+
This data helps improve the flow and identify potential issues.
|
package/eslint.config.js
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
import js from '@eslint/js'
|
2
|
+
import globals from 'globals'
|
3
|
+
import reactHooks from 'eslint-plugin-react-hooks'
|
4
|
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
5
|
+
import tseslint from 'typescript-eslint'
|
6
|
+
|
7
|
+
export default tseslint.config(
|
8
|
+
{ ignores: ['dist'] },
|
9
|
+
{
|
10
|
+
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
11
|
+
files: ['**/*.{ts,tsx}'],
|
12
|
+
languageOptions: {
|
13
|
+
ecmaVersion: 2020,
|
14
|
+
globals: globals.browser,
|
15
|
+
},
|
16
|
+
plugins: {
|
17
|
+
'react-hooks': reactHooks,
|
18
|
+
'react-refresh': reactRefresh,
|
19
|
+
},
|
20
|
+
rules: {
|
21
|
+
...reactHooks.configs.recommended.rules,
|
22
|
+
'react-refresh/only-export-components': [
|
23
|
+
'warn',
|
24
|
+
{ allowConstantExport: true },
|
25
|
+
],
|
26
|
+
},
|
27
|
+
},
|
28
|
+
)
|
package/index.html
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8" />
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/saafe-icon.svg" />
|
6
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
7
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
8
|
+
<link
|
9
|
+
href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
|
10
|
+
rel="stylesheet"
|
11
|
+
/>
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
13
|
+
<title>Saafe</title>
|
14
|
+
</head>
|
15
|
+
<body>
|
16
|
+
<div id="root"></div>
|
17
|
+
<script type="module" src="/src/main.tsx"></script>
|
18
|
+
</body>
|
19
|
+
</html>
|
package/install.sh
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Color definitions
|
4
|
+
RED='\033[0;31m'
|
5
|
+
GREEN='\033[0;32m'
|
6
|
+
YELLOW='\033[1;33m'
|
7
|
+
BLUE='\033[0;34m'
|
8
|
+
CYAN='\033[0;36m'
|
9
|
+
NC='\033[0m' # No Color
|
10
|
+
|
11
|
+
# Logo
|
12
|
+
print_logo() {
|
13
|
+
echo -e "${CYAN}"
|
14
|
+
cat << "EOF"
|
15
|
+
██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗
|
16
|
+
██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ ██╔══██╗██╔══██╗██╔═══██╗
|
17
|
+
██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ ██████╔╝██████╔╝██║ ██║
|
18
|
+
██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ ██╔═══╝ ██╔══██╗██║ ██║
|
19
|
+
╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ ██║ ██║ ██║╚██████╔╝
|
20
|
+
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
|
21
|
+
EOF
|
22
|
+
echo -e "${NC}"
|
23
|
+
}
|
24
|
+
|
25
|
+
# Get download folder path
|
26
|
+
get_downloads_dir() {
|
27
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
28
|
+
echo "$HOME/Downloads"
|
29
|
+
else
|
30
|
+
if [ -f "$HOME/.config/user-dirs.dirs" ]; then
|
31
|
+
. "$HOME/.config/user-dirs.dirs"
|
32
|
+
echo "${XDG_DOWNLOAD_DIR:-$HOME/Downloads}"
|
33
|
+
else
|
34
|
+
echo "$HOME/Downloads"
|
35
|
+
fi
|
36
|
+
fi
|
37
|
+
}
|
38
|
+
|
39
|
+
# Get latest version
|
40
|
+
get_latest_version() {
|
41
|
+
echo -e "${CYAN}ℹ️ Checking latest version...${NC}"
|
42
|
+
latest_release=$(curl -s https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest) || {
|
43
|
+
echo -e "${RED}❌ Cannot get latest version information${NC}"
|
44
|
+
exit 1
|
45
|
+
}
|
46
|
+
|
47
|
+
VERSION=$(echo "$latest_release" | grep -o '"tag_name": ".*"' | cut -d'"' -f4 | tr -d 'v')
|
48
|
+
if [ -z "$VERSION" ]; then
|
49
|
+
echo -e "${RED}❌ Failed to parse version from GitHub API response:\n${latest_release}"
|
50
|
+
exit 1
|
51
|
+
fi
|
52
|
+
|
53
|
+
echo -e "${GREEN}✅ Found latest version: ${VERSION}${NC}"
|
54
|
+
}
|
55
|
+
|
56
|
+
# Detect system type and architecture
|
57
|
+
detect_os() {
|
58
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
59
|
+
# Detect macOS architecture
|
60
|
+
ARCH=$(uname -m)
|
61
|
+
if [[ "$ARCH" == "arm64" ]]; then
|
62
|
+
OS="mac_arm64"
|
63
|
+
echo -e "${CYAN}ℹ️ Detected macOS ARM64 architecture${NC}"
|
64
|
+
else
|
65
|
+
OS="mac_intel"
|
66
|
+
echo -e "${CYAN}ℹ️ Detected macOS Intel architecture${NC}"
|
67
|
+
fi
|
68
|
+
elif [[ "$(uname)" == "Linux" ]]; then
|
69
|
+
# Detect Linux architecture
|
70
|
+
ARCH=$(uname -m)
|
71
|
+
if [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then
|
72
|
+
OS="linux_arm64"
|
73
|
+
echo -e "${CYAN}ℹ️ Detected Linux ARM64 architecture${NC}"
|
74
|
+
else
|
75
|
+
OS="linux_x64"
|
76
|
+
echo -e "${CYAN}ℹ️ Detected Linux x64 architecture${NC}"
|
77
|
+
fi
|
78
|
+
else
|
79
|
+
# Assume Windows
|
80
|
+
OS="windows"
|
81
|
+
echo -e "${CYAN}ℹ️ Detected Windows system${NC}"
|
82
|
+
fi
|
83
|
+
}
|
84
|
+
|
85
|
+
# Install and download
|
86
|
+
install_cursor_free_vip() {
|
87
|
+
local downloads_dir=$(get_downloads_dir)
|
88
|
+
local binary_name="CursorFreeVIP_${VERSION}_${OS}"
|
89
|
+
local binary_path="${downloads_dir}/${binary_name}"
|
90
|
+
local download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
|
91
|
+
|
92
|
+
# Check if file already exists
|
93
|
+
if [ -f "${binary_path}" ]; then
|
94
|
+
echo -e "${GREEN}✅ Found existing installation file${NC}"
|
95
|
+
echo -e "${CYAN}ℹ️ Location: ${binary_path}${NC}"
|
96
|
+
|
97
|
+
# Check if running as root
|
98
|
+
if [ "$EUID" -ne 0 ]; then
|
99
|
+
echo -e "${YELLOW}⚠️ Requesting administrator privileges...${NC}"
|
100
|
+
if command -v sudo >/dev/null 2>&1; then
|
101
|
+
echo -e "${CYAN}ℹ️ Starting program with sudo...${NC}"
|
102
|
+
sudo chmod +x "${binary_path}"
|
103
|
+
sudo "${binary_path}"
|
104
|
+
else
|
105
|
+
echo -e "${YELLOW}⚠️ sudo not found, trying to run normally...${NC}"
|
106
|
+
chmod +x "${binary_path}"
|
107
|
+
"${binary_path}"
|
108
|
+
fi
|
109
|
+
else
|
110
|
+
# Already running as root
|
111
|
+
echo -e "${CYAN}ℹ️ Already running as root, starting program...${NC}"
|
112
|
+
chmod +x "${binary_path}"
|
113
|
+
"${binary_path}"
|
114
|
+
fi
|
115
|
+
return
|
116
|
+
fi
|
117
|
+
|
118
|
+
echo -e "${CYAN}ℹ️ No existing installation file found, starting download...${NC}"
|
119
|
+
echo -e "${CYAN}ℹ️ Downloading to ${downloads_dir}...${NC}"
|
120
|
+
echo -e "${CYAN}ℹ️ Download link: ${download_url}${NC}"
|
121
|
+
|
122
|
+
# Check if file exists
|
123
|
+
if curl --output /dev/null --silent --head --fail "$download_url"; then
|
124
|
+
echo -e "${GREEN}✅ File exists, starting download...${NC}"
|
125
|
+
else
|
126
|
+
echo -e "${RED}❌ Download link does not exist: ${download_url}${NC}"
|
127
|
+
echo -e "${YELLOW}⚠️ Trying without architecture...${NC}"
|
128
|
+
|
129
|
+
# Try without architecture
|
130
|
+
if [[ "$OS" == "mac_arm64" || "$OS" == "mac_intel" ]]; then
|
131
|
+
OS="mac"
|
132
|
+
binary_name="CursorFreeVIP_${VERSION}_${OS}"
|
133
|
+
download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
|
134
|
+
echo -e "${CYAN}ℹ️ New download link: ${download_url}${NC}"
|
135
|
+
|
136
|
+
if ! curl --output /dev/null --silent --head --fail "$download_url"; then
|
137
|
+
echo -e "${RED}❌ New download link does not exist${NC}"
|
138
|
+
exit 1
|
139
|
+
fi
|
140
|
+
elif [[ "$OS" == "linux_x64" || "$OS" == "linux_arm64" ]]; then
|
141
|
+
OS="linux"
|
142
|
+
binary_name="CursorFreeVIP_${VERSION}_${OS}"
|
143
|
+
download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
|
144
|
+
echo -e "${CYAN}ℹ️ New download link: ${download_url}${NC}"
|
145
|
+
|
146
|
+
if ! curl --output /dev/null --silent --head --fail "$download_url"; then
|
147
|
+
echo -e "${RED}❌ New download link does not exist${NC}"
|
148
|
+
exit 1
|
149
|
+
fi
|
150
|
+
else
|
151
|
+
exit 1
|
152
|
+
fi
|
153
|
+
fi
|
154
|
+
|
155
|
+
# Download file
|
156
|
+
if ! curl -L -o "${binary_path}" "$download_url"; then
|
157
|
+
echo -e "${RED}❌ Download failed${NC}"
|
158
|
+
exit 1
|
159
|
+
fi
|
160
|
+
|
161
|
+
# Check downloaded file size
|
162
|
+
local file_size=$(stat -f%z "${binary_path}" 2>/dev/null || stat -c%s "${binary_path}" 2>/dev/null)
|
163
|
+
echo -e "${CYAN}ℹ️ Downloaded file size: ${file_size} bytes${NC}"
|
164
|
+
|
165
|
+
# If file is too small, it might be an error message
|
166
|
+
if [ "$file_size" -lt 1000 ]; then
|
167
|
+
echo -e "${YELLOW}⚠️ Warning: Downloaded file is too small, possibly not a valid executable file${NC}"
|
168
|
+
echo -e "${YELLOW}⚠️ File content:${NC}"
|
169
|
+
cat "${binary_path}"
|
170
|
+
echo ""
|
171
|
+
echo -e "${RED}❌ Download failed, please check version and operating system${NC}"
|
172
|
+
exit 1
|
173
|
+
fi
|
174
|
+
|
175
|
+
echo -e "${CYAN}ℹ️ Setting executable permissions...${NC}"
|
176
|
+
if chmod +x "${binary_path}"; then
|
177
|
+
echo -e "${GREEN}✅ Installation completed!${NC}"
|
178
|
+
echo -e "${CYAN}ℹ️ Program downloaded to: ${binary_path}${NC}"
|
179
|
+
echo -e "${CYAN}ℹ️ Starting program...${NC}"
|
180
|
+
|
181
|
+
# Run program directly
|
182
|
+
"${binary_path}"
|
183
|
+
else
|
184
|
+
echo -e "${RED}❌ Installation failed${NC}"
|
185
|
+
exit 1
|
186
|
+
fi
|
187
|
+
}
|
188
|
+
|
189
|
+
# Main program
|
190
|
+
main() {
|
191
|
+
print_logo
|
192
|
+
get_latest_version
|
193
|
+
detect_os
|
194
|
+
install_cursor_free_vip
|
195
|
+
}
|
196
|
+
|
197
|
+
# Run main program
|
198
|
+
main
|
package/package.json
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
{
|
2
|
+
"name": "saafe-redirection-flow",
|
3
|
+
"version": "2.0.0",
|
4
|
+
"type": "module",
|
5
|
+
"scripts": {
|
6
|
+
"dev": "vite",
|
7
|
+
"build": "tsc -b && vite build",
|
8
|
+
"build:production": "vite build --mode production",
|
9
|
+
"build:stage": "vite build --mode stage",
|
10
|
+
"build:sandbox": "vite build --mode sandbox",
|
11
|
+
"build:gh": "vite build --base=/saafe-redirection-flow/",
|
12
|
+
"lint": "eslint .",
|
13
|
+
"preview": "vite preview",
|
14
|
+
"preview:production": "vite preview --mode production",
|
15
|
+
"preview:stage": "vite preview --mode stage",
|
16
|
+
"preview:sandbox": "vite preview --mode sandbox",
|
17
|
+
"storybook": "storybook dev -p 6006",
|
18
|
+
"build-storybook": "storybook build",
|
19
|
+
"semantic-release": "semantic-release",
|
20
|
+
"semantic-release:dry-run": "semantic-release --dry-run",
|
21
|
+
"release": "npm run semantic-release",
|
22
|
+
"release:dry": "npm run semantic-release:dry-run",
|
23
|
+
"version:check": "echo \"Current version: $(node -p 'require(\"./package.json\").version')\"",
|
24
|
+
"env:check": "node -e \"console.log('Environment:', process.env.NODE_ENV || 'development'); console.log('Available env vars:', Object.keys(process.env).filter(k => k.startsWith('VITE_')).join(', '))\"",
|
25
|
+
"env:list": "ls -la .env*",
|
26
|
+
"build:all": "npm run build:production && npm run build:stage && npm run build:sandbox",
|
27
|
+
"release:real": "semantic-release --no-ci",
|
28
|
+
"release:prod": "git checkout prod && npm run release:real"
|
29
|
+
},
|
30
|
+
"dependencies": {
|
31
|
+
"@hookform/resolvers": "^4.1.3",
|
32
|
+
"@radix-ui/react-avatar": "^1.1.3",
|
33
|
+
"@radix-ui/react-checkbox": "^1.1.4",
|
34
|
+
"@radix-ui/react-collapsible": "^1.1.3",
|
35
|
+
"@radix-ui/react-dialog": "^1.1.6",
|
36
|
+
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
37
|
+
"@radix-ui/react-icons": "^1.3.2",
|
38
|
+
"@radix-ui/react-label": "^2.1.2",
|
39
|
+
"@radix-ui/react-popover": "^1.1.6",
|
40
|
+
"@radix-ui/react-radio-group": "^1.2.3",
|
41
|
+
"@radix-ui/react-scroll-area": "^1.2.3",
|
42
|
+
"@radix-ui/react-select": "^2.1.7",
|
43
|
+
"@radix-ui/react-separator": "^1.1.2",
|
44
|
+
"@radix-ui/react-slot": "^1.2.0",
|
45
|
+
"@radix-ui/react-tabs": "^1.1.3",
|
46
|
+
"@radix-ui/react-tooltip": "^1.1.8",
|
47
|
+
"@tailwindcss/vite": "^4.0.14",
|
48
|
+
"@tanstack/react-query": "^5.71.5",
|
49
|
+
"@tanstack/react-query-devtools": "^5.71.5",
|
50
|
+
"axios": "^1.8.4",
|
51
|
+
"class-variance-authority": "^0.7.1",
|
52
|
+
"clsx": "^2.1.1",
|
53
|
+
"framer-motion": "^12.6.0",
|
54
|
+
"i18next": "^24.2.3",
|
55
|
+
"i18next-browser-languagedetector": "^8.0.4",
|
56
|
+
"input-otp": "^1.4.2",
|
57
|
+
"lucide-react": "^0.482.0",
|
58
|
+
"next-themes": "^0.4.6",
|
59
|
+
"posthog-js": "^1.234.10",
|
60
|
+
"react": "^19.0.0",
|
61
|
+
"react-day-picker": "^9.6.7",
|
62
|
+
"react-dom": "^19.0.0",
|
63
|
+
"react-hook-form": "^7.54.2",
|
64
|
+
"react-i18next": "^15.4.1",
|
65
|
+
"react-router-dom": "^7.4.0",
|
66
|
+
"sonner": "^2.0.3",
|
67
|
+
"tailwind-merge": "^3.0.2",
|
68
|
+
"tailwindcss": "^4.0.14",
|
69
|
+
"tailwindcss-animate": "^1.0.7",
|
70
|
+
"zod": "^3.24.2",
|
71
|
+
"zustand": "^5.0.3"
|
72
|
+
},
|
73
|
+
"devDependencies": {
|
74
|
+
"@chromatic-com/storybook": "^3.2.6",
|
75
|
+
"@eslint/js": "^9.21.0",
|
76
|
+
"@semantic-release/changelog": "^6.0.3",
|
77
|
+
"@semantic-release/git": "^10.0.1",
|
78
|
+
"@semantic-release/npm": "^12.0.1",
|
79
|
+
"@storybook/addon-essentials": "^8.6.10",
|
80
|
+
"@storybook/addon-interactions": "^8.6.10",
|
81
|
+
"@storybook/addon-links": "^8.6.10",
|
82
|
+
"@storybook/addon-onboarding": "^8.6.10",
|
83
|
+
"@storybook/blocks": "^8.6.10",
|
84
|
+
"@storybook/experimental-addon-test": "^8.6.10",
|
85
|
+
"@storybook/react": "^8.6.10",
|
86
|
+
"@storybook/react-vite": "^8.6.10",
|
87
|
+
"@storybook/test": "^8.6.10",
|
88
|
+
"@storybook/testing-library": "^0.2.2",
|
89
|
+
"@types/node": "^22.13.10",
|
90
|
+
"@types/react": "^19.0.10",
|
91
|
+
"@types/react-dom": "^19.0.4",
|
92
|
+
"@vitejs/plugin-react": "^4.3.4",
|
93
|
+
"@vitest/browser": "^3.0.9",
|
94
|
+
"@vitest/coverage-v8": "^3.0.9",
|
95
|
+
"cross-env": "^7.0.3",
|
96
|
+
"eslint": "^9.21.0",
|
97
|
+
"eslint-plugin-react-hooks": "^5.1.0",
|
98
|
+
"eslint-plugin-react-refresh": "^0.4.19",
|
99
|
+
"eslint-plugin-storybook": "^0.12.0",
|
100
|
+
"globals": "^15.15.0",
|
101
|
+
"playwright": "^1.51.1",
|
102
|
+
"react-docgen-typescript": "^2.2.2",
|
103
|
+
"semantic-release": "^24.2.5",
|
104
|
+
"storybook": "^8.6.10",
|
105
|
+
"typescript": "~5.7.2",
|
106
|
+
"typescript-eslint": "^8.24.1",
|
107
|
+
"vite": "^6.2.0",
|
108
|
+
"vitest": "^3.0.9"
|
109
|
+
},
|
110
|
+
"eslintConfig": {
|
111
|
+
"extends": [
|
112
|
+
"plugin:storybook/recommended"
|
113
|
+
]
|
114
|
+
}
|
115
|
+
}
|
Binary file
|