From 638de063477e434a64f0e70298d204279d6f0faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C5=9Al=C4=99zak?= Date: Fri, 8 Aug 2025 07:43:29 +0200 Subject: [PATCH 1/2] add qr to enrollment modal --- web/package.json | 2 +- web/pnpm-lock.yaml | 30 +++---- .../addDevice/utils/enrollmentToToken.ts | 25 +----- web/src/pages/loader/style.scss | 2 +- .../EnrollmentTokenCard.tsx | 81 +++++++------------ .../components/EnrollmentTokenCard/style.scss | 13 +-- web/src/shared/scss/base/_base.scss | 3 +- 7 files changed, 57 insertions(+), 99 deletions(-) diff --git a/web/package.json b/web/package.json index 40417bb1e0..e59dbb3897 100644 --- a/web/package.json +++ b/web/package.json @@ -140,7 +140,7 @@ "standard-version": "^9.5.0", "type-fest": "^4.41.0", "typescript": "~5.9.2", - "vite": "^7.1.0", + "vite": "^7.1.1", "vite-plugin-package-version": "^1.1.0" } } diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index c588e235fd..32f1544d00 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -263,7 +263,7 @@ importers: version: 1.8.8 '@vitejs/plugin-react-swc': specifier: ^3.11.0 - version: 3.11.0(vite@7.1.0(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)) + version: 3.11.0(vite@7.1.1(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)) autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.6) @@ -298,11 +298,11 @@ importers: specifier: ~5.9.2 version: 5.9.2 vite: - specifier: ^7.1.0 - version: 7.1.0(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) + specifier: ^7.1.1 + version: 7.1.1(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) vite-plugin-package-version: specifier: ^1.1.0 - version: 1.1.0(vite@7.1.0(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)) + version: 1.1.0(vite@7.1.1(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)) packages: @@ -1530,8 +1530,8 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - electron-to-chromium@1.5.198: - resolution: {integrity: sha512-G5COfnp3w+ydVu80yprgWSfmfQaYRh9DOxfhAxstLyetKaLyl55QrNjx8C38Pc/C+RaDmb1M0Lk8wPEMQ+bGgQ==} + electron-to-chromium@1.5.199: + resolution: {integrity: sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -2939,8 +2939,8 @@ packages: peerDependencies: vite: '>=2.0.0-beta.69' - vite@7.1.0: - resolution: {integrity: sha512-3jdAy3NhBJYsa/lCFcnRfbK4kNkO/bhijFCnv5ByUQk/eekYagoV2yQSISUrhpV+5JiY5hmwOh7jNnQ68dFMuQ==} + vite@7.1.1: + resolution: {integrity: sha512-yJ+Mp7OyV+4S+afWo+QyoL9jFWD11QFH0i5i7JypnfTcA1rmgxCbiA8WwAICDEtZ1Z1hzrVhN8R8rGTqkTY8ZQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3773,11 +3773,11 @@ snapshots: '@use-gesture/core': 10.3.1 react: 18.3.1 - '@vitejs/plugin-react-swc@3.11.0(vite@7.1.0(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1))': + '@vitejs/plugin-react-swc@3.11.0(vite@7.1.1(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.27 '@swc/core': 1.13.3 - vite: 7.1.0(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) + vite: 7.1.1(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) transitivePeerDependencies: - '@swc/helpers' @@ -3856,7 +3856,7 @@ snapshots: browserslist@4.25.1: dependencies: caniuse-lite: 1.0.30001731 - electron-to-chromium: 1.5.198 + electron-to-chromium: 1.5.199 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.1) @@ -4234,7 +4234,7 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - electron-to-chromium@1.5.198: {} + electron-to-chromium@1.5.199: {} emoji-regex@8.0.0: {} @@ -5860,11 +5860,11 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-plugin-package-version@1.1.0(vite@7.1.0(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)): + vite-plugin-package-version@1.1.0(vite@7.1.1(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)): dependencies: - vite: 7.1.0(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) + vite: 7.1.1(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) - vite@7.1.0(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1): + vite@7.1.1(@types/node@24.2.0)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1): dependencies: esbuild: 0.25.8 fdir: 6.4.6(picomatch@4.0.3) diff --git a/web/src/pages/addDevice/utils/enrollmentToToken.ts b/web/src/pages/addDevice/utils/enrollmentToToken.ts index b926e816e1..5d3cdf52d5 100644 --- a/web/src/pages/addDevice/utils/enrollmentToToken.ts +++ b/web/src/pages/addDevice/utils/enrollmentToToken.ts @@ -5,33 +5,10 @@ export type EnrollmentData = { token: string; }; -const useLocalProxy = import.meta.env.DEV; - -const extractProxyPort = (input: string): string | undefined => { - try { - const url = new URL(input); - const port = url.port; - const parsed = port ? parseInt(port, 10) : undefined; - if (parsed && !Number.isNaN(parsed)) { - return `:${parsed}`; - } - return undefined; - } catch { - return undefined; - } -}; - export const enrollmentToImportToken = (url: string, token: string): string => { - let proxyUrl: string; - if (useLocalProxy) { - const port = extractProxyPort(url); - proxyUrl = `http://10.0.2.2${port}`; - } else { - proxyUrl = url; - } const data: EnrollmentData = { token, - url: proxyUrl, + url, }; const jsonString = JSON.stringify(data); const textEncoder = new TextEncoder(); diff --git a/web/src/pages/loader/style.scss b/web/src/pages/loader/style.scss index a9eaa8b511..ec84297561 100644 --- a/web/src/pages/loader/style.scss +++ b/web/src/pages/loader/style.scss @@ -1,6 +1,6 @@ #loader-page { + min-height: 100dvh; width: 100%; - height: 100%; display: flex; flex-direction: column; align-content: center; diff --git a/web/src/pages/users/UsersOverview/modals/AddUserModal/components/EnrollmentTokenCard/EnrollmentTokenCard.tsx b/web/src/pages/users/UsersOverview/modals/AddUserModal/components/EnrollmentTokenCard/EnrollmentTokenCard.tsx index 6f44468fc1..98d050936c 100644 --- a/web/src/pages/users/UsersOverview/modals/AddUserModal/components/EnrollmentTokenCard/EnrollmentTokenCard.tsx +++ b/web/src/pages/users/UsersOverview/modals/AddUserModal/components/EnrollmentTokenCard/EnrollmentTokenCard.tsx @@ -1,18 +1,17 @@ import './style.scss'; -import { isUndefined } from 'lodash-es'; -import { type ReactNode, useMemo } from 'react'; - +import { useMemo } from 'react'; +import QRCode from 'react-qr-code'; import { useI18nContext } from '../../../../../../../i18n/i18n-react'; -import { ActionButton } from '../../../../../../../shared/defguard-ui/components/Layout/ActionButton/ActionButton'; -import { ActionButtonVariant } from '../../../../../../../shared/defguard-ui/components/Layout/ActionButton/types'; import { Button } from '../../../../../../../shared/defguard-ui/components/Layout/Button/Button'; import { ButtonSize, ButtonStyleVariant, } from '../../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { ExpandableCard } from '../../../../../../../shared/defguard-ui/components/Layout/ExpandableCard/ExpandableCard'; +import { CopyField } from '../../../../../../../shared/defguard-ui/components/Layout/CopyField/CopyField'; +import { isPresent } from '../../../../../../../shared/defguard-ui/utils/isPresent'; import { useClipboard } from '../../../../../../../shared/hooks/useClipboard'; +import { enrollmentToImportToken } from '../../../../../../addDevice/utils/enrollmentToToken'; import { useAddUserModal } from '../../hooks/useAddUserModal'; export const EnrollmentTokenCard = () => { @@ -21,56 +20,34 @@ export const EnrollmentTokenCard = () => { const { writeToClipboard } = useClipboard(); const closeModal = useAddUserModal((state) => state.close); - const tokenActions = useMemo( - (): ReactNode[] => [ - { - if (tokenResponse) { - void writeToClipboard(tokenResponse.enrollment_token); - } - }} - key={0} - />, - ], - [tokenResponse, writeToClipboard], - ); + const qrData = useMemo(() => { + if (tokenResponse) { + return enrollmentToImportToken( + tokenResponse.enrollment_url, + tokenResponse.enrollment_token, + ); + } + }, [tokenResponse]); - const urlActions = useMemo( - (): ReactNode[] => [ - { - if (tokenResponse) { - void writeToClipboard(tokenResponse.enrollment_url); - } - }} - key={0} - />, - ], - [tokenResponse, writeToClipboard], - ); + if (!isPresent(tokenResponse)) return null; return (
- -

{tokenResponse?.enrollment_url}

-
- -

{tokenResponse?.enrollment_token}

-
+ + + {isPresent(qrData) && ( +
+ +
+ )}
{% endmacro text_section %} +{% macro inline_image(src, height="100px", width="100px", alt="") %} +
+ + + + + + +
+
+ + + + + + +
+ + + + + + +
+
+ {{ alt }} +
+
+
+
+
+
+{% endmacro inline_image %} + {% macro paragraph(content="", color="#222", font_size="12px", align="left", line_height="120%", font_weight="400") %}