From 8fc15081e29e415f2087cbb699e4982614547ede Mon Sep 17 00:00:00 2001 From: elpatron Date: Mon, 1 Jun 2026 09:58:16 +0200 Subject: [PATCH] Show QR codes for invite and public share links. Generate scannable QR codes in settings next to collaboration links so crew can open invites on mobile without copying long URLs. Co-authored-by: Cursor --- client/package-lock.json | 42 ++++---------- client/package.json | 5 +- client/src/App.css | 32 +++++++++++ client/src/components/LinkQrCode.tsx | 54 ++++++++++++++++++ client/src/components/LiveLogView.tsx | 3 - client/src/components/SettingsForm.tsx | 77 ++++++++++++++------------ client/src/i18n/locales/da.json | 2 + client/src/i18n/locales/de.json | 2 + client/src/i18n/locales/en.json | 2 + client/src/i18n/locales/nb.json | 2 + client/src/i18n/locales/sv.json | 2 + 11 files changed, 154 insertions(+), 69 deletions(-) create mode 100644 client/src/components/LinkQrCode.tsx diff --git a/client/package-lock.json b/client/package-lock.json index d951186..b0dcfb2 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -17,6 +17,7 @@ "jspdf": "^4.2.1", "leaflet": "^1.9.4", "lucide-react": "^1.16.0", + "qrcode": "^1.5.4", "react": "^19.2.6", "react-dom": "^19.2.6", "react-i18next": "^17.0.8" @@ -25,6 +26,7 @@ "@eslint/js": "^10.0.1", "@types/leaflet": "^1.9.21", "@types/node": "^24.12.3", + "@types/qrcode": "^1.5.5", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^4.7.0", @@ -34,7 +36,6 @@ "globals": "^17.6.0", "happy-dom": "^20.9.0", "playwright": "^1.51.0", - "qrcode": "^1.5.4", "typescript": "~6.0.2", "typescript-eslint": "^8.59.2", "vite": "^6.3.5", @@ -2970,6 +2971,16 @@ "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", "license": "MIT" }, + "node_modules/@types/qrcode": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", + "integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/raf": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", @@ -3461,7 +3472,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3471,7 +3481,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -3777,7 +3786,6 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3855,7 +3863,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -3867,7 +3874,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -3880,7 +3886,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/commander": { @@ -4051,7 +4056,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4140,7 +4144,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", - "dev": true, "license": "MIT" }, "node_modules/dompurify": { @@ -4195,7 +4198,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/entities": { @@ -4948,7 +4950,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -5498,7 +5499,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6208,7 +6208,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -6231,7 +6230,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6376,7 +6374,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.13.0" @@ -6458,7 +6455,6 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", - "dev": true, "license": "MIT", "dependencies": { "dijkstrajs": "^1.0.1", @@ -6653,7 +6649,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6673,7 +6668,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true, "license": "ISC" }, "node_modules/resolve": { @@ -6845,7 +6839,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, "license": "ISC" }, "node_modules/set-function-length": { @@ -7113,7 +7106,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -7230,7 +7222,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -8067,7 +8058,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true, "license": "ISC" }, "node_modules/which-typed-array": { @@ -8343,7 +8333,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -8380,7 +8369,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, "license": "ISC" }, "node_modules/yallist": { @@ -8394,7 +8382,6 @@ "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, "license": "MIT", "dependencies": { "cliui": "^6.0.0", @@ -8417,7 +8404,6 @@ "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, "license": "ISC", "dependencies": { "camelcase": "^5.0.0", @@ -8431,7 +8417,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "license": "MIT", "dependencies": { "locate-path": "^5.0.0", @@ -8445,7 +8430,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "license": "MIT", "dependencies": { "p-locate": "^4.1.0" @@ -8458,7 +8442,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "license": "MIT", "dependencies": { "p-try": "^2.0.0" @@ -8474,7 +8457,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "license": "MIT", "dependencies": { "p-limit": "^2.2.0" diff --git a/client/package.json b/client/package.json index a9feb6b..7831a10 100644 --- a/client/package.json +++ b/client/package.json @@ -29,12 +29,14 @@ "lucide-react": "^1.16.0", "react": "^19.2.6", "react-dom": "^19.2.6", - "react-i18next": "^17.0.8" + "react-i18next": "^17.0.8", + "qrcode": "^1.5.4" }, "devDependencies": { "@eslint/js": "^10.0.1", "@types/leaflet": "^1.9.21", "@types/node": "^24.12.3", + "@types/qrcode": "^1.5.5", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^4.7.0", @@ -44,7 +46,6 @@ "globals": "^17.6.0", "happy-dom": "^20.9.0", "playwright": "^1.51.0", - "qrcode": "^1.5.4", "typescript": "~6.0.2", "typescript-eslint": "^8.59.2", "vite": "^6.3.5", diff --git a/client/src/App.css b/client/src/App.css index 54c49e6..e334014 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -1716,6 +1716,38 @@ html.scheme-dark .themed-select-option.is-selected { min-width: 0; } +.link-with-qr { + display: flex; + flex-direction: column; + gap: 16px; +} + +.link-qr-block { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + padding: 16px; + border-radius: var(--app-radius-card, 12px); + background: var(--app-surface-inset, rgba(0, 0, 0, 0.2)); + border: 1px solid var(--app-border-subtle); +} + +.link-qr-label { + margin: 0; + font-size: 13px; + color: var(--app-text-muted); + text-align: center; +} + +.link-qr-image { + display: block; + border-radius: 8px; + background: #fff; + padding: 8px; + box-sizing: content-box; +} + .form-actions--start { justify-content: flex-start; } diff --git a/client/src/components/LinkQrCode.tsx b/client/src/components/LinkQrCode.tsx new file mode 100644 index 0000000..62dcc3e --- /dev/null +++ b/client/src/components/LinkQrCode.tsx @@ -0,0 +1,54 @@ +import { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import QRCode from 'qrcode' + +interface LinkQrCodeProps { + value: string + size?: number +} + +export default function LinkQrCode({ value, size = 200 }: LinkQrCodeProps) { + const { t } = useTranslation() + const [dataUrl, setDataUrl] = useState(null) + + useEffect(() => { + if (!value.trim()) { + setDataUrl(null) + return + } + + let cancelled = false + void QRCode.toDataURL(value, { + width: size, + margin: 2, + errorCorrectionLevel: 'M', + color: { dark: '#0f172a', light: '#ffffff' } + }) + .then((url) => { + if (!cancelled) setDataUrl(url) + }) + .catch((err) => { + console.error('QR code generation failed:', err) + if (!cancelled) setDataUrl(null) + }) + + return () => { + cancelled = true + } + }, [value, size]) + + if (!value.trim() || !dataUrl) return null + + return ( +
+

{t('settings.link_qr_hint')}

+ {t('settings.link_qr_alt')} +
+ ) +} diff --git a/client/src/components/LiveLogView.tsx b/client/src/components/LiveLogView.tsx index 24c7f02..59dbac2 100644 --- a/client/src/components/LiveLogView.tsx +++ b/client/src/components/LiveLogView.tsx @@ -21,7 +21,6 @@ import { Zap } from 'lucide-react' import { db } from '../services/db.js' -import { getActiveMasterKey } from '../services/auth.js' import { getLogbookKey } from '../services/logbookKeys.js' import { decryptJson } from '../services/crypto.js' import { PlausibleEvents, trackPlausibleEvent } from '../services/analytics.js' @@ -66,8 +65,6 @@ import CourseDialInput from './CourseDialInput.tsx' import LiveCameraCapture from './LiveCameraCapture.tsx' import { saveEntryPhoto, deleteEntryPhoto } from '../services/photoAttachments.js' import { blobToCompressedJpegDataUrl } from '../utils/imageCompress.js' -import i18n from '../i18n/index.js' - interface LiveLogViewProps { logbookId: string onOpenEditor: (entryId: string) => void diff --git a/client/src/components/SettingsForm.tsx b/client/src/components/SettingsForm.tsx index 3a23669..b3796a0 100644 --- a/client/src/components/SettingsForm.tsx +++ b/client/src/components/SettingsForm.tsx @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next' import { Settings as SettingsIcon, Check, Users, Trash2, Copy, Link as LinkIcon } from 'lucide-react' import { ensureLogbookKey } from '../services/logbookKeys.js' import LogbookBackupPanel from './LogbookBackupPanel.tsx' +import LinkQrCode from './LinkQrCode.tsx' import { useDialog } from './ModalDialog.tsx' import { PlausibleEvents, trackPlausibleEvent } from '../services/analytics.js' import { apiFetch } from '../services/api.js' @@ -314,23 +315,27 @@ export default function SettingsForm({ logbookId, onLogbookRestored }: SettingsF {shareEnabled && shareLink && ( -
- (e.target as HTMLInputElement).select()} - /> - +
+
+ (e.target as HTMLInputElement).select()} + /> + +
+
)}
@@ -367,23 +372,27 @@ export default function SettingsForm({ logbookId, onLogbookRestored }: SettingsF {inviteLink && ( -
- (e.target as HTMLInputElement).select()} - /> - +
+
+ (e.target as HTMLInputElement).select()} + /> + +
+
)} diff --git a/client/src/i18n/locales/da.json b/client/src/i18n/locales/da.json index 0c060da..06ca2ca 100644 --- a/client/src/i18n/locales/da.json +++ b/client/src/i18n/locales/da.json @@ -633,6 +633,8 @@ "share_enable": "Aktivér offentligt link", "share_copied": "Link kopieret!", "share_copy_btn": "Kopier link", + "link_qr_hint": "Scan QR-koden med din telefon", + "link_qr_alt": "QR-kode til linket", "danger_zone_title": "Farezone", "danger_zone_desc": "Når du sletter din konto, slettes alle dine Passkey'er, logbøger, skibsdata, besætningsprofiler, rejseindlæg og E2E-nøgler uigenkaldeligt. Denne handling kan ikke fortrydes.", "delete_account_btn": "Slet konto uigenkaldeligt", diff --git a/client/src/i18n/locales/de.json b/client/src/i18n/locales/de.json index 679c3fc..8bd6ae2 100644 --- a/client/src/i18n/locales/de.json +++ b/client/src/i18n/locales/de.json @@ -633,6 +633,8 @@ "share_enable": "Öffentlichen Link aktivieren", "share_copied": "Link kopiert!", "share_copy_btn": "Link kopieren", + "link_qr_hint": "QR-Code zum Scannen mit dem Smartphone", + "link_qr_alt": "QR-Code für den Link", "danger_zone_title": "Gefahrenzone", "danger_zone_desc": "Durch das Löschen deines Kontos werden alle deine Passkeys, Logbücher, Schiffsdaten, Crew-Profile, Reiseeinträge und E2E-Schlüssel unwiderruflich gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.", "delete_account_btn": "Konto unwiderruflich löschen", diff --git a/client/src/i18n/locales/en.json b/client/src/i18n/locales/en.json index e977726..204a2cf 100644 --- a/client/src/i18n/locales/en.json +++ b/client/src/i18n/locales/en.json @@ -633,6 +633,8 @@ "share_enable": "Enable Public Link", "share_copied": "Link copied!", "share_copy_btn": "Copy Link", + "link_qr_hint": "Scan this QR code with your phone", + "link_qr_alt": "QR code for the link", "danger_zone_title": "Danger Zone", "danger_zone_desc": "Deleting your account will permanently delete all your passkeys, logbooks, vessel data, crew profiles, travel logs, and E2E keys. This action cannot be undone.", "delete_account_btn": "Permanently Delete Account", diff --git a/client/src/i18n/locales/nb.json b/client/src/i18n/locales/nb.json index 123661e..1394cc6 100644 --- a/client/src/i18n/locales/nb.json +++ b/client/src/i18n/locales/nb.json @@ -633,6 +633,8 @@ "share_enable": "Aktiver offentlig lenke", "share_copied": "Linken er kopiert!", "share_copy_btn": "Kopier lenke", + "link_qr_hint": "Skann QR-koden med telefonen", + "link_qr_alt": "QR-kode for lenken", "danger_zone_title": "Faresone", "danger_zone_desc": "Hvis du sletter kontoen din, slettes alle dine Passkeys, loggbøker, skipsdata, mannskapsprofiler, reiseoppføringer og E2E-nøkler ugjenkallelig. Denne handlingen kan ikke angres.", "delete_account_btn": "Slett konto ugjenkallelig", diff --git a/client/src/i18n/locales/sv.json b/client/src/i18n/locales/sv.json index 1a58910..07fee46 100644 --- a/client/src/i18n/locales/sv.json +++ b/client/src/i18n/locales/sv.json @@ -633,6 +633,8 @@ "share_enable": "Aktivera offentlig länk", "share_copied": "Länk kopierad!", "share_copy_btn": "Kopiera länk", + "link_qr_hint": "Skanna QR-koden med mobilen", + "link_qr_alt": "QR-kod för länken", "danger_zone_title": "Farlig zon", "danger_zone_desc": "Om du raderar ditt konto raderas oåterkalleligen alla dina Passkey, loggböcker, fartygsdata, besättningsprofiler, reseanteckningar och E2E-nycklar. Denna åtgärd kan inte ångras.", "delete_account_btn": "Ta bort konto oåterkalleligt",