import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { api } from "../../api"; import { useToast } from "../../toast"; import { useConfirm } from "../../confirm"; import Icon from "../../components/Icon"; import IconChip from "../../components/IconChip"; /** * Gerätemarken für die Schnellerfassung per iOS-Kurzbefehl. * * Ein Kurzbefehl kann sich nicht anmelden, er braucht ein Zugangsmerkmal. Die * Marke darf ausschliesslich erfassen – nicht lesen, nicht ändern. Geht das * Telefon verloren, ist damit kein Datenabfluss möglich. * * Den Kurzbefehl fertig zum Download anzubieten geht nicht: Seit iOS 15 müssen * solche Dateien signiert sein, und signieren kann nur ein Mac mit Apple-Konto. * Deshalb steht hier eine Bauanleitung mit kopierfertigen Werten. */ export default function Geraetemarken() { const { t } = useTranslation(); const toast = useToast(); const confirm = useConfirm(); const [marken, setMarken] = useState([]); const [konten, setKonten] = useState([]); const [name, setName] = useState(""); const [konto, setKonto] = useState(""); const [frisch, setFrisch] = useState(null); // { token, name } – nur einmal sichtbar const [busy, setBusy] = useState(false); const basis = useMemo(() => window.location.origin, []); async function laden() { try { const [m, k] = await Promise.all([api.listTokens(), api.listAccounts()]); setMarken(m); setKonten(k); if (!konto && k.length) setKonto(String(k[0].id)); } catch (e) { toast(e.message, "error"); } } useEffect(() => { laden(); }, []); async function anlegen(e) { e.preventDefault(); if (!name.trim()) return; setBusy(true); try { const neu = await api.createToken({ name: name.trim(), default_account_id: konto ? Number(konto) : null, }); setFrisch(neu); setName(""); laden(); } catch (e) { toast(e.message, "error"); } finally { setBusy(false); } } async function widerrufen(m) { if (!(await confirm({ title: t("tokens.revoke"), message: t("tokens.revokeConfirm", { name: m.name }), danger: true, confirmLabel: t("tokens.revoke"), }))) return; try { await api.deleteToken(m.id); laden(); } catch (e) { toast(e.message, "error"); } } async function kopieren(text) { try { await navigator.clipboard.writeText(text); toast(t("tokens.copied")); } catch { toast(t("tokens.copyFailed"), "error"); } } const kontoName = (id) => konten.find((k) => k.id === id)?.name; return (
{t("tokens.intro")}
{/* Gestapelt statt nebeneinander: Die Karte steht in einer schmalen Spalte, waagerecht quetschen sich Beschriftung und Feld ineinander. */} {frisch && ({t("tokens.guideNoDownload")}
{t("tokens.guideHomeScreen")}
{t("tokens.guideAutomation")}
{t("tokens.fileGuideIntro")}
{t("tokens.accountIds")}
{t("tokens.fileGuideNote")}