From c70dd11d18df9073785df0c8924c5605c21b2625 Mon Sep 17 00:00:00 2001 From: k1nq Date: Tue, 22 Sep 2026 08:29:49 +0500 Subject: [PATCH] feat: redesign secure access flows --- frontend/package.json | 2 + frontend/pnpm-lock.yaml | 154 ++++++++++-------- frontend/src/components/layout/AuthLayout.tsx | 51 ++++++ frontend/src/components/ui/Modal.tsx | 2 +- frontend/src/i18n/locales/en.json | 13 ++ frontend/src/i18n/locales/ru.json | 13 ++ frontend/src/main.tsx | 8 + frontend/src/pages/AcceptInvitePage.tsx | 21 +-- frontend/src/pages/LoginPage.tsx | 21 +-- frontend/src/pages/ResetPasswordPage.tsx | 21 +-- frontend/src/styles/global.css | 87 ++++------ 11 files changed, 228 insertions(+), 165 deletions(-) create mode 100644 frontend/src/components/layout/AuthLayout.tsx diff --git a/frontend/package.json b/frontend/package.json index dabbd76..f43ed1e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,6 +14,8 @@ "api:generate": "node scripts/generate-api.mjs" }, "dependencies": { + "@fontsource/funnel-sans": "^5.3.0", + "@fontsource/geist-mono": "^5.3.0", "@hookform/resolvers": "^5.1.0", "@tanstack/react-query": "^5.80.0", "i18next": "^25.2.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index a0b7ed2..7d614fa 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -8,6 +8,12 @@ importers: .: dependencies: + '@fontsource/funnel-sans': + specifier: ^5.3.0 + version: 5.3.0 + '@fontsource/geist-mono': + specifier: ^5.3.0 + version: 5.3.0 '@hookform/resolvers': specifier: ^5.1.0 version: 5.9.1(react-hook-form@7.87.0(react@19.3.0))(zod@3.25.76) @@ -59,22 +65,22 @@ importers: version: 19.3.0(@types/react@19.3.0) '@vitejs/plugin-react': specifier: ^4.5.0 - version: 4.7.0(vite@6.4.3) + version: 4.7.0(supports-color@10.2.2)(vite@6.4.3) eslint: specifier: ^9.28.0 - version: 9.39.5 + version: 9.39.5(supports-color@10.2.2) eslint-plugin-jsx-a11y: specifier: ^6.10.2 - version: 6.10.2(eslint@9.39.5) + version: 6.10.2(eslint@9.39.5(supports-color@10.2.2)) eslint-plugin-react-hooks: specifier: ^5.2.0 - version: 5.2.0(eslint@9.39.5) + version: 5.2.0(eslint@9.39.5(supports-color@10.2.2)) globals: specifier: ^16.2.0 version: 16.5.0 jsdom: specifier: ^26.1.0 - version: 26.1.0 + version: 26.1.0(supports-color@10.2.2) openapi-typescript: specifier: ^7.8.0 version: 7.13.0(typescript@5.8.3) @@ -83,13 +89,13 @@ importers: version: 5.8.3 typescript-eslint: specifier: ^8.33.0 - version: 8.70.0(eslint@9.39.5)(typescript@5.8.3) + version: 8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3) vite: specifier: ^6.3.5 version: 6.4.3 vitest: specifier: ^3.2.0 - version: 3.2.7(jsdom@26.1.0) + version: 3.2.7(jsdom@26.1.0(supports-color@10.2.2))(supports-color@10.2.2) packages: @@ -408,6 +414,12 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@fontsource/funnel-sans@5.3.0': + resolution: {integrity: sha512-LmINq/6qfoYW1LOkOwaiZw7YVEpi3QACa36V7jxgBlO8MPGPbeSxFrPjk4lF6uP1ZbCp2VsBWmneo+1b59GbbA==} + + '@fontsource/geist-mono@5.3.0': + resolution: {integrity: sha512-UtJ1BBBCVpMYdIcW7nEB45UAoAw5M53ZXs2t0ciPW+IokuAAIc56M8+kW5tXbRJCTpDw4XTtU7proT6NdQAHTg==} + '@hookform/resolvers@5.9.1': resolution: {integrity: sha512-7b7vsbraJxKgjVSA1Nur9tLwj539WGJUBLA7QNvXnFoT2pM5Z7G+6rlukk4B2/QrTZy6huRtH6wKeESPKuIr6w==} peerDependencies: @@ -2195,16 +2207,16 @@ snapshots: '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.7': + '@babel/core@7.29.7(supports-color@10.2.2)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.8 '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helpers': 7.29.7 '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) '@babel/types': 7.29.8 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 @@ -2233,19 +2245,19 @@ snapshots: '@babel/helper-globals@7.29.7': {} - '@babel/helper-module-imports@7.29.7': + '@babel/helper-module-imports@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -2266,14 +2278,14 @@ snapshots: dependencies: '@babel/types': 7.29.8 - '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 '@babel/runtime@7.29.7': {} @@ -2284,7 +2296,7 @@ snapshots: '@babel/parser': 7.29.8 '@babel/types': 7.29.8 - '@babel/traverse@7.29.8': + '@babel/traverse@7.29.8(supports-color@10.2.2)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.8 @@ -2399,14 +2411,14 @@ snapshots: '@esbuild/win32-x64@0.25.12': optional: true - '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5)': + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(supports-color@10.2.2))': dependencies: - eslint: 9.39.5 + eslint: 9.39.5(supports-color@10.2.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.2': + '@eslint/config-array@0.21.2(supports-color@10.2.2)': dependencies: '@eslint/object-schema': 2.1.7 debug: 4.4.3(supports-color@10.2.2) @@ -2422,7 +2434,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.7': + '@eslint/eslintrc@3.3.7(supports-color@10.2.2)': dependencies: ajv: 6.15.0 debug: 4.4.3(supports-color@10.2.2) @@ -2445,6 +2457,10 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@fontsource/funnel-sans@5.3.0': {} + + '@fontsource/geist-mono@5.3.0': {} + '@hookform/resolvers@5.9.1(react-hook-form@7.87.0(react@19.3.0))(zod@3.25.76)': dependencies: '@standard-schema/utils': 0.3.0 @@ -2676,15 +2692,15 @@ snapshots: dependencies: csstype: 3.2.3 - '@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3))(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.70.0(eslint@9.39.5)(typescript@5.8.3) + '@typescript-eslint/parser': 8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3) '@typescript-eslint/scope-manager': 8.70.0 - '@typescript-eslint/type-utils': 8.70.0(eslint@9.39.5)(typescript@5.8.3) - '@typescript-eslint/utils': 8.70.0(eslint@9.39.5)(typescript@5.8.3) + '@typescript-eslint/type-utils': 8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3) + '@typescript-eslint/utils': 8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.70.0 - eslint: 9.39.5 + eslint: 9.39.5(supports-color@10.2.2) ignore: 7.0.9 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@5.8.3) @@ -2692,19 +2708,19 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.8.3)': + '@typescript-eslint/parser@8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3)': dependencies: '@typescript-eslint/scope-manager': 8.70.0 '@typescript-eslint/types': 8.70.0 - '@typescript-eslint/typescript-estree': 8.70.0(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.70.0(supports-color@10.2.2)(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.70.0 debug: 4.4.3(supports-color@10.2.2) - eslint: 9.39.5 + eslint: 9.39.5(supports-color@10.2.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.70.0(typescript@5.8.3)': + '@typescript-eslint/project-service@8.70.0(supports-color@10.2.2)(typescript@5.8.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.70.0(typescript@5.8.3) '@typescript-eslint/types': 8.70.0 @@ -2722,13 +2738,13 @@ snapshots: dependencies: typescript: 5.8.3 - '@typescript-eslint/type-utils@8.70.0(eslint@9.39.5)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3)': dependencies: '@typescript-eslint/types': 8.70.0 - '@typescript-eslint/typescript-estree': 8.70.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.70.0(eslint@9.39.5)(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.70.0(supports-color@10.2.2)(typescript@5.8.3) + '@typescript-eslint/utils': 8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3) debug: 4.4.3(supports-color@10.2.2) - eslint: 9.39.5 + eslint: 9.39.5(supports-color@10.2.2) ts-api-utils: 2.5.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: @@ -2736,9 +2752,9 @@ snapshots: '@typescript-eslint/types@8.70.0': {} - '@typescript-eslint/typescript-estree@8.70.0(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.70.0(supports-color@10.2.2)(typescript@5.8.3)': dependencies: - '@typescript-eslint/project-service': 8.70.0(typescript@5.8.3) + '@typescript-eslint/project-service': 8.70.0(supports-color@10.2.2)(typescript@5.8.3) '@typescript-eslint/tsconfig-utils': 8.70.0(typescript@5.8.3) '@typescript-eslint/types': 8.70.0 '@typescript-eslint/visitor-keys': 8.70.0 @@ -2751,13 +2767,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.70.0(eslint@9.39.5)(typescript@5.8.3)': + '@typescript-eslint/utils@8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(supports-color@10.2.2)) '@typescript-eslint/scope-manager': 8.70.0 '@typescript-eslint/types': 8.70.0 - '@typescript-eslint/typescript-estree': 8.70.0(typescript@5.8.3) - eslint: 9.39.5 + '@typescript-eslint/typescript-estree': 8.70.0(supports-color@10.2.2)(typescript@5.8.3) + eslint: 9.39.5(supports-color@10.2.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -2767,11 +2783,11 @@ snapshots: '@typescript-eslint/types': 8.70.0 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-react@4.7.0(vite@6.4.3)': + '@vitejs/plugin-react@4.7.0(supports-color@10.2.2)(vite@6.4.3)': dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 @@ -3198,7 +3214,7 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.5): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.5(supports-color@10.2.2)): dependencies: aria-query: 5.3.2 array-includes: 3.2.0 @@ -3208,7 +3224,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.39.5 + eslint: 9.39.5(supports-color@10.2.2) hasown: 2.0.4 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -3217,9 +3233,9 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@5.2.0(eslint@9.39.5): + eslint-plugin-react-hooks@5.2.0(eslint@9.39.5(supports-color@10.2.2)): dependencies: - eslint: 9.39.5 + eslint: 9.39.5(supports-color@10.2.2) eslint-scope@8.4.0: dependencies: @@ -3232,14 +3248,14 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@9.39.5: + eslint@9.39.5(supports-color@10.2.2): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(supports-color@10.2.2)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.2 + '@eslint/config-array': 0.21.2(supports-color@10.2.2) '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.7 + '@eslint/eslintrc': 3.3.7(supports-color@10.2.2) '@eslint/js': 9.39.5 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.8 @@ -3417,7 +3433,7 @@ snapshots: dependencies: void-elements: 3.1.0 - http-proxy-agent@7.0.2: + http-proxy-agent@7.0.2(supports-color@10.2.2): dependencies: agent-base: 7.1.4 debug: 4.4.3(supports-color@10.2.2) @@ -3584,13 +3600,13 @@ snapshots: dependencies: argparse: 2.0.1 - jsdom@26.1.0: + jsdom@26.1.0(supports-color@10.2.2): dependencies: cssstyle: 4.6.0 data-urls: 5.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.2 + http-proxy-agent: 7.0.2(supports-color@10.2.2) https-proxy-agent: 7.0.6(supports-color@10.2.2) is-potential-custom-element-name: 1.0.1 nwsapi: 2.2.27 @@ -4122,13 +4138,13 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.70.0(eslint@9.39.5)(typescript@5.8.3): + typescript-eslint@8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.70.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3) - '@typescript-eslint/parser': 8.70.0(eslint@9.39.5)(typescript@5.8.3) - '@typescript-eslint/typescript-estree': 8.70.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.70.0(eslint@9.39.5)(typescript@5.8.3) - eslint: 9.39.5 + '@typescript-eslint/eslint-plugin': 8.70.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3))(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3) + '@typescript-eslint/parser': 8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.70.0(supports-color@10.2.2)(typescript@5.8.3) + '@typescript-eslint/utils': 8.70.0(eslint@9.39.5(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.8.3) + eslint: 9.39.5(supports-color@10.2.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -4154,7 +4170,7 @@ snapshots: dependencies: punycode: 2.3.1 - vite-node@3.2.4: + vite-node@3.2.4(supports-color@10.2.2): dependencies: cac: 6.7.14 debug: 4.4.3(supports-color@10.2.2) @@ -4186,7 +4202,7 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - vitest@3.2.7(jsdom@26.1.0): + vitest@3.2.7(jsdom@26.1.0(supports-color@10.2.2))(supports-color@10.2.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.7 @@ -4209,10 +4225,10 @@ snapshots: tinypool: 1.1.1 tinyrainbow: 2.0.0 vite: 6.4.3 - vite-node: 3.2.4 + vite-node: 3.2.4(supports-color@10.2.2) why-is-node-running: 2.3.0 optionalDependencies: - jsdom: 26.1.0 + jsdom: 26.1.0(supports-color@10.2.2) transitivePeerDependencies: - jiti - less diff --git a/frontend/src/components/layout/AuthLayout.tsx b/frontend/src/components/layout/AuthLayout.tsx new file mode 100644 index 0000000..00767cc --- /dev/null +++ b/frontend/src/components/layout/AuthLayout.tsx @@ -0,0 +1,51 @@ +import type { ReactNode } from 'react' +import { Link } from 'react-router-dom' +import { useTranslation } from 'react-i18next' +import { LangSwitcher } from '@/components/layout/LangSwitcher' + +interface AuthLayoutProps { + title: string + subtitle: string + context: string + children: ReactNode +} + +export function AuthLayout({ title, subtitle, context, children }: AuthLayoutProps) { + const { t } = useTranslation() + + return ( +
+ +
+
+ {context} + +
+
+

{title}

+

{subtitle}

+ {children} +
+
+ {t('authShell.private')} + {t('authShell.singleUse')} + {t('authShell.rub')} +
+
+
+ ) +} diff --git a/frontend/src/components/ui/Modal.tsx b/frontend/src/components/ui/Modal.tsx index 647560f..24a28cc 100644 --- a/frontend/src/components/ui/Modal.tsx +++ b/frontend/src/components/ui/Modal.tsx @@ -42,7 +42,7 @@ export function Modal({ open, onClose, title, children }: ModalProps) { onClick={onClose} aria-label={t('common.close')} > - ✕ +