+ {offset + 1} to {Math.min(offset + PAGE_SIZE, total)} of {total}
+
+
+
+
+
+ )}
+ setPending(null)}
+ onConfirm={confirmPending}
+ title={pending?.kind === 'forget' ? 'Forget this device?' : 'Wipe this data?'}
+ message={confirmMessage(pending)}
+ confirmText={pending?.kind === 'forget' ? 'Forget' : 'Wipe'}
+ variant="destructive"
+ />
+
+ );
+}
+
+function wipeReasonLabel(reason: NonNullable): string {
+ switch (reason) {
+ case 'user': return 'by the user';
+ case 'rejected': return 'the server refused the login';
+ default: return reason;
+ }
+}
+
+function confirmMessage(pending: Pending | null): string {
+ if (!pending) return '';
+ const consequences = 'Mail, keys and files kept on it are deleted and the account is signed out the next time it connects. Mail that was waiting to be sent is lost. S/MIME and OpenPGP keys imported in that browser or app go for every account there, because they are not kept per account: anyone using them has to import them again. Keys in the OS certificate store stay. The mail on the server is not touched.';
+ if (pending.kind === 'device') return `${pending.device.platform || 'This device'} of ${pending.device.user}. ${consequences}`;
+ if (pending.kind === 'user') return `All ${pending.count} devices of ${pending.user}. ${consequences}`;
+ return pending.device.wipe
+ ? `The wipe waiting for ${pending.device.platform || 'this device'} of ${pending.device.user} is dropped with its row. The device will not be told.`
+ : `${pending.device.platform || 'This device'} of ${pending.device.user} leaves this list. It comes back as a new row if it registers again.`;
+}
diff --git a/app/(main)/admin/_pages/logs.tsx b/app/(main)/admin/_pages/logs.tsx
index 4077e9732..d6930eeb2 100644
--- a/app/(main)/admin/_pages/logs.tsx
+++ b/app/(main)/admin/_pages/logs.tsx
@@ -63,6 +63,10 @@ export function LogsPage() {
+
+
+
+