From fe05c55ffbe07a7e780cef332d39e7d8cc81f3bc Mon Sep 17 00:00:00 2001 From: Karl Ludwig Weise Date: Fri, 4 Sep 2026 14:53:29 +0200 Subject: [PATCH] Resolve missing staff crypto situations -wip --- package-lock.json | 8 +- package.json | 2 +- project.inlang/messages/de.json | 7 ++ project.inlang/messages/en.json | 7 ++ .../responsive-dialog.svelte | 20 +++- .../(components)/MissingStaffCrypto.svelte | 100 ++++++++++++++++++ src/routes/(pages)/dashboard/+layout.svelte | 4 + .../(components)/AppointmentPreview.svelte | 13 ++- 8 files changed, 149 insertions(+), 12 deletions(-) create mode 100644 src/routes/(pages)/dashboard/(components)/MissingStaffCrypto.svelte diff --git a/package-lock.json b/package-lock.json index 4d07139..05eb401 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ "@types/dotenv": "^6.1.1", "@types/node": "^24", "@types/nodemailer": "^6.4.17", - "bits-ui": "^2.15.4", + "bits-ui": "^2.19.0", "clsx": "^2.1.1", "drizzle-kit": "^0.31.1", "eslint": "^9.29.0", @@ -3803,9 +3803,9 @@ "license": "MIT" }, "node_modules/bits-ui": { - "version": "2.18.1", - "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.18.1.tgz", - "integrity": "sha512-KkemzKFH4T3gt3H+P86JcnAWExjByv/6vlwjm/BoCwTPHu03yiCdxbghdJLvFReQTe0acCAiRcKfmixxD6XvlA==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.19.0.tgz", + "integrity": "sha512-dHDm5Jw2NZJgLRvHAwPeuR4BnZij+AqTIq33OqVfo+qiMXontoC/yIvGT5TZaviv7OuBdJ2QUy2JPQ2yKBvF6w==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 37271e0..57daa4e 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "@types/dotenv": "^6.1.1", "@types/node": "^24", "@types/nodemailer": "^6.4.17", - "bits-ui": "^2.15.4", + "bits-ui": "^2.19.0", "clsx": "^2.1.1", "drizzle-kit": "^0.31.1", "eslint": "^9.29.0", diff --git a/project.inlang/messages/de.json b/project.inlang/messages/de.json index d56ac29..034f902 100644 --- a/project.inlang/messages/de.json +++ b/project.inlang/messages/de.json @@ -458,6 +458,13 @@ } ], "error": "Fehler beim Senden" + }, + "missingCryptoKeys": { + "title": "Kryptografische Schlüssel fehlen", + "description": "Dieser Tab enthält nicht die kryptografischen Schlüssel zum Entschlüsseln und Verschlüsseln von Daten. Lade die Schlüssel, um Termine/Daten anzuzeigen und hinzuzufügen.", + "cancel": "Ich will nur Einstellungen ändern", + "success": "Kryptografische Schlüssel geladen", + "error": "Fehler beim Laden der kryptografischen Schlüssel" } }, "agents": { diff --git a/project.inlang/messages/en.json b/project.inlang/messages/en.json index e4b5ab8..4a32832 100644 --- a/project.inlang/messages/en.json +++ b/project.inlang/messages/en.json @@ -466,6 +466,13 @@ } ], "error": "Failed to send reminders" + }, + "missingCryptoKeys": { + "title": "Cryptography Keys Missing", + "description": "This tab is missing the cryptographic keys to decrypt and encrypt data. Load keys to view and add appointments/data.", + "cancel": "I just want to change settings", + "success": "Cryptography keys loaded", + "error": "Failed to load cryptography keys" } }, "agents": { diff --git a/src/lib/components/ui/responsive-dialog/responsive-dialog.svelte b/src/lib/components/ui/responsive-dialog/responsive-dialog.svelte index 4da3c65..21fde88 100644 --- a/src/lib/components/ui/responsive-dialog/responsive-dialog.svelte +++ b/src/lib/components/ui/responsive-dialog/responsive-dialog.svelte @@ -52,6 +52,7 @@ triggerHidden = false, triggerVariant = "default", isActionLoading = false, + isDismissable = true, actions, children, }: HTMLAttributes & { @@ -62,6 +63,7 @@ description?: string; triggerVariant?: ButtonVariant; isActionLoading?: boolean; + isDismissable?: boolean; actions?: ListItemAction[]; } = $props(); @@ -153,9 +155,13 @@ 0 && "[&>button:last-child]:hidden", // hides default close button + // hides default close button + actions && actions.length > 0 ? "[&>button:last-child]:hidden" : "", )} onOpenAutoFocus={(e) => e.preventDefault()} + escapeKeydownBehavior={isDismissable === false ? "ignore" : "close"} + interactOutsideBehavior={isDismissable === false ? "ignore" : "close"} + showCloseButton={isDismissable === true} >
@@ -183,7 +189,7 @@ {:else} - + {#if !triggerHidden} {#if typeof triggerLabel === "string"} @@ -196,6 +202,8 @@ e.preventDefault()} + escapeKeydownBehavior={isDismissable === false ? "ignore" : "close"} + interactOutsideBehavior={isDismissable === false ? "ignore" : "close"} >
@@ -213,9 +221,11 @@ {@render children?.()} - - {m.cancel()} - + {#if isDismissable !== false} + + {m.cancel()} + + {/if} {/if} diff --git a/src/routes/(pages)/dashboard/(components)/MissingStaffCrypto.svelte b/src/routes/(pages)/dashboard/(components)/MissingStaffCrypto.svelte new file mode 100644 index 0000000..e1724dd --- /dev/null +++ b/src/routes/(pages)/dashboard/(components)/MissingStaffCrypto.svelte @@ -0,0 +1,100 @@ + + + +
+ + +
+
diff --git a/src/routes/(pages)/dashboard/+layout.svelte b/src/routes/(pages)/dashboard/+layout.svelte index afb2486..9ff1275 100644 --- a/src/routes/(pages)/dashboard/+layout.svelte +++ b/src/routes/(pages)/dashboard/+layout.svelte @@ -9,6 +9,8 @@ import { staff } from "$lib/stores/staff"; import { QueryClient, QueryClientProvider } from "@tanstack/svelte-query"; import { browser } from "$app/environment"; + import MissingStaffCrypto from "./(components)/MissingStaffCrypto.svelte"; + import { openDialog } from "$lib/components/ui/responsive-dialog"; let { data, children }: LayoutProps = $props(); @@ -80,6 +82,7 @@ if (success) { console.log("✅ Staff crypto initialized successfully"); } else { + openDialog("missing-staff-crypto"); console.log("ℹ️ Staff crypto not initialized (no session data or error)"); } } @@ -88,4 +91,5 @@ {@render children()} + diff --git a/src/routes/(pages)/dashboard/calendar/(components)/AppointmentPreview.svelte b/src/routes/(pages)/dashboard/calendar/(components)/AppointmentPreview.svelte index 1799c0f..7bbb8f7 100644 --- a/src/routes/(pages)/dashboard/calendar/(components)/AppointmentPreview.svelte +++ b/src/routes/(pages)/dashboard/calendar/(components)/AppointmentPreview.svelte @@ -9,6 +9,7 @@ import { Button } from "$lib/components/ui/button"; import { m } from "$i18n/messages"; import { cn } from "$lib/utils"; + import { auth } from "$lib/stores/auth"; let { item, @@ -29,7 +30,7 @@ missingKeyShare: "Missing key share", }; - const decrypt = async () => { + const decrypt = async (retry = true) => { if (!item.appointment) { console.error("Unable to decrypt appointment data - no appointment data in item.", item.id); error = "Missing data"; @@ -52,7 +53,7 @@ // Wait for crypto to be initialized (max 5 seconds) if (!$staffCrypto.isAuthenticated || !$staffCrypto.crypto) { - const maxWaitTime = 5000; // 5 seconds + const maxWaitTime = 2000; const startTime = Date.now(); while (!$staffCrypto.isAuthenticated && Date.now() - startTime < maxWaitTime) { @@ -60,6 +61,14 @@ } if (!$staffCrypto.isAuthenticated || !$staffCrypto.crypto) { + const user = $auth.user; + if (retry && user && user.tenantId) { + console.warn("Staff crypto not initialized, retrying decryption..."); + // TODO: This should only be done once, not in every instance + await staffCrypto.authenticate(user.id, user.tenantId); + decrypt(false); + return; + } error = "Crypto not initialized"; console.error("Staff crypto not initialized after waiting"); return;