import { useEffect, useState } from 'react' import QRCode from 'qrcode' import { api } from '../api/client' import { Layout } from '../components/Layout' interface UserOut { id: string; first_name: string; last_name: string; email: string; role: string } export function QrStampSettingsPage() { const [me, setMe] = useState(null) const [companyName, setCompanyName] = useState('') const [psEnabled, setPsEnabled] = useState(false) const [psStatus, setPsStatus] = useState<{ configured: boolean; created_at: string | null } | null>(null) const [psUrl, setPsUrl] = useState(null) const [psQr, setPsQr] = useState(null) const [psBusy, setPsBusy] = useState(false) const [psCopied, setPsCopied] = useState(false) const [saving, setSaving] = useState(false) const [saved, setSaved] = useState(false) const [error, setError] = useState(null) useEffect(() => { api.get('/auth/me').then(setMe).catch(() => {}) api.get<{ name: string }>('/companies/me').then(c => setCompanyName(c.name)).catch(() => {}) api.get<{ enabled: boolean; configured: boolean; created_at: string | null }>('/companies/me/public-stamp-token') .then(s => { setPsStatus({ configured: s.configured, created_at: s.created_at }); setPsEnabled(s.enabled) }) .catch(() => {}) }, []) async function saveEnabled(next: boolean) { setSaving(true) setError(null) setSaved(false) try { await api.patch('/companies/me', { public_stamp_enabled: next }) setPsEnabled(next) setSaved(true) setTimeout(() => setSaved(false), 3000) } catch (e: unknown) { setError(e instanceof Error ? e.message : 'Fehler beim Speichern') } finally { setSaving(false) } } async function rotatePublicStampToken() { setPsBusy(true) setError(null) try { const res = await api.post<{ token: string; public_url: string; created_at: string }>( '/companies/me/public-stamp-token/rotate', {} ) setPsUrl(res.public_url) setPsStatus({ configured: true, created_at: res.created_at }) setPsCopied(false) setPsQr(await QRCode.toDataURL(res.public_url, { width: 320, margin: 2, errorCorrectionLevel: 'H' })) } catch (e: unknown) { setError(e instanceof Error ? e.message : 'Fehler beim Generieren des QR-Tokens') } finally { setPsBusy(false) } } async function revokePublicStampToken() { if (!confirm('QR-Code wirklich deaktivieren? Bestehende ausgedruckte Codes und eingerichtete Tablets funktionieren danach nicht mehr.')) return setPsBusy(true) setError(null) try { await api.del('/companies/me/public-stamp-token') setPsStatus({ configured: false, created_at: null }) setPsUrl(null) setPsQr(null) } catch (e: unknown) { setError(e instanceof Error ? e.message : 'Fehler beim Deaktivieren') } finally { setPsBusy(false) } } async function copyPublicStampUrl() { if (!psUrl) return try { await navigator.clipboard.writeText(psUrl) setPsCopied(true) setTimeout(() => setPsCopied(false), 2000) } catch { /* clipboard nicht verfügbar */ } } function openOnThisDevice() { if (!psUrl) return window.open(psUrl, '_blank', 'noopener') } function printPublicStampQr() { if (!psQr) return const w = window.open('', '_blank', 'width=600,height=800') if (!w) return w.document.write(`QR-Stempel

Zeiterfassung – ${companyName || ''}

QR

Mit dem Handy scannen, dann Personalnummer + PIN eingeben und ein-/ausstempeln.

`) w.document.close() } const isAdmin = me?.role === 'COMPANY_ADMIN' || me?.role === 'SUPER_ADMIN' return (

QR-Stempeln (Handy & Tablet)

{!isAdmin && (
Diese Einstellung ist nur für Firmen-Administratoren verfügbar.
)} {error && (
{error}
)} {saved && (
Gespeichert.
)} {isAdmin && (
📲

QR-Stempeln

Statischer Zugang zum Ein-/Ausstempeln. Mitarbeiter scannen den QR-Code mit dem privaten Handy oder ihr richtet ein Tablet am Eingang dauerhaft auf der Stempel-Seite ein. Anmeldung jeweils per Personalnummer + PIN.

⚠ Schwächer als Kiosk-Terminals: keine Geräte-Signatur. Schutz nur über PIN + Sperre nach Fehlversuchen. Nur aktivieren, wenn benötigt.
{/* Toggle aktiv (eigenes Speichern) */}

QR-Stempeln aktivieren

Wird sofort gespeichert.

{psStatus?.configured ? (
Zugang aktiv {psStatus.created_at && ( (seit {new Date(psStatus.created_at).toLocaleString('de-DE')}) )}
) : (
Kein Zugang generiert
)}
{psUrl && (

⚠ Jetzt sichern – der Link/QR-Code wird aus Sicherheitsgründen nur einmal angezeigt.

{psQr && (
QR-Code
)} {/* Tablet-Link */}

🖥 Tablet-Link (Direktaufruf am Eingang)

e.currentTarget.select()} className="flex-1 font-mono text-xs px-3 py-2 border border-amber-300 rounded bg-white" />

Diesen Link auf dem Tablet im Browser öffnen und als Startseite/Lesezeichen oder im Kiosk-/Vollbildmodus fixieren. Derselbe Link funktioniert auch hinter dem QR-Code fürs Handy.

)}
{psStatus?.configured && ( )} {psStatus?.configured && ( Beim Neugenerieren werden der bisherige Link und QR-Code ungültig. )}
)}
) }