const { useState, useEffect, useRef, useContext } = React;

/* ---------------- API helper ---------------- */
function getToken() { return localStorage.getItem("token"); }
async function api(path, opts = {}) {
  const token = getToken();
  const headers = Object.assign({}, opts.headers, token ? { Authorization: "Bearer " + token } : {});
  let body = opts.body;
  if (body && !(body instanceof FormData)) {
    headers["Content-Type"] = "application/json";
    body = JSON.stringify(body);
  }
  const res = await fetch(path, { ...opts, headers, body });
  if (res.status === 401) {
    localStorage.removeItem("token");
    throw new Error("unauth");
  }
  if (!res.ok) {
    const e = await res.json().catch(() => ({ error: "Fehler" }));
    throw new Error(e.error || "Fehler");
  }
  return res.status === 204 ? null : res.json();
}

/* ---------------- i18n ---------------- */
// Übersetzung passiert ausschliesslich clientseitig: der Server speichert/
// liefert Kriterien- und Kategorietexte sowie Audit-Snapshots immer auf
// Deutsch (das ist die kanonische Quelle). CRITERIA_I18N/CATEGORY_I18N sind
// daher reine Anzeige-Overlays, per stabiler Kriterien-ID bzw. Kategoriename
// nachgeschlagen - vergangene Audits bleiben inhaltlich unverändert.
const LangContext = React.createContext({ lang: "de", setLang: () => {}, t: (k) => k });
function useLang() { return useContext(LangContext); }

const STRINGS = {
  de: {
    loading: "Lädt…",
    konto: "Konto",
    abmelden: "Abmelden",
    toggleSidebar: "Menü ein-/ausblenden",
    navHighlights: "Highlights",
    navAudit: "Audit durchführen",
    navAuswertung: "Auswertung",
    navHistorie: "Vergangene Audits",
    navTasks: "Tasks",
    navMitarbeitende: "Mitarbeitende",
    navBenutzer: "Benutzerverwaltung",

    loginSub: "Mit Benutzername und Passwort anmelden. Die Rolle ist beim Konto hinterlegt.",
    loginFailed: "Anmeldung fehlgeschlagen",
    username: "Benutzername",
    usernamePlaceholder: "z. B. sm.zuerich",
    password: "Passwort",
    passwordPlaceholder: "••••••••",
    loginButton: "Anmelden",
    language: "Sprache",

    kontoTitle: "Konto",
    kontoSub: "Deine E-Mail-Adresse und dein Passwort.",
    emailSectionTitle: "E-Mail für Benachrichtigungen",
    emailSectionSub: "Wird z. B. genutzt, um dich zu informieren, wenn ein neuer Auditbericht für deinen Store verfügbar ist.",
    emailSavedOk: "E-Mail gespeichert.",
    emailLabel: "E-Mail-Adresse",
    emailPlaceholder: "name@digitecgalaxus.ch",
    saveEmailButton: "E-Mail speichern",
    passwordSectionTitle: "Passwort ändern",
    passwordMismatch: "Die Passwörter stimmen nicht überein.",
    currentPassword: "Aktuelles Passwort",
    newPassword: "Neues Passwort",
    confirmPassword: "Neues Passwort bestätigen",
    passwordSavedOk: "Passwort wurde geändert.",
    savePasswordButton: "Passwort speichern",

    highlightsTitle: "Highlights",
    highlightsSub: "Aktuelle Highlights aus allen Stores.",
    highlightsEmpty: "Noch keine Highlights markiert. Highlights aus Audits erscheinen hier.",

    auditTitle: "Audit durchführen",
    auditNoneRunning: "Aktuell läuft kein Audit für diesen Store.",
    auditJustCompleted: (store) => `Audit für ${store} wurde gespeichert und abgeschlossen.`,
    auditJustCancelled: (store) => `Audit für ${store} wurde verworfen.`,
    auditLeaderEmpty: "Sobald Quality ein Audit für diesen Store startet, erscheint es hier zur Ansicht.",
    auditStartHint: "Ein neues Audit trägt automatisch das heutige Datum als Startdatum ein. Danach kannst du Kategorie für Kategorie durchgehen.",
    auditStartButton: "Audit starten",
    readonlyTag: "Ansicht",
    auditRunningSince: (date, name) => `Läuft seit ${date} · gestartet von ${name}`,
    auditStartedProgress: (date, done, total) => `Gestartet am ${date} · ${done}/${total} Kriterien bewertet`,
    back: "Zurück",
    next: "Weiter",
    completeMissing: `Bitte alle Kriterien bewerten (jedes ist Pflichtfeld, auch "Nicht anwendbar" zählt), bevor du das Audit abschliesst.`,
    completeButton: "Audit abschliessen",
    cancelButton: "Audit abbrechen",
    cancelConfirm: "Bist du sicher, dass du diesen Audit verwerfen willst?",
    storeLabel: "Store",

    markHighlight: "Als Highlight markieren",
    commentPlaceholderNeeded: "Begründung eintragen…",
    commentPlaceholderOptional: "Kommentar eintragen (optional)…",
    commentRequiredHint: "Bei \"Teilweise erfüllt\" oder \"Nicht erfüllt\" ist eine Begründung Pflicht.",
    photoUploadButton: "Foto hochladen",
    photoPickCamera: "Kamera",
    photoPickFiles: "Dateien",
    photoAlt: "Beleg",

    erfuelltSuffix: "erfüllt",
    freigebenButton: (n) => `${n} Punkt(e) an Store freigeben`,
    filterAlle: "Alle",
    emptyEntries: "Keine Einträge in dieser Ansicht.",
    offen: "Offen",
    releasedTag: "an Store freigegeben",

    auswertungTitle: "Auswertung",
    auswertungNone: "Noch kein abgeschlossenes Audit für diesen Store.",
    auswertungEmptyHint: "Sobald Quality ein Audit für diesen Store abschliesst, erscheint die Auswertung hier.",
    printTitle: "Auditbericht",
    printMeta: (completed, started, pct) => `Abgeschlossen am ${completed} · gestartet am ${started} · Gesamtergebnis ${pct}% erfüllt`,
    auswertungSub: (completed, started) => `Abgeschlossen am ${completed} · gestartet am ${started}`,
    exportPdfButton: "Als PDF exportieren",

    historieTitle: "Vergangene Audits",
    backToList: "Zurück zur Liste",
    auditOf: (store) => `Audit ${store}`,
    historieDetailSub: (completed, started, name) => `Abgeschlossen am ${completed} · gestartet am ${started} · von ${name}`,
    historieSub: (store) => `Abgeschlossene Audits für ${store}`,
    historieEmpty: "Für diesen Store liegen noch keine abgeschlossenen Audits vor.",
    completedOn: (date) => `Abgeschlossen am ${date}`,
    startedOnBy: (date, name) => `Gestartet am ${date} · von ${name}`,

    tasksTitle: "Tasks",
    tasksSubCoordinator: "Dir zugewiesene Aufgaben.",
    tasksSubLeader: "Alle Tasks (Ansicht) – eskalierte Tasks kannst du delegieren oder abschliessen.",
    tasksSubDefault: "Offene und delegierte Aufgaben. Bei Freigabe erhält ein Task 30 Tage Frist, danach eine Erinnerung, nach weiteren 30 Tagen die Eskalation an Leader Retail.",
    tasksEmpty: "Keine Tasks vorhanden.",
    taskMeta: (store, fromRole, to) => `${store} · von ${fromRole} · an ${to}`,
    taskDue: (date) => ` · Frist: ${date}`,
    overdue: " · überfällig",
    escalated: " · an Leader Retail eskaliert",
    delegateTo: "Delegieren an…",
    doneButton: "Erledigt",

    resetConfirm: (username) => `Passwort für ${username} wirklich zurücksetzen?`,
    newPasswordLabel: "Neues Passwort:",
    ok: "OK",
    resetButton: "Passwort zurücksetzen",

    mitarbeitendeTitle: "Mitarbeitende",
    mitarbeitendeSub: (count, store) => `${count} erfasst in ${store}`,
    namePlaceholder: "Name",
    usernameOptional: "Benutzername (optional)",
    passwordOptional: "Passwort (optional)",
    emailOptional: "E-Mail (optional)",
    addButton: "Erfassen",
    loginLabel: (username) => `Login: ${username}`,
    mitarbeitendeEmpty: "Noch keine Mitarbeitenden erfasst.",

    benutzerTitle: "Benutzerverwaltung",
    benutzerSub: "Deputy Leader Retail für Shop Manager aktivieren.",
    deputyTitle: "Deputy Leader Retail",
    deputyExplain: "Aktiviert für einen Shop Manager zusätzlich die Einsicht in alle Stores (Auswertung, Vergangene Audits, Tasks) – bearbeiten kann er weiterhin nur seinen eigenen Store.",
    deputyActive: "Deputy Leader Retail (aktiv)",
    deputyInactive: "Deputy Leader Retail (inaktiv)",
    deputyDeactivate: "Deaktivieren",
    deputyActivate: "Aktivieren",
    noShopmanagers: "Noch keine Shop Manager erfasst.",

    createUserTitle: "Neuen Benutzer erstellen",
    createUserExplain: "Erstellt ein neues Konto für Quality, Leader Retail, Shop Manager oder Showroom Koordinator.",
    roleLabelField: "Rolle",
    createUserButton: "Benutzer erstellen",
    createUserOk: (username) => `Konto ${username} wurde erstellt.`,
    qualityLeaderListTitle: "Quality & Leader Retail Konten",
    noQualityLeader: "Noch keine weiteren Quality- oder Leader-Retail-Konten erfasst.",

    statusErfuellt: "Erfüllt",
    statusTeilweise: "Teilweise erfüllt",
    statusNicht: "Nicht erfüllt",
    statusNa: "Nicht anwendbar",
    roleCoordinator: "Showroom Koordinator",
    taskStatusOffen: "offen",
    taskStatusDelegiert: "delegiert",
    taskStatusErledigt: "erledigt",

    deleteButton: "Löschen",
    deleteUserConfirm: (name) => `Konto von ${name} wirklich löschen?`,
    deleteAuditConfirm: "Diesen Auditbericht wirklich unwiderruflich löschen?",
    deleteAuditButton: "Bericht löschen",
    deleteTaskConfirm: "Diesen Task wirklich unwiderruflich löschen?",
    navStatistik: "Statistik",
    statistikTitle: "Statistik",
    statistikSub: "Durchschnittliches Ergebnis (erfüllt-%) über die Zeit.",
    statistikOverall: "Alle Stores (Durchschnitt)",
    statistikEmpty: "Noch keine Daten für diese Auswahl - sobald Audits abgeschlossen sind, erscheint hier der Verlauf.",
    filterStoreLabel: "Store",
    filterAllStores: "Alle Stores",
    filterOverdueLabel: "Mind. Tage überfällig",
    doneCommentLabel: "Was wurde gemacht?",
    doneCommentPlaceholder: "Kurz beschreiben, was zur Erledigung unternommen wurde…",
    doneUploadOptional: "Foto als Nachweis (optional)",
    doneSubmit: "Als erledigt speichern",
    doneCancel: "Abbrechen",
    doneMissingComment: "Bitte einen Text zur Erledigung eintragen.",
    printSubMeta: (started, pct) => `Gestartet am ${started} · Gesamtergebnis ${pct}% erfüllt`,

    editUserTitle: (name) => `Konto bearbeiten – ${name}`,
    editUserPrimaryStoreLabel: "Store",
    editUserAdditionalStoresLabel: "Zusätzliche Stores (mehrere möglich)",
    editUserSave: "Speichern",
  },
  fr: {
    loading: "Chargement…",
    konto: "Compte",
    abmelden: "Déconnexion",
    toggleSidebar: "Afficher/masquer le menu",
    navHighlights: "Highlights",
    navAudit: "Effectuer un audit",
    navAuswertung: "Évaluation",
    navHistorie: "Audits passés",
    navTasks: "Tâches",
    navMitarbeitende: "Collaborateurs",
    navBenutzer: "Gestion des utilisateurs",

    loginSub: "Connexion avec nom d'utilisateur et mot de passe. Le rôle est défini au niveau du compte.",
    loginFailed: "Échec de la connexion",
    username: "Nom d'utilisateur",
    usernamePlaceholder: "p. ex. sm.zuerich",
    password: "Mot de passe",
    passwordPlaceholder: "••••••••",
    loginButton: "Se connecter",
    language: "Langue",

    kontoTitle: "Compte",
    kontoSub: "Ton adresse e-mail et ton mot de passe.",
    emailSectionTitle: "E-mail pour les notifications",
    emailSectionSub: "Utilisé par exemple pour t'informer lorsqu'un nouveau rapport d'audit est disponible pour ton store.",
    emailSavedOk: "E-mail enregistré.",
    emailLabel: "Adresse e-mail",
    emailPlaceholder: "name@digitecgalaxus.ch",
    saveEmailButton: "Enregistrer l'e-mail",
    passwordSectionTitle: "Changer le mot de passe",
    passwordMismatch: "Les mots de passe ne correspondent pas.",
    currentPassword: "Mot de passe actuel",
    newPassword: "Nouveau mot de passe",
    confirmPassword: "Confirmer le nouveau mot de passe",
    passwordSavedOk: "Le mot de passe a été modifié.",
    savePasswordButton: "Enregistrer le mot de passe",

    highlightsTitle: "Highlights",
    highlightsSub: "Highlights actuels de tous les stores.",
    highlightsEmpty: "Aucun highlight pour l'instant. Les highlights des audits apparaissent ici.",

    auditTitle: "Effectuer un audit",
    auditNoneRunning: "Aucun audit en cours pour ce store actuellement.",
    auditJustCompleted: (store) => `L'audit pour ${store} a été enregistré et clôturé.`,
    auditJustCancelled: (store) => `L'audit pour ${store} a été annulé.`,
    auditLeaderEmpty: "Dès que Quality démarre un audit pour ce store, il apparaît ici en consultation.",
    auditStartHint: "Un nouvel audit enregistre automatiquement la date du jour comme date de début. Tu peux ensuite parcourir les catégories une à une.",
    auditStartButton: "Démarrer l'audit",
    readonlyTag: "Consultation",
    auditRunningSince: (date, name) => `En cours depuis le ${date} · démarré par ${name}`,
    auditStartedProgress: (date, done, total) => `Démarré le ${date} · ${done}/${total} critères évalués`,
    back: "Précédent",
    next: "Suivant",
    completeMissing: `Merci d'évaluer tous les critères (chacun est obligatoire, "Non applicable" compte aussi) avant de clôturer l'audit.`,
    completeButton: "Clôturer l'audit",
    cancelButton: "Annuler l'audit",
    cancelConfirm: "Es-tu sûr de vouloir annuler cet audit ?",
    storeLabel: "Store",

    markHighlight: "Marquer comme highlight",
    commentPlaceholderNeeded: "Indiquer une justification…",
    commentPlaceholderOptional: "Ajouter un commentaire (facultatif)…",
    commentRequiredHint: "Pour \"Partiellement conforme\" ou \"Non conforme\", une justification est obligatoire.",
    photoUploadButton: "Télécharger une photo",
    photoPickCamera: "Appareil photo",
    photoPickFiles: "Fichiers",
    photoAlt: "Justificatif",

    erfuelltSuffix: "conforme",
    freigebenButton: (n) => `Transmettre ${n} point(s) au store`,
    filterAlle: "Tous",
    emptyEntries: "Aucune entrée dans cette vue.",
    offen: "Ouvert",
    releasedTag: "transmis au store",

    auswertungTitle: "Évaluation",
    auswertungNone: "Pas encore d'audit clôturé pour ce store.",
    auswertungEmptyHint: "Dès que Quality clôture un audit pour ce store, l'évaluation apparaît ici.",
    printTitle: "Rapport d'audit",
    printMeta: (completed, started, pct) => `Clôturé le ${completed} · démarré le ${started} · résultat global ${pct}% conforme`,
    auswertungSub: (completed, started) => `Clôturé le ${completed} · démarré le ${started}`,
    exportPdfButton: "Exporter en PDF",

    historieTitle: "Audits passés",
    backToList: "Retour à la liste",
    auditOf: (store) => `Audit ${store}`,
    historieDetailSub: (completed, started, name) => `Clôturé le ${completed} · démarré le ${started} · par ${name}`,
    historieSub: (store) => `Audits clôturés pour ${store}`,
    historieEmpty: "Aucun audit clôturé pour ce store pour l'instant.",
    completedOn: (date) => `Clôturé le ${date}`,
    startedOnBy: (date, name) => `Démarré le ${date} · par ${name}`,

    tasksTitle: "Tâches",
    tasksSubCoordinator: "Tâches qui te sont assignées.",
    tasksSubLeader: "Toutes les tâches (vue) – tu peux déléguer ou clôturer les tâches escaladées.",
    tasksSubDefault: "Tâches ouvertes et déléguées. Une fois transmise, une tâche a un délai de 30 jours, puis un rappel, puis après 30 jours supplémentaires une escalade vers Leader Retail.",
    tasksEmpty: "Aucune tâche pour l'instant.",
    taskMeta: (store, fromRole, to) => `${store} · de ${fromRole} · à ${to}`,
    taskDue: (date) => ` · Délai : ${date}`,
    overdue: " · en retard",
    escalated: " · escaladé à Leader Retail",
    delegateTo: "Déléguer à…",
    doneButton: "Terminé",

    resetConfirm: (username) => `Réinitialiser vraiment le mot de passe de ${username} ?`,
    newPasswordLabel: "Nouveau mot de passe :",
    ok: "OK",
    resetButton: "Réinitialiser le mot de passe",

    mitarbeitendeTitle: "Collaborateurs",
    mitarbeitendeSub: (count, store) => `${count} enregistré(s) à ${store}`,
    namePlaceholder: "Nom",
    usernameOptional: "Nom d'utilisateur (facultatif)",
    passwordOptional: "Mot de passe (facultatif)",
    emailOptional: "E-mail (facultatif)",
    addButton: "Enregistrer",
    loginLabel: (username) => `Login : ${username}`,
    mitarbeitendeEmpty: "Aucun collaborateur enregistré pour l'instant.",

    benutzerTitle: "Gestion des utilisateurs",
    benutzerSub: "Activer Deputy Leader Retail pour les Shop Managers.",
    deputyTitle: "Deputy Leader Retail",
    deputyExplain: "Donne en plus à un Shop Manager l'accès en consultation à tous les stores (évaluation, audits passés, tâches) – il ne peut toujours modifier que son propre store.",
    deputyActive: "Deputy Leader Retail (actif)",
    deputyInactive: "Deputy Leader Retail (inactif)",
    deputyDeactivate: "Désactiver",
    deputyActivate: "Activer",
    noShopmanagers: "Aucun Shop Manager enregistré pour l'instant.",

    createUserTitle: "Créer un nouvel utilisateur",
    createUserExplain: "Crée un nouveau compte pour Quality, Leader Retail, Shop Manager ou Coordinateur Showroom.",
    roleLabelField: "Rôle",
    createUserButton: "Créer l'utilisateur",
    createUserOk: (username) => `Le compte ${username} a été créé.`,
    qualityLeaderListTitle: "Comptes Quality & Leader Retail",
    noQualityLeader: "Aucun autre compte Quality ou Leader Retail enregistré pour l'instant.",

    statusErfuellt: "Conforme",
    statusTeilweise: "Partiellement conforme",
    statusNicht: "Non conforme",
    statusNa: "Non applicable",
    roleCoordinator: "Coordinateur Showroom",
    taskStatusOffen: "ouvert",
    taskStatusDelegiert: "délégué",
    taskStatusErledigt: "terminé",

    deleteButton: "Supprimer",
    deleteUserConfirm: (name) => `Vraiment supprimer le compte de ${name} ?`,
    deleteAuditConfirm: "Vraiment supprimer définitivement ce rapport d'audit ?",
    deleteAuditButton: "Supprimer le rapport",
    deleteTaskConfirm: "Vraiment supprimer définitivement cette tâche ?",
    navStatistik: "Statistiques",
    statistikTitle: "Statistiques",
    statistikSub: "Résultat moyen (% conforme) dans le temps.",
    statistikOverall: "Tous les stores (moyenne)",
    statistikEmpty: "Pas encore de données pour cette sélection - dès que des audits sont clôturés, l'évolution apparaît ici.",
    filterStoreLabel: "Store",
    filterAllStores: "Tous les stores",
    filterOverdueLabel: "Jours de retard min.",
    doneCommentLabel: "Qu'est-ce qui a été fait ?",
    doneCommentPlaceholder: "Décrire brièvement ce qui a été entrepris…",
    doneUploadOptional: "Photo comme preuve (facultatif)",
    doneSubmit: "Enregistrer comme terminé",
    doneCancel: "Annuler",
    doneMissingComment: "Merci d'indiquer un texte pour la clôture.",
    printSubMeta: (started, pct) => `Démarré le ${started} · résultat global ${pct}% conforme`,

    editUserTitle: (name) => `Modifier le compte – ${name}`,
    editUserPrimaryStoreLabel: "Store",
    editUserAdditionalStoresLabel: "Stores supplémentaires (plusieurs possibles)",
    editUserSave: "Enregistrer",
  },
  en: {
    loading: "Loading…",
    konto: "Account",
    abmelden: "Log out",
    toggleSidebar: "Toggle menu",
    navHighlights: "Highlights",
    navAudit: "Run audit",
    navAuswertung: "Report",
    navHistorie: "Past audits",
    navTasks: "Tasks",
    navMitarbeitende: "Staff",
    navBenutzer: "User management",

    loginSub: "Sign in with username and password. Your role is set on your account.",
    loginFailed: "Login failed",
    username: "Username",
    usernamePlaceholder: "e.g. sm.zuerich",
    password: "Password",
    passwordPlaceholder: "••••••••",
    loginButton: "Sign in",
    language: "Language",

    kontoTitle: "Account",
    kontoSub: "Your email address and password.",
    emailSectionTitle: "Email for notifications",
    emailSectionSub: "Used e.g. to notify you when a new audit report is available for your store.",
    emailSavedOk: "Email saved.",
    emailLabel: "Email address",
    emailPlaceholder: "name@digitecgalaxus.ch",
    saveEmailButton: "Save email",
    passwordSectionTitle: "Change password",
    passwordMismatch: "The passwords do not match.",
    currentPassword: "Current password",
    newPassword: "New password",
    confirmPassword: "Confirm new password",
    passwordSavedOk: "Password has been changed.",
    savePasswordButton: "Save password",

    highlightsTitle: "Highlights",
    highlightsSub: "Current highlights from all stores.",
    highlightsEmpty: "No highlights marked yet. Highlights from audits appear here.",

    auditTitle: "Run audit",
    auditNoneRunning: "No audit currently running for this store.",
    auditJustCompleted: (store) => `The audit for ${store} was saved and completed.`,
    auditJustCancelled: (store) => `The audit for ${store} was discarded.`,
    auditLeaderEmpty: "As soon as Quality starts an audit for this store, it appears here for viewing.",
    auditStartHint: "A new audit automatically records today's date as the start date. You can then go through categories one by one.",
    auditStartButton: "Start audit",
    readonlyTag: "View only",
    auditRunningSince: (date, name) => `Running since ${date} · started by ${name}`,
    auditStartedProgress: (date, done, total) => `Started on ${date} · ${done}/${total} criteria assessed`,
    back: "Back",
    next: "Next",
    completeMissing: `Please assess every criterion (each is mandatory, "Not applicable" counts too) before completing the audit.`,
    completeButton: "Complete audit",
    cancelButton: "Cancel audit",
    cancelConfirm: "Are you sure you want to discard this audit?",
    storeLabel: "Store",

    markHighlight: "Mark as highlight",
    commentPlaceholderNeeded: "Enter a reason…",
    commentPlaceholderOptional: "Enter a comment (optional)…",
    commentRequiredHint: "A reason is required for \"Partially fulfilled\" or \"Not fulfilled\".",
    photoUploadButton: "Upload photo",
    photoPickCamera: "Camera",
    photoPickFiles: "Files",
    photoAlt: "Evidence",

    erfuelltSuffix: "fulfilled",
    freigebenButton: (n) => `Release ${n} item(s) to store`,
    filterAlle: "All",
    emptyEntries: "No entries in this view.",
    offen: "Open",
    releasedTag: "released to store",

    auswertungTitle: "Report",
    auswertungNone: "No completed audit for this store yet.",
    auswertungEmptyHint: "As soon as Quality completes an audit for this store, the report appears here.",
    printTitle: "Audit report",
    printMeta: (completed, started, pct) => `Completed on ${completed} · started on ${started} · overall result ${pct}% fulfilled`,
    auswertungSub: (completed, started) => `Completed on ${completed} · started on ${started}`,
    exportPdfButton: "Export as PDF",

    historieTitle: "Past audits",
    backToList: "Back to list",
    auditOf: (store) => `Audit ${store}`,
    historieDetailSub: (completed, started, name) => `Completed on ${completed} · started on ${started} · by ${name}`,
    historieSub: (store) => `Completed audits for ${store}`,
    historieEmpty: "No completed audits for this store yet.",
    completedOn: (date) => `Completed on ${date}`,
    startedOnBy: (date, name) => `Started on ${date} · by ${name}`,

    tasksTitle: "Tasks",
    tasksSubCoordinator: "Tasks assigned to you.",
    tasksSubLeader: "All tasks (view) – you can delegate or complete escalated tasks.",
    tasksSubDefault: "Open and delegated tasks. Once released, a task gets a 30-day deadline, then a reminder, then after another 30 days escalation to Leader Retail.",
    tasksEmpty: "No tasks yet.",
    taskMeta: (store, fromRole, to) => `${store} · from ${fromRole} · to ${to}`,
    taskDue: (date) => ` · Due: ${date}`,
    overdue: " · overdue",
    escalated: " · escalated to Leader Retail",
    delegateTo: "Delegate to…",
    doneButton: "Done",

    resetConfirm: (username) => `Really reset the password for ${username}?`,
    newPasswordLabel: "New password:",
    ok: "OK",
    resetButton: "Reset password",

    mitarbeitendeTitle: "Staff",
    mitarbeitendeSub: (count, store) => `${count} recorded at ${store}`,
    namePlaceholder: "Name",
    usernameOptional: "Username (optional)",
    passwordOptional: "Password (optional)",
    emailOptional: "Email (optional)",
    addButton: "Add",
    loginLabel: (username) => `Login: ${username}`,
    mitarbeitendeEmpty: "No staff recorded yet.",

    benutzerTitle: "User management",
    benutzerSub: "Activate Deputy Leader Retail for Shop Managers.",
    deputyTitle: "Deputy Leader Retail",
    deputyExplain: "Additionally grants a Shop Manager read access to all stores (report, past audits, tasks) – they can still only edit their own store.",
    deputyActive: "Deputy Leader Retail (active)",
    deputyInactive: "Deputy Leader Retail (inactive)",
    deputyDeactivate: "Deactivate",
    deputyActivate: "Activate",
    noShopmanagers: "No Shop Managers recorded yet.",

    createUserTitle: "Create new user",
    createUserExplain: "Creates a new account for Quality, Leader Retail, Shop Manager or Showroom Coordinator.",
    roleLabelField: "Role",
    createUserButton: "Create user",
    createUserOk: (username) => `Account ${username} was created.`,
    qualityLeaderListTitle: "Quality & Leader Retail accounts",
    noQualityLeader: "No other Quality or Leader Retail accounts recorded yet.",

    statusErfuellt: "Fulfilled",
    statusTeilweise: "Partially fulfilled",
    statusNicht: "Not fulfilled",
    statusNa: "Not applicable",
    roleCoordinator: "Showroom Coordinator",
    taskStatusOffen: "open",
    taskStatusDelegiert: "delegated",
    taskStatusErledigt: "done",

    deleteButton: "Delete",
    deleteUserConfirm: (name) => `Really delete the account of ${name}?`,
    deleteAuditConfirm: "Really delete this audit report permanently?",
    deleteAuditButton: "Delete report",
    deleteTaskConfirm: "Really delete this task permanently?",
    navStatistik: "Statistics",
    statistikTitle: "Statistics",
    statistikSub: "Average result (% fulfilled) over time.",
    statistikOverall: "All stores (average)",
    statistikEmpty: "No data yet for this selection - once audits are completed, the trend appears here.",
    filterStoreLabel: "Store",
    filterAllStores: "All stores",
    filterOverdueLabel: "Min. days overdue",
    doneCommentLabel: "What was done?",
    doneCommentPlaceholder: "Briefly describe what was done…",
    doneUploadOptional: "Photo as evidence (optional)",
    doneSubmit: "Save as done",
    doneCancel: "Cancel",
    doneMissingComment: "Please enter a text describing what was done.",
    printSubMeta: (started, pct) => `Started on ${started} · overall result ${pct}% fulfilled`,

    editUserTitle: (name) => `Edit account – ${name}`,
    editUserPrimaryStoreLabel: "Store",
    editUserAdditionalStoresLabel: "Additional stores (multiple possible)",
    editUserSave: "Save",
  },
};

// Übersetzungs-Overlay für den Kriterienkatalog (Kategorienamen + Kriterientexte),
// per stabiler ID/Kategoriename nachgeschlagen. Deutsch bleibt immer die
// serverseitige Quelle (auch für gespeicherte Audit-Snapshots) - fr/en werden
// nur zur Anzeige übersetzt.
const CATEGORY_I18N = {
  "Eingangsbereich": { fr: "Zone d'entrée", en: "Entrance area" },
  "Showroom": { fr: "Showroom", en: "Showroom" },
  "Smartphone Ausstellung": { fr: "Exposition smartphones", en: "Smartphone display" },
  "TV Wand": { fr: "Mur TV", en: "TV wall" },
  "Audiotisch": { fr: "Table audio", en: "Audio table" },
  "Smartwatch Ausstellung": { fr: "Exposition montres connectées", en: "Smartwatch display" },
  "Kamera Ausstellung": { fr: "Exposition appareils photo", en: "Camera display" },
  "Notebook Ausstellung": { fr: "Exposition ordinateurs portables", en: "Laptop display" },
  "Apple Ausstellung": { fr: "Exposition Apple", en: "Apple display" },
  "Kassenbereich": { fr: "Zone caisses", en: "Checkout area" },
  "Dienstleistungen": { fr: "Prestations de services", en: "Services" },
  "Lager": { fr: "Entrepôt", en: "Warehouse" },
  "Backoffice": { fr: "Back-office", en: "Backoffice" },
  "Sonstiges": { fr: "Divers", en: "Miscellaneous" },
  "Feedback": { fr: "Feedback", en: "Feedback" },
};

const CRITERIA_I18N = {
  "eingang-1": { fr: "Tous les autocollants nécessaires, les horaires d'ouverture et les informations sont-ils visibles à l'entrée ?", en: "Are all required stickers, opening hours and information visible at the entrance?" },
  "eingang-2": { fr: "Le distributeur de tickets est-il en bon état et fonctionnel ?", en: "Is the ticket machine in proper condition and working?" },
  "eingang-3": { fr: "La zone d'entrée est-elle propre et rangée ?", en: "Is the entrance area clean and tidy?" },
  "eingang-4": { fr: "Première impression du magasin, y compris les collaborateurs", en: "First impression of the store, including staff" },

  "showroom-1": { fr: "Propreté générale du showroom (alignement des tables / sol sale / îlots poussiéreux / écrans sales)", en: "General cleanliness of the showroom (table alignment / dirty floor / dusty islands / dirty displays)" },
  "showroom-2": { fr: "Les antivols émettent-ils un bip ?", en: "Are security tags beeping?" },
  "showroom-3": { fr: "Concept marketing (flyers, affiches, etc.)", en: "Marketing concept (flyers, posters, etc.)" },
  "showroom-4": { fr: "L'exposition du showroom est-elle à jour ?", en: "Is the showroom display up to date?" },
  "showroom-5": { fr: "Les écrans publicitaires fonctionnent-ils ?", en: "Are the advertising screens working?" },
  "showroom-6": { fr: "Machine à café propre, en ordre et rangée", en: "Coffee machine clean, tidy and organised" },
  "showroom-7": { fr: "Boîte de dépôt (Dropoffbox)", en: "Drop-off box" },
  "showroom-8": { fr: "Issues de secours dégagées", en: "Emergency exits clear" },
  "showroom-9": { fr: "Machine à brouillard", en: "Fog machine" },

  "smartphone-1": { fr: "Les smartphones fonctionnent-ils et sont-ils accessibles aux clients ?", en: "Do the smartphones work and are they accessible to customers?" },
  "smartphone-2": { fr: "Propreté de la table et des smartphones", en: "Cleanliness of the table and the smartphones" },
  "smartphone-3": { fr: "L'Applock est-il correctement installé sur les smartphones ?", en: "Is Applock correctly installed on the smartphones?" },
  "smartphone-4": { fr: "Un compte Google est-il configuré sur les smartphones ?", en: "Is a Google account set up on the smartphones?" },
  "smartphone-5": { fr: "Tous les smartphones sont-ils correctement exposés et sécurisés ?", en: "Are all smartphones correctly displayed and secured?" },
  "smartphone-6": { fr: "Les étiquettes de prix des smartphones sont-elles correctes ?", en: "Are the price labels for the smartphones correct?" },

  "tv-1": { fr: "Tous les téléviseurs fonctionnent-ils et affichent-ils le bon contenu ?", en: "Do all TVs work and show the correct content?" },
  "tv-2": { fr: "Chaque téléviseur porte-t-il la bonne étiquette énergétique ?", en: "Does each TV have the correct energy label?" },
  "tv-3": { fr: "Les étiquettes de prix des téléviseurs sont-elles correctes ?", en: "Are the price labels for the TVs correct?" },
  "tv-4": { fr: "Tous les téléviseurs sont-ils exposés ?", en: "Are all TVs on display?" },

  "audio-1": { fr: "Les étiquettes de prix des produits audio sont-elles correctes ?", en: "Are the price labels for the audio products correct?" },
  "audio-2": { fr: "Propreté de la table et des produits", en: "Cleanliness of the table and the products" },
  "audio-3": { fr: "Tous les produits sont-ils exposés et correctement sécurisés ?", en: "Are all products on display and correctly secured?" },

  "watch-1": { fr: "Les produits fonctionnent-ils et sont-ils accessibles aux clients ?", en: "Do the products work and are they accessible to customers?" },
  "watch-2": { fr: "Propreté de la table et des produits", en: "Cleanliness of the table and the products" },
  "watch-3": { fr: "Tous les produits sont-ils exposés et correctement sécurisés ?", en: "Are all products on display and correctly secured?" },
  "watch-4": { fr: "Les étiquettes de prix des produits sont-elles correctes ?", en: "Are the price labels for the products correct?" },

  "kamera-1": { fr: "Tous les appareils photo fonctionnent-ils, sont-ils accessibles aux clients et ont-ils une batterie chargée ?", en: "Do all cameras work, are they accessible to customers and do they have a charged battery?" },
  "kamera-2": { fr: "Propreté de la table et des appareils photo", en: "Cleanliness of the table and the cameras" },
  "kamera-3": { fr: "Tous les appareils photo sont-ils exposés et correctement sécurisés ?", en: "Are all cameras on display and correctly secured?" },
  "kamera-4": { fr: "Les étiquettes de prix des appareils photo sont-elles correctes ?", en: "Are the price labels for the cameras correct?" },

  "notebook-1": { fr: "Tous les ordinateurs portables fonctionnent-ils, sont-ils accessibles aux clients et en mode démo ?", en: "Do all laptops work, are they accessible to customers and in demo mode?" },
  "notebook-2": { fr: "Propreté de la table et des ordinateurs portables", en: "Cleanliness of the table and the laptops" },
  "notebook-3": { fr: "Tous les ordinateurs portables sont-ils exposés et correctement sécurisés ?", en: "Are all laptops on display and correctly secured?" },
  "notebook-4": { fr: "Les étiquettes de prix des ordinateurs portables sont-elles correctes ?", en: "Are the price labels for the laptops correct?" },

  "apple-1": { fr: "Tous les produits fonctionnent-ils, sont-ils accessibles aux clients et en mode démo ?", en: "Do all products work, are they accessible to customers and in demo mode?" },
  "apple-2": { fr: "Propreté de la table et des produits", en: "Cleanliness of the table and the products" },
  "apple-3": { fr: "Tous les produits sont-ils exposés et correctement sécurisés ?", en: "Are all products on display and correctly secured?" },
  "apple-4": { fr: "Les étiquettes de prix des produits sont-elles correctes ?", en: "Are the price labels for the products correct?" },

  "kasse-1": { fr: "Postes de travail propres et rangés", en: "Workstations clean and tidy" },
  "kasse-2": { fr: "Rangement derrière les caisses", en: "Storage behind the checkouts" },
  "kasse-3": { fr: "Téléphones de remplacement chargés et nettoyés", en: "Spare phones charged and cleaned" },

  "dienst-1": { fr: "Prestations en attente", en: "Pending services" },
  "dienst-2": { fr: "Zone de service propre et rangée", en: "Service area clean and tidy" },
  "dienst-3": { fr: "Matériel de travail disponible / stock de films", en: "Work materials available / film stock" },

  "lager-1": { fr: "Propreté générale de l'entrepôt", en: "General cleanliness of the warehouse" },
  "lager-2": { fr: "Logique de stockage (petits produits dans les petits casiers / produits lourds accessibles, etc.)", en: "Storage logic (small products in small bins / heavy products accessible, etc.)" },
  "lager-3": { fr: "Stock RMA / OVP correctement enregistré", en: "RMA / OVP stock correctly booked in" },
  "lager-4": { fr: "Élimination et recyclage", en: "Disposal and recycling" },
  "lager-5": { fr: "Données clients / autocollants PAU", en: "Customer data / PAU stickers" },
  "lager-6": { fr: "Issues de secours dégagées", en: "Emergency exits clear" },
  "lager-7": { fr: "Machine à brouillard", en: "Fog machine" },

  "backoffice-1": { fr: "Tickets logistique en attente (plus de 48h)", en: "Logistics tickets pending (over 48h)" },
  "backoffice-2": { fr: "IPS dans un statut incorrect", en: "IPS in the wrong status" },

  "sonstiges-1": { fr: "Divers", en: "Miscellaneous" },

  "feedback-1": { fr: "Feedback discuté", en: "Feedback discussed" },
};

function trCat(cat, lang) {
  if (lang === "de") return cat;
  return (CATEGORY_I18N[cat] && CATEGORY_I18N[cat][lang]) || cat;
}
function trText(item, lang) {
  if (lang === "de") return item.text;
  return (CRITERIA_I18N[item.id] && CRITERIA_I18N[item.id][lang]) || item.text;
}
// Task-Titel sind eine Kopie des Kriterientexts zum Zeitpunkt der Erstellung
// (item_key = "auditId:criterionId") - lässt sich über die ID trotzdem übersetzen.
function trTaskTitle(t, lang) {
  if (lang === "de") return t.title;
  const cid = t.item_key && t.item_key.split(":")[1];
  const tr = cid && CRITERIA_I18N[cid];
  return (tr && tr[lang]) || t.title;
}
function taskStatusLabel(status, S) {
  if (status === "offen") return S.taskStatusOffen;
  if (status === "delegiert") return S.taskStatusDelegiert;
  if (status === "erledigt") return S.taskStatusErledigt;
  return status;
}

/* ---------------- Icons (small inline SVGs, no external icon lib) ---------------- */
function Icon({ name, size = 16, color = "currentColor", fill = "none" }) {
  const common = { width: size, height: size, viewBox: "0 0 24 24", fill, stroke: color, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" };
  const paths = {
    rss: <><path d="M4 4a16 16 0 0 1 16 16" /><path d="M4 10a10 10 0 0 1 10 10" /><circle cx="5.5" cy="18.5" r="1.5" fill={color} /></>,
    clipboard: <><rect x="6" y="4" width="12" height="17" rx="1.5" /><rect x="9" y="2.5" width="6" height="3" rx="1" /></>,
    store: <><path d="M4 9l1-5h14l1 5" /><path d="M4 9v10h16V9" /><path d="M9 19v-6h6v6" /></>,
    history: <><path d="M3 12a9 9 0 1 0 3-6.7" /><path d="M3 4v5h5" /><path d="M12 8v4l3 2" /></>,
    send: <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" />,
    users: <><circle cx="9" cy="8" r="3" /><path d="M2 20c0-3.5 3-6 7-6s7 2.5 7 6" /><path d="M17 6.5a3 3 0 0 1 0 5.9" /><path d="M22 20c0-2.8-1.9-5-5-5.7" /></>,
    logout: <><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" /><path d="M16 17l5-5-5-5" /><path d="M21 12H9" /></>,
    check: <path d="M20 6L9 17l-5-5" />,
    warn: <><path d="M12 3l10 18H2L12 3z" /><path d="M12 10v4" /><circle cx="12" cy="17" r="0.6" fill={color} /></>,
    x: <><circle cx="12" cy="12" r="9" /><path d="M9.5 9.5l5 5M14.5 9.5l-5 5" /></>,
    star: <path d="M12 3l2.6 5.9 6.4.6-4.8 4.2 1.4 6.3L12 16.9 6.4 20l1.4-6.3-4.8-4.2 6.4-.6L12 3z" />,
    camera: <><path d="M4 8h3l2-2h6l2 2h3v11H4V8z" /><circle cx="12" cy="13.5" r="3.3" /></>,
    filedown: <><path d="M14 2H6a1 1 0 0 0-1 1v18a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V8l-5-6z" /><path d="M12 11v6M9.5 14.5L12 17l2.5-2.5" /></>,
    chevronleft: <path d="M15 6l-6 6 6 6" />,
    chevronright: <path d="M9 6l6 6-6 6" />,
    userplus: <><circle cx="9" cy="8" r="3" /><path d="M2 20c0-3.5 3-6 7-6s7 2.5 7 6" /><path d="M18 8v6M15 11h6" /></>,
    lock: <><rect x="5" y="11" width="14" height="9" rx="1.5" /><path d="M8 11V7a4 4 0 0 1 8 0v4" /></>,
    play: <path d="M7 4l13 8-13 8V4z" />,
    flag: <><path d="M6 3v18" /><path d="M6 4h11l-2.5 4L17 12H6" /></>,
    eye: <><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7z" /><circle cx="12" cy="12" r="3" /></>,
    gear: <><circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.9 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.9.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.9-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.9V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z" /></>,
    mail: <><rect x="3" y="5" width="18" height="14" rx="1.5" /><path d="M3 6.5l9 6.5 9-6.5" /></>,
    ban: <><circle cx="12" cy="12" r="9" /><path d="M5.8 5.8l12.4 12.4" /></>,
    menu: <><path d="M3 6h18" /><path d="M3 12h18" /><path d="M3 18h18" /></>,
  };
  return <svg {...common}>{paths[name]}</svg>;
}

const ROLE_LABEL_BASE = {
  quality: "Quality",
  shopmanager: "Shop Manager",
  leader: "Leader Retail",
};
function roleLabel(role, S) {
  if (role === "coordinator") return S.roleCoordinator;
  return ROLE_LABEL_BASE[role] || role;
}
// Icon/Farbe je Status sind sprachunabhängig, das Label kommt aus STRINGS.
const STATUS_META = {
  erfuellt: { color: "var(--ok)", icon: "check" },
  teilweise: { color: "var(--warn)", icon: "warn" },
  nicht: { color: "var(--bad)", icon: "x" },
  na: { color: "var(--muted)", icon: "ban" },
};
function statusLabel(key, S) {
  if (key === "erfuellt") return S.statusErfuellt;
  if (key === "teilweise") return S.statusTeilweise;
  if (key === "nicht") return S.statusNicht;
  if (key === "na") return S.statusNa;
  return key;
}

function scoreOf(items) {
  const counts = { erfuellt: 0, teilweise: 0, nicht: 0, na: 0, offen: 0 };
  items.forEach((i) => (i.status ? counts[i.status]++ : counts.offen++));
  // "Nicht anwendbar" fliesst nicht in die Bewertung ein.
  const scored = items.length - counts.offen - counts.na;
  const pct = scored ? Math.round((counts.erfuellt / scored) * 100) : 0;
  return { counts, pct };
}
function categoriesOf(items) {
  const seen = [];
  items.forEach((i) => { if (!seen.includes(i.cat)) seen.push(i.cat); });
  return seen;
}
// Darf über alle Stores hinweg lesen (Auswertung/Historie/Mitarbeitende-Liste)?
function canBrowseAllStores(session) {
  return session.role === "quality" || session.role === "leader" || session.deputyLeader;
}
// Liste der für diese Session zugänglichen Stores - alle Stores bei Quality/
// Leader Retail/Deputy Leader Retail, sonst der primäre Store plus über die
// Benutzerverwaltung zusätzlich angehängte Stores (session.stores, von /api/me).
function accessibleStoreList(session, allStores) {
  if (canBrowseAllStores(session)) return allStores;
  if (session.stores && session.stores.length > 1) return session.stores;
  return [session.store];
}

/* ---------------- Lightbox (antippbares Foto -> gross anzeigen) ---------------- */
function Lightbox({ src, onClose }) {
  if (!src) return null;
  return (
    <div className="lightbox" onClick={onClose}>
      <img src={src} onClick={(e) => e.stopPropagation()} />
    </div>
  );
}

/* ---------------- LineChart (einfaches, selbstgezeichnetes Liniendiagramm) ---------------- */
function LineChart({ data }) {
  const { t } = useLang();
  if (!data || data.length === 0) return <div className="empty">{t("statistikEmpty")}</div>;
  const W = 760, H = 220, padL = 34, padR = 14, padT = 14, padB = 26;
  const innerW = W - padL - padR, innerH = H - padT - padB;
  const x = (i) => padL + (data.length === 1 ? innerW / 2 : (i / (data.length - 1)) * innerW);
  const y = (pct) => padT + innerH - (pct / 100) * innerH;
  const points = data.map((d, i) => `${x(i)},${y(d.pct)}`).join(" ");
  return (
    <div className="chart-wrap">
      <svg className="linechart" viewBox={`0 0 ${W} ${H}`}>
        {[0, 25, 50, 75, 100].map((g) => (
          <line key={g} className="chart-grid" x1={padL} x2={W - padR} y1={y(g)} y2={y(g)} />
        ))}
        {[0, 25, 50, 75, 100].map((g) => (
          <text key={g} className="chart-axis-label" x={4} y={y(g) + 3}>{g}%</text>
        ))}
        <polyline className="chart-line" fill="none" points={points} />
        {data.map((d, i) => (
          <circle key={d.period} className="chart-dot" cx={x(i)} cy={y(d.pct)} r={3} />
        ))}
        {data.map((d, i) => (
          <text key={d.period} className="chart-axis-label" x={x(i)} y={H - 6} textAnchor="middle">{d.period.slice(2)}</text>
        ))}
      </svg>
    </div>
  );
}

/* ---------------- Statistik ---------------- */
function Statistik({ session }) {
  const { t } = useLang();
  const [stats, setStats] = useState(null);
  const [selected, setSelected] = useState("overall");

  useEffect(() => { api("/api/stats").then(setStats); }, []);

  if (!stats) return <div className="panel"><h1>{t("statistikTitle")}</h1><p className="sub">{t("loading")}</p></div>;

  const storeNames = Object.keys(stats.stores);

  // Mehrere zugängliche Stores (Quality/Leader Retail/Deputy Leader Retail
  // oder ein Shop Manager mit mehreren angehängten Stores) -> Dropdown.
  if (storeNames.length > 1) {
    const data = selected === "overall" ? stats.overall : (stats.stores[selected] || []);
    return (
      <div className="panel">
        <div className="panel-head">
          <div><h1>{t("statistikTitle")}</h1><p className="sub">{t("statistikSub")}</p></div>
          <div className="field inline">
            <label>{t("filterStoreLabel")}</label>
            <select value={selected} onChange={(e) => setSelected(e.target.value)}>
              <option value="overall">{t("statistikOverall")}</option>
              {storeNames.map((s) => <option key={s} value={s}>{s}</option>)}
            </select>
          </div>
        </div>
        <LineChart data={data} />
      </div>
    );
  }

  // Nur ein zugänglicher Store: Gesamt + dieser eine Store.
  const soleStore = storeNames[0] || session.store;
  return (
    <div className="panel">
      <h1>{t("statistikTitle")}</h1>
      <p className="sub">{t("statistikSub")}</p>
      <h2 className="stat-subtitle">{t("statistikOverall")}</h2>
      <LineChart data={stats.overall} />
      <h2 className="stat-subtitle">{soleStore}</h2>
      <LineChart data={stats.stores[soleStore] || []} />
    </div>
  );
}

/* ---------------- Root (verwaltet die Sprache, unabhängig vom Login-Status) ---------------- */
function Root() {
  const [lang, setLangState] = useState(() => {
    try { return localStorage.getItem("lang") || "de"; } catch { return "de"; }
  });
  function setLang(l) {
    setLangState(l);
    try { localStorage.setItem("lang", l); } catch {}
  }
  const t = (key) => (STRINGS[lang] && STRINGS[lang][key] !== undefined ? STRINGS[lang][key] : STRINGS.de[key]);

  return (
    <LangContext.Provider value={{ lang, setLang, t }}>
      <App />
    </LangContext.Provider>
  );
}

/* ---------------- App root ---------------- */
function App() {
  const { lang, t } = useLang();
  const [session, setSession] = useState(null);
  const [checking, setChecking] = useState(true);
  const [meta, setMeta] = useState(null);
  const [tab, setTab] = useState("feed");
  const [sidebarOpen, setSidebarOpen] = useState(() => {
    try {
      const v = localStorage.getItem("sidebarOpen");
      if (v !== null) return v === "1";
      // Kein gespeicherter Zustand: auf dem Telefon startet die Schublade
      // eingefahren (spart Platz), am Desktop startet die Sidebar offen.
      return window.innerWidth > 720;
    } catch { return true; }
  });
  function toggleSidebar() {
    setSidebarOpen((open) => {
      const next = !open;
      try { localStorage.setItem("sidebarOpen", next ? "1" : "0"); } catch {}
      return next;
    });
  }
  function toggleSidebarClose() {
    setSidebarOpen(false);
    try { localStorage.setItem("sidebarOpen", "0"); } catch {}
  }

  useEffect(() => {
    if (!getToken()) { setChecking(false); return; }
    api("/api/me").then((r) => setSession(r.user)).catch(() => {}).finally(() => setChecking(false));
  }, []);

  useEffect(() => {
    if (session) api("/api/meta").then(setMeta).catch(() => {});
  }, [session]);

  function handleLogin(user) { setSession(user); setTab("feed"); }
  function logout() { localStorage.removeItem("token"); setSession(null); }

  if (checking) return <div className="login"><span style={{ color: "var(--muted)" }}>{t("loading")}</span></div>;
  if (!session) return <Login onLogin={handleLogin} />;
  if (!meta) return <div className="login"><span style={{ color: "var(--muted)" }}>{t("loading")}</span></div>;

  const nav = navFor(session.role, t);

  return (
    <div className={"app" + (sidebarOpen ? "" : " sidebar-collapsed")}>
      <aside className={"sidebar" + (sidebarOpen ? "" : " collapsed")}>
        <div className="brand">
          <button className="sidebar-toggle" onClick={toggleSidebar} title={t("toggleSidebar")} aria-label={t("toggleSidebar")}>
            <Icon name="menu" size={18} />
          </button>
          <div className="brand-logo"><img src="/digitec-galaxus-logo.png" alt="Digitec Galaxus" /></div>
          <span>DG Store Audit</span>
        </div>
        <div className="session-card">
          <div className="session-role">
            {roleLabel(session.role, STRINGS[lang])}
            {session.deputyLeader && <span className="deputy-tag">Deputy Leader Retail</span>}
          </div>
          {session.store && <div className="session-store">{session.store}</div>}
          {session.name && <div className="session-name">{session.name}</div>}
        </div>
        <nav>
          {nav.map((n) => (
            <button key={n.key} className={"navbtn" + (tab === n.key ? " active" : "")} onClick={() => { setTab(n.key); if (window.innerWidth <= 720) toggleSidebarClose(); }}>
              <Icon name={n.icon} size={17} /><span>{n.label}</span>
            </button>
          ))}
        </nav>
        <button className={"navbtn" + (tab === "konto" ? " active" : "")} onClick={() => setTab("konto")}>
          <Icon name="lock" size={17} /><span>{t("konto")}</span>
        </button>
        <button className="navbtn logout" onClick={logout}><Icon name="logout" size={17} /><span>{t("abmelden")}</span></button>
      </aside>
      {sidebarOpen && <div className="sidebar-scrim" onClick={toggleSidebar} />}
      <button className="mobile-menu-btn" onClick={toggleSidebar} aria-label={t("toggleSidebar")} title={t("toggleSidebar")}>
        <Icon name="menu" size={19} />
      </button>
      <main className="content">
        {tab === "feed" && <Feed />}
        {tab === "audit" && <AuditRun meta={meta} session={session} />}
        {tab === "auswertung" && <Auswertung session={session} meta={meta} />}
        {tab === "historie" && <Historie session={session} meta={meta} />}
        {tab === "tasks" && <Tasks session={session} />}
        {tab === "mitarbeitende" && <Mitarbeitende session={session} meta={meta} />}
        {tab === "benutzer" && <Benutzerverwaltung meta={meta} />}
        {tab === "statistik" && <Statistik session={session} />}
        {tab === "konto" && <Konto session={session} />}
      </main>
    </div>
  );
}

function navFor(role, t) {
  const feed = { key: "feed", label: t("navHighlights"), icon: "rss" };
  const audit = { key: "audit", label: t("navAudit"), icon: "clipboard" };
  const auswertung = { key: "auswertung", label: t("navAuswertung"), icon: "store" };
  const historie = { key: "historie", label: t("navHistorie"), icon: "history" };
  const tasks = { key: "tasks", label: t("navTasks"), icon: "send" };
  const team = { key: "mitarbeitende", label: t("navMitarbeitende"), icon: "users" };
  const benutzer = { key: "benutzer", label: t("navBenutzer"), icon: "gear" };
  const statistik = { key: "statistik", label: t("navStatistik"), icon: "history" };
  // Benutzerverwaltung deckt nur noch Deputy Leader Retail ab - neue Shop
  // Manager/Showroom Koordinatoren (inkl. Login) werden weiterhin über
  // "Mitarbeitende" angelegt, daher braucht Quality diesen Punkt wieder.
  // "Audit durchführen" ist für Leader Retail ausgeblendet - er sieht laufende
  // Audits ohnehin nicht mehr aktiv (nur Quality startet/bearbeitet Audits).
  if (role === "quality") return [feed, audit, auswertung, historie, tasks, team, benutzer, statistik];
  if (role === "leader") return [feed, auswertung, historie, tasks, team, statistik];
  if (role === "shopmanager") return [feed, auswertung, historie, tasks, team, statistik];
  return [feed, tasks];
}

/* ---------------- Login ---------------- */
function Login({ onLogin }) {
  const { lang, setLang, t } = useLang();
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");
  const [busy, setBusy] = useState(false);

  async function submit(e) {
    e.preventDefault();
    setError(""); setBusy(true);
    try {
      const r = await api("/api/login", { method: "POST", body: { username, password } });
      localStorage.setItem("token", r.token);
      onLogin(r.user);
    } catch (err) {
      setError(err.message === "unauth" ? t("loginFailed") : err.message);
    } finally {
      setBusy(false);
    }
  }

  return (
    <div className="login">
      <form className="login-card" onSubmit={submit}>
        <div className="login-logo"><img src="/digitec-galaxus-logo.png" alt="Digitec Galaxus" /></div>
        <div className="login-brand">DG Store Audit</div>
        <p className="login-sub">{t("loginSub")}</p>
        {error && <div className="error-box">{error}</div>}
        <div className="field">
          <label>{t("language")}</label>
          <select value={lang} onChange={(e) => setLang(e.target.value)}>
            <option value="de">Deutsch</option>
            <option value="fr">Français</option>
            <option value="en">English</option>
          </select>
        </div>
        <div className="field">
          <label>{t("username")}</label>
          <input autoFocus value={username} onChange={(e) => setUsername(e.target.value)} placeholder={t("usernamePlaceholder")} />
        </div>
        <div className="field">
          <label>{t("password")}</label>
          <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder={t("passwordPlaceholder")} />
        </div>
        <button className="primary" disabled={busy || !username || !password}>
          <Icon name="lock" size={15} /> {t("loginButton")}
        </button>
      </form>
    </div>
  );
}

/* ---------------- Konto (E-Mail + Passwort) ---------------- */
function Konto({ session }) {
  const { t } = useLang();
  const [email, setEmail] = useState(session.email || "");
  const [emailStatus, setEmailStatus] = useState(null);
  const [currentPassword, setCurrentPassword] = useState("");
  const [newPassword, setNewPassword] = useState("");
  const [confirm, setConfirm] = useState("");
  const [pwError, setPwError] = useState("");
  const [pwOk, setPwOk] = useState(false);
  const [busy, setBusy] = useState(false);

  async function saveEmail(e) {
    e.preventDefault();
    setEmailStatus(null);
    try {
      await api("/api/me/email", { method: "PUT", body: { email } });
      setEmailStatus("ok");
    } catch (err) {
      setEmailStatus(err.message);
    }
  }

  async function submitPassword(e) {
    e.preventDefault();
    setPwError(""); setPwOk(false);
    if (newPassword !== confirm) { setPwError(t("passwordMismatch")); return; }
    setBusy(true);
    try {
      await api("/api/change-password", { method: "POST", body: { currentPassword, newPassword } });
      setPwOk(true);
      setCurrentPassword(""); setNewPassword(""); setConfirm("");
    } catch (err) {
      setPwError(err.message);
    } finally {
      setBusy(false);
    }
  }

  return (
    <div className="panel">
      <h1>{t("kontoTitle")}</h1>
      <p className="sub">{t("kontoSub")}</p>

      <div className="konto-section">
        <h2>{t("emailSectionTitle")}</h2>
        <p className="sub">{t("emailSectionSub")}</p>
        <form className="password-form" onSubmit={saveEmail}>
          {emailStatus === "ok" && <div className="ok-box">{t("emailSavedOk")}</div>}
          {emailStatus && emailStatus !== "ok" && <div className="error-box">{emailStatus}</div>}
          <div className="field">
            <label>{t("emailLabel")}</label>
            <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder={t("emailPlaceholder")} />
          </div>
          <button className="primary"><Icon name="mail" size={15} /> {t("saveEmailButton")}</button>
        </form>
      </div>

      <div className="konto-section">
        <h2>{t("passwordSectionTitle")}</h2>
        <form className="password-form" onSubmit={submitPassword}>
          {pwError && <div className="error-box">{pwError}</div>}
          {pwOk && <div className="ok-box">{t("passwordSavedOk")}</div>}
          <div className="field">
            <label>{t("currentPassword")}</label>
            <input type="password" value={currentPassword} onChange={(e) => setCurrentPassword(e.target.value)} />
          </div>
          <div className="field">
            <label>{t("newPassword")}</label>
            <input type="password" value={newPassword} onChange={(e) => setNewPassword(e.target.value)} />
          </div>
          <div className="field">
            <label>{t("confirmPassword")}</label>
            <input type="password" value={confirm} onChange={(e) => setConfirm(e.target.value)} />
          </div>
          <button className="primary" disabled={busy || !currentPassword || !newPassword}>
            <Icon name="lock" size={15} /> {t("savePasswordButton")}
          </button>
        </form>
      </div>
    </div>
  );
}

/* ---------------- Highlights ---------------- */
function Feed() {
  const { t } = useLang();
  const [highlights, setHighlights] = useState(null);
  const [lightbox, setLightbox] = useState(null);
  useEffect(() => { api("/api/highlights").then(setHighlights).catch(() => setHighlights([])); }, []);
  if (highlights === null) return <div className="panel"><h1>{t("highlightsTitle")}</h1><p className="sub">{t("loading")}</p></div>;
  return (
    <div className="panel">
      <h1>{t("highlightsTitle")}</h1>
      <p className="sub">{t("highlightsSub")}</p>
      {highlights.length === 0 && <div className="empty">{t("highlightsEmpty")}</div>}
      <div className="feed-list">
        {highlights.map((h) => (
          <div key={h.id} className="feed-item">
            <div className="feed-item-top">
              <Icon name="star" size={15} color="var(--accent)" fill="var(--accent)" />
              <span className="feed-store">{h.store}</span>
              <span className="feed-date">{h.date}</span>
            </div>
            <div className="feed-text">{h.text}</div>
            {h.comment && <div className="feed-comment">„{h.comment}"</div>}
            {h.photo && <img src={h.photo} className="thumb tappable" alt={t("photoAlt")} onClick={() => setLightbox(h.photo)} />}
          </div>
        ))}
      </div>
      <Lightbox src={lightbox} onClose={() => setLightbox(null)} />
    </div>
  );
}

/* ---------------- Audit durchführen ---------------- */
function AuditRun({ meta, session }) {
  const { lang, t } = useLang();
  const isQuality = session.role === "quality";
  const readOnly = session.role === "leader";
  const [store, setStore] = useState(meta.stores[0]);
  const [audit, setAudit] = useState(undefined); // undefined = lädt, null = keins
  const [catIndex, setCatIndex] = useState(0);
  const [starting, setStarting] = useState(false);
  const [completing, setCompleting] = useState(false);
  const [completeError, setCompleteError] = useState("");
  const [cancelling, setCancelling] = useState(false);
  const [justCompletedStore, setJustCompletedStore] = useState(null);
  const [justCancelledStore, setJustCancelledStore] = useState(null);

  function load(s) {
    setAudit(undefined);
    api("/api/audit/" + encodeURIComponent(s) + "/in-progress").then((r) => {
      setAudit(r.audit);
      setCatIndex(0);
    });
  }
  useEffect(() => { load(store); }, [store]);

  function changeStore(s) {
    setJustCompletedStore(null);
    setJustCancelledStore(null);
    setStore(s);
  }

  async function startAudit() {
    setStarting(true);
    setJustCompletedStore(null);
    setJustCancelledStore(null);
    try {
      const r = await api(`/api/audit/${encodeURIComponent(store)}/start`, { method: "POST" });
      setAudit(r.audit);
      setCatIndex(0);
    } finally {
      setStarting(false);
    }
  }

  async function updateItem(cid, patch) {
    setAudit((prev) => ({ ...prev, items: prev.items.map((i) => (i.id === cid ? { ...i, ...patch } : i)) }));
    await api(`/api/audit/byid/${audit.id}/item/${cid}`, { method: "PUT", body: patch });
  }
  async function uploadPhoto(cid, file) {
    const fd = new FormData(); fd.append("photo", file);
    const r = await api(`/api/audit/byid/${audit.id}/item/${cid}/photo`, { method: "POST", body: fd });
    setAudit((prev) => ({ ...prev, items: prev.items.map((i) => (i.id === cid ? { ...i, photo: r.url } : i)) }));
  }
  async function completeAudit() {
    setCompleting(true);
    setCompleteError("");
    try {
      await api(`/api/audit/byid/${audit.id}/complete`, { method: "POST" });
      setJustCompletedStore(store);
      setAudit(null);
    } catch (e) {
      setCompleteError(e.message);
    } finally {
      setCompleting(false);
    }
  }
  async function cancelAudit() {
    if (!window.confirm(t("cancelConfirm"))) return;
    setCancelling(true);
    try {
      await api(`/api/audit/byid/${audit.id}/cancel`, { method: "POST" });
      setJustCancelledStore(store);
      setAudit(null);
    } finally {
      setCancelling(false);
    }
  }

  const storeSelector = (
    <div className="field inline">
      <label>{t("storeLabel")}</label>
      <select value={store} onChange={(e) => changeStore(e.target.value)}>
        {meta.stores.map((s) => <option key={s} value={s}>{s}</option>)}
      </select>
    </div>
  );

  if (audit === undefined) {
    return <div className="panel"><h1>{t("auditTitle")}</h1><p className="sub">{t("loading")}</p></div>;
  }

  if (audit === null) {
    return (
      <div className="panel">
        <div className="panel-head">
          <div><h1>{t("auditTitle")}</h1><p className="sub">{t("auditNoneRunning")}</p></div>
          {storeSelector}
        </div>
        {justCompletedStore === store && (
          <div className="ok-box"><Icon name="check" size={14} /> {t("auditJustCompleted")(store)}</div>
        )}
        {justCancelledStore === store && (
          <div className="error-box"><Icon name="ban" size={14} /> {t("auditJustCancelled")(store)}</div>
        )}
        {readOnly ? (
          <div className="empty">{t("auditLeaderEmpty")}</div>
        ) : (
          <div className="start-audit">
            <p>{t("auditStartHint")}</p>
            <button className="primary" disabled={starting} onClick={startAudit}><Icon name="play" size={15} /> {t("auditStartButton")}</button>
          </div>
        )}
      </div>
    );
  }

  // Leader Retail: reine Leseansicht des laufenden Audits, kein Bearbeiten.
  if (readOnly) {
    return (
      <div className="panel">
        <div className="panel-head">
          <div>
            <h1>{t("auditTitle")} <span className="readonly-tag"><Icon name="eye" size={13} /> {t("readonlyTag")}</span></h1>
            <p className="sub">{t("auditRunningSince")(audit.started_at, audit.started_by)}</p>
          </div>
          {storeSelector}
        </div>
        <AuditDetail audit={audit} isQuality={false} />
      </div>
    );
  }

  // Eine Frage pro Bildschirm, statt einer Liste pro Kategorie - deutlich
  // übersichtlicher auf Mobile. catIndex zählt hier über ALLE Kriterien
  // hinweg (nicht mehr über Kategorien), "Weiter"/"Zurück" bewegen sich
  // Schritt für Schritt, auch über Kategoriegrenzen hinweg.
  const total = audit.items.length;
  const stepIndex = Math.min(catIndex, Math.max(total - 1, 0));
  const item = audit.items[stepIndex];
  // Bei "teilweise erfüllt"/"nicht erfüllt" ist eine Begründung Pflicht -
  // ein Kriterium zählt erst als beantwortet, wenn die dazu auch eingetragen ist.
  const isAnswered = (i) => !!i.status && (!(i.status === "teilweise" || i.status === "nicht") || !!(i.comment && i.comment.trim()));
  const done = audit.items.filter(isAnswered).length;
  const isLast = stepIndex === total - 1;
  const allAnswered = done === total;
  const commentMissing = item && (item.status === "teilweise" || item.status === "nicht") && !(item.comment && item.comment.trim());
  const naAllowedById = {};
  meta.criteria.forEach((c) => { naAllowedById[c.id] = !!c.naAllowed; });
  const progressPct = total ? Math.round(((stepIndex + 1) / total) * 100) : 0;

  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h1>{t("auditTitle")}</h1>
          <p className="sub">{t("auditStartedProgress")(audit.started_at, done, total)}</p>
        </div>
        <div className="head-actions">
          {storeSelector}
          <button className="secondary danger" disabled={cancelling} onClick={cancelAudit}>
            <Icon name="ban" size={15} /> {t("cancelButton")}
          </button>
        </div>
      </div>

      <div className="step-progress">
        <div className="step-progress-bar"><div className="step-progress-fill" style={{ width: progressPct + "%" }} /></div>
        <div className="step-progress-label">{trCat(item.cat, lang)} · {stepIndex + 1}/{total}</div>
      </div>

      {item && (
        <CriterionRow
          key={item.id}
          item={item}
          naAllowed={naAllowedById[item.id]}
          onChange={(p) => updateItem(item.id, p)}
          onPhoto={(f) => uploadPhoto(item.id, f)}
          big
        />
      )}

      <div className="step-nav">
        <button className="secondary" disabled={stepIndex === 0} onClick={() => setCatIndex((i) => i - 1)}>
          <Icon name="chevronleft" size={15} /> {t("back")}
        </button>
        {!isLast && (
          <button className="primary" disabled={commentMissing} onClick={() => setCatIndex((i) => i + 1)}>
            {t("next")} <Icon name="chevronright" size={15} />
          </button>
        )}
      </div>

      {isLast && (
        <div className="complete-row">
          {!allAnswered && (
            <div className="error-box">{commentMissing ? t("commentRequiredHint") : t("completeMissing")}</div>
          )}
          {completeError && <div className="error-box">{completeError}</div>}
          <button className="primary" disabled={completing || !allAnswered} onClick={completeAudit}>
            <Icon name="flag" size={16} /> {t("completeButton")}
          </button>
        </div>
      )}
    </div>
  );
}

// Kleines Auswahlmenü statt direktem Klick auf ein natives <input type=file> -
// so wird auf dem Handy immer explizit "Kamera" oder "Dateien" angeboten,
// statt sich auf das (je nach Browser/Gerät uneinheitliche) Standardverhalten
// des nackten Datei-Inputs zu verlassen.
function PhotoPickerButton({ label, onPick, selectedName }) {
  const { t } = useLang();
  const [open, setOpen] = useState(false);
  const cameraRef = useRef(null);
  const filesRef = useRef(null);
  const wrapRef = useRef(null);

  useEffect(() => {
    if (!open) return;
    function onDocClick(e) {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    }
    document.addEventListener("mousedown", onDocClick);
    return () => document.removeEventListener("mousedown", onDocClick);
  }, [open]);

  function handleFile(e) {
    const f = e.target.files[0] || null;
    e.target.value = "";
    setOpen(false);
    if (f) onPick(f);
  }

  return (
    <div className="photo-picker" ref={wrapRef}>
      <input ref={cameraRef} type="file" accept="image/*" capture="environment" hidden onChange={handleFile} />
      <input ref={filesRef} type="file" accept="image/*" hidden onChange={handleFile} />
      <button type="button" className="secondary" onClick={() => setOpen((o) => !o)}>
        <Icon name="camera" size={15} /> {selectedName || label}
      </button>
      {open && (
        <div className="photo-picker-menu">
          <button type="button" onClick={() => cameraRef.current.click()}><Icon name="camera" size={14} /> {t("photoPickCamera")}</button>
          <button type="button" onClick={() => filesRef.current.click()}><Icon name="filedown" size={14} /> {t("photoPickFiles")}</button>
        </div>
      )}
    </div>
  );
}

function CriterionRow({ item, naAllowed, onChange, onPhoto, big }) {
  const { lang, t } = useLang();
  const S = STRINGS[lang];
  const showComment = !!item.status && item.status !== "na";
  const needsPhoto = item.status === "teilweise" || item.status === "nicht";

  return (
    <div className={"crit" + (big ? " crit-step" : "")}>
      <div className="crit-top">
        <div className="crit-text">{trText(item, lang)}</div>
        <button className="star" onClick={() => onChange({ highlighted: !item.highlighted })} title={t("markHighlight")}>
          <Icon name="star" size={big ? 20 : 17} color={item.highlighted ? "var(--accent)" : "var(--muted)"} fill={item.highlighted ? "var(--accent)" : "none"} />
        </button>
      </div>
      <div className={"status-row" + (big ? " status-row-stacked" : "")}>
        {Object.entries(STATUS_META)
          .filter(([key]) => key !== "na" || naAllowed)
          .map(([key, m]) => (
            <button key={key} className={"status-btn" + (big ? " status-btn-big" : "") + (item.status === key ? " sel" : "")} style={{ "--c": m.color }} onClick={() => onChange({ status: key })}>
              <Icon name={m.icon} size={15} /> {statusLabel(key, S)}
            </button>
          ))}
      </div>
      {showComment && (
        <div className="proof">
          <CommentField
            value={item.comment}
            placeholder={needsPhoto ? t("commentPlaceholderNeeded") : t("commentPlaceholderOptional")}
            onSave={(comment) => onChange({ comment })}
          />
          {needsPhoto && !(item.comment && item.comment.trim()) && (
            <div className="comment-required-hint">{t("commentRequiredHint")}</div>
          )}
        </div>
      )}
      {/* Foto-Upload ist bei jedem Kriterium möglich, unabhängig vom Status - auch vor der Bewertung. */}
      <div className="photo-row">
        <PhotoPickerButton label={t("photoUploadButton")} onPick={onPhoto} />
        {item.photo && <img src={item.photo} alt={t("photoAlt")} className="thumb" />}
      </div>
    </div>
  );
}

// Eigener Speicher-Zyklus statt Speichern bei jedem Tastendruck: sonst können
// mehrere parallele PUT-Requests (einer pro Zeichen) in falscher Reihenfolge
// beim Server ankommen und einen bereits gespeicherten, längeren Text wieder
// mit einer älteren, kürzeren Zwischenversion überschreiben - der Kommentar
// "geht verloren", obwohl im Feld selbst der volle Text sichtbar war.
// Stattdessen: sofort responsives Tippen lokal, gespeichert wird erst nach
// einer kurzen Tippause, beim Verlassen des Feldes und beim Unmount.
function CommentField({ value, placeholder, onSave }) {
  const [draft, setDraft] = useState(value);
  const draftRef = useRef(value);
  const timerRef = useRef(null);
  const onSaveRef = useRef(onSave);
  onSaveRef.current = onSave;

  useEffect(() => { setDraft(value); draftRef.current = value; }, [value]);

  function flush() {
    if (timerRef.current) {
      clearTimeout(timerRef.current);
      timerRef.current = null;
      onSaveRef.current(draftRef.current);
    }
  }
  useEffect(() => () => flush(), []);

  function handleChange(e) {
    const v = e.target.value;
    setDraft(v);
    draftRef.current = v;
    if (timerRef.current) clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => { timerRef.current = null; onSaveRef.current(draftRef.current); }, 700);
  }

  return <textarea placeholder={placeholder} value={draft} onChange={handleChange} onBlur={flush} />;
}

/* ---------------- Gemeinsame Detailansicht (Auswertung + Historie + Leader-Ansicht) ---------------- */
function AuditDetail({ audit, isQuality, onFreigeben }) {
  const { lang, t } = useLang();
  const S = STRINGS[lang];
  const [filter, setFilter] = useState("alle");
  const [lightbox, setLightbox] = useState(null);
  const { counts, pct } = scoreOf(audit.items);
  const visible = audit.items.filter((i) => (filter === "alle" ? true : i.status === filter));
  const unreleased = audit.items.filter((i) => (i.status === "nicht" || i.status === "teilweise") && !i.released);

  return (
    <>
      <div className="score-row">
        <div className="score-big">{pct}%<span>{t("erfuelltSuffix")}</span></div>
        <div className="score-legend">
          <span><Icon name="check" size={14} color="var(--ok)" /> {counts.erfuellt} {S.statusErfuellt}</span>
          <span><Icon name="warn" size={14} color="var(--warn)" /> {counts.teilweise} {S.statusTeilweise}</span>
          <span><Icon name="x" size={14} color="var(--bad)" /> {counts.nicht} {S.statusNicht}</span>
          {counts.na > 0 && <span><Icon name="ban" size={14} color="var(--muted)" /> {counts.na} {S.statusNa}</span>}
        </div>
        {isQuality && onFreigeben && unreleased.length > 0 && (
          <button className="primary small" onClick={onFreigeben}><Icon name="send" size={14} /> {t("freigebenButton")(unreleased.length)}</button>
        )}
      </div>

      <div className="tabs">
        {["alle", "erfuellt", "teilweise", "nicht", "na"].map((f) => (
          <button key={f} className={"tab" + (filter === f ? " sel" : "")} onClick={() => setFilter(f)}>
            {f === "alle" ? t("filterAlle") : statusLabel(f, S)}
          </button>
        ))}
      </div>

      <div className="crit-list-readonly">
        {visible.length === 0 && <div className="empty">{t("emptyEntries")}</div>}
        {visible.map((i) => (
          <div key={i.id} className="crit-ro">
            <div className="crit-ro-top">
              {i.status ? (
                <span className="pill" style={{ "--c": STATUS_META[i.status].color }}><Icon name={STATUS_META[i.status].icon} size={12} /> {statusLabel(i.status, S)}</span>
              ) : <span className="pill offen">{t("offen")}</span>}
              <span className="crit-cat">{trCat(i.cat, lang)}</span>
              {i.highlighted && <Icon name="star" size={13} fill="var(--accent)" color="var(--accent)" />}
              {i.released && <span className="tag">{t("releasedTag")}</span>}
            </div>
            <div className="crit-text">{trText(i, lang)}</div>
            {i.comment && <div className="crit-comment">{i.comment}</div>}
            {i.photo && <img src={i.photo} className="thumb tappable" alt={t("photoAlt")} onClick={() => setLightbox(i.photo)} />}
          </div>
        ))}
      </div>
      <Lightbox src={lightbox} onClose={() => setLightbox(null)} />
    </>
  );
}

/* ---------------- Auswertung (letztes abgeschlossenes Audit) ---------------- */
function Auswertung({ session, meta }) {
  const { t } = useLang();
  const isQuality = session.role === "quality";
  const storeList = accessibleStoreList(session, meta.stores);
  const [store, setStore] = useState(storeList[0]);
  const [audit, setAudit] = useState(undefined);
  const [deleteError, setDeleteError] = useState("");

  function load(s) {
    setAudit(undefined);
    setDeleteError("");
    api("/api/audit/" + encodeURIComponent(s) + "/latest-completed").then((r) => setAudit(r.audit));
  }
  useEffect(() => { load(store); }, [store]);

  async function deleteAudit() {
    if (!window.confirm(t("deleteAuditConfirm"))) return;
    try {
      await api(`/api/audit/byid/${audit.id}`, { method: "DELETE" });
      load(store);
    } catch (e) {
      setDeleteError(e.message);
      window.alert(e.message);
    }
  }

  // Store + Datum sollen auch als PDF-Dateiname (Browser-Druckdialog schlägt
  // den Seitentitel vor) und im gedruckten Dokument selbst ersichtlich sein.
  useEffect(() => {
    if (audit) document.title = `DG Store Audit – Auswertung ${store} – ${audit.completed_at}`;
    return () => { document.title = "DG Store Audit"; };
  }, [audit, store]);

  async function freigeben() {
    await api(`/api/audit/byid/${audit.id}/freigeben`, { method: "POST" });
    load(store);
  }

  const storeSelector = storeList.length > 1 && (
    <div className="field inline">
      <label>{t("storeLabel")}</label>
      <select value={store} onChange={(e) => setStore(e.target.value)}>
        {storeList.map((s) => <option key={s} value={s}>{s}</option>)}
      </select>
    </div>
  );

  if (audit === undefined) return <div className="panel"><h1>{t("auswertungTitle")}</h1><p className="sub">{t("loading")}</p></div>;

  if (audit === null) {
    return (
      <div className="panel">
        <div className="panel-head">
          <div><h1>{t("auswertungTitle")}</h1><p className="sub">{t("auswertungNone")}</p></div>
          {storeSelector}
        </div>
        <div className="empty">{t("auswertungEmptyHint")}</div>
      </div>
    );
  }

  const { pct } = scoreOf(audit.items);

  return (
    <div className="panel">
      {/* Nur im PDF-Export sichtbar (siehe @media print) - macht Store und Ergebnis im Dokument selbst ersichtlich. */}
      <div className="print-header">
        <div className="print-header-left">
          <img src="/digitec-galaxus-logo.png" alt="Digitec Galaxus" className="print-logo" />
          <div className="print-title">{t("printTitle")}</div>
          <div className="print-store">{store}</div>
        </div>
        <div className="print-header-right">
          <div className="print-date">{audit.completed_at}</div>
        </div>
      </div>
      <div className="print-sub-meta">{t("printSubMeta")(audit.started_at, pct)}</div>
      <div className="panel-head">
        <div>
          <h1>{t("auswertungTitle")}</h1>
          <p className="sub">{store} · {t("auswertungSub")(audit.completed_at, audit.started_at)}</p>
        </div>
        <div className="head-actions">
          {storeSelector}
          <button className="secondary" onClick={() => window.print()}><Icon name="filedown" size={15} /> {t("exportPdfButton")}</button>
          {isQuality && (
            <button className="secondary danger" onClick={deleteAudit}><Icon name="ban" size={15} /> {t("deleteAuditButton")}</button>
          )}
        </div>
      </div>
      {deleteError && <div className="error-box">{deleteError}</div>}
      <AuditDetail audit={audit} isQuality={isQuality} onFreigeben={freigeben} />
    </div>
  );
}

/* ---------------- Vergangene Audits ---------------- */
function Historie({ session, meta }) {
  const { t } = useLang();
  const isQuality = session.role === "quality";
  const storeList = accessibleStoreList(session, meta.stores);
  const [store, setStore] = useState(storeList[0]);
  const [list, setList] = useState(undefined);
  const [openId, setOpenId] = useState(null);
  const [detail, setDetail] = useState(undefined);

  function load(s) {
    setList(undefined); setOpenId(null); setDetail(undefined);
    api("/api/audit/" + encodeURIComponent(s) + "/history").then(setList);
  }
  useEffect(() => { load(store); }, [store]);

  function openAudit(id) {
    setOpenId(id); setDetail(undefined);
    api("/api/audit/byid/" + id).then((r) => setDetail(r.audit));
  }

  async function deleteAudit(id) {
    if (!window.confirm(t("deleteAuditConfirm"))) return;
    try {
      await api(`/api/audit/byid/${id}`, { method: "DELETE" });
      load(store);
    } catch (e) {
      window.alert(e.message);
    }
  }

  const storeSelector = storeList.length > 1 && (
    <div className="field inline">
      <label>{t("storeLabel")}</label>
      <select value={store} onChange={(e) => setStore(e.target.value)}>
        {storeList.map((s) => <option key={s} value={s}>{s}</option>)}
      </select>
    </div>
  );

  if (openId) {
    return (
      <div className="panel">
        <div className="panel-head">
          <div>
            <button className="secondary small" onClick={() => setOpenId(null)}><Icon name="chevronleft" size={14} /> {t("backToList")}</button>
          </div>
          {isQuality && (
            <button className="secondary danger" onClick={() => deleteAudit(openId)}><Icon name="ban" size={15} /> {t("deleteAuditButton")}</button>
          )}
        </div>
        {detail === undefined ? <p className="sub">{t("loading")}</p> : (
          <>
            <h1>{t("auditOf")(store)}</h1>
            <p className="sub">{t("historieDetailSub")(detail.completed_at, detail.started_at, detail.started_by)}</p>
            <AuditDetail audit={detail} isQuality={false} />
          </>
        )}
      </div>
    );
  }

  return (
    <div className="panel">
      <div className="panel-head">
        <div><h1>{t("historieTitle")}</h1><p className="sub">{t("historieSub")(store)}</p></div>
        {storeSelector}
      </div>
      {list === undefined && <p className="sub">{t("loading")}</p>}
      {list && list.length === 0 && <div className="empty">{t("historieEmpty")}</div>}
      <div className="history-list">
        {list && list.map((a) => (
          <button key={a.id} className="history-row" onClick={() => openAudit(a.id)}>
            <div className="history-main">
              <div className="history-date">{t("completedOn")(a.completed_at)}</div>
              <div className="history-meta">{t("startedOnBy")(a.started_at, a.started_by)}</div>
            </div>
            <div className="history-score">{a.pct}%</div>
            <Icon name="chevronright" size={16} color="var(--muted)" />
          </button>
        ))}
      </div>
    </div>
  );
}

/* ---------------- Task erledigen (Pflichttext + optionales Foto) ---------------- */
function TaskDoneForm({ onSubmit, onCancel }) {
  const { t } = useLang();
  const [comment, setComment] = useState("");
  const [file, setFile] = useState(null);
  const [error, setError] = useState("");
  const [busy, setBusy] = useState(false);

  async function submit() {
    if (!comment.trim()) { setError(t("doneMissingComment")); return; }
    setError(""); setBusy(true);
    try {
      await onSubmit(comment.trim(), file);
    } finally {
      setBusy(false);
    }
  }

  return (
    <div className="task-done-form">
      {error && <div className="error-box">{error}</div>}
      <div className="field">
        <label>{t("doneCommentLabel")}</label>
        <textarea value={comment} onChange={(e) => setComment(e.target.value)} placeholder={t("doneCommentPlaceholder")} />
      </div>
      <div className="field">
        <label>{t("doneUploadOptional")}</label>
        <PhotoPickerButton label={t("photoUploadButton")} selectedName={file ? file.name : null} onPick={setFile} />
      </div>
      <div className="task-done-actions">
        <button className="secondary small" disabled={busy} onClick={onCancel}>{t("doneCancel")}</button>
        <button className="primary small" disabled={busy} onClick={submit}><Icon name="check" size={14} /> {t("doneSubmit")}</button>
      </div>
    </div>
  );
}

/* ---------------- Tasks ---------------- */
function Tasks({ session }) {
  const { lang, t } = useLang();
  const S = STRINGS[lang];
  const [tasks, setTasks] = useState(null);
  const [employees, setEmployees] = useState([]);
  const [storeEmployees, setStoreEmployees] = useState({});
  const [delegateTo, setDelegateTo] = useState({});
  const [doneOpenId, setDoneOpenId] = useState(null);
  const [filterStore, setFilterStore] = useState("");
  const [filterOverdueDays, setFilterOverdueDays] = useState("");
  const isLeader = session.role === "leader";
  const isQuality = session.role === "quality";
  const todayISO = new Date().toISOString().slice(0, 10);

  function load() { api("/api/tasks").then(setTasks); }
  useEffect(() => {
    load();
    if (session.role === "shopmanager") api("/api/employees/" + encodeURIComponent(session.store)).then(setEmployees);
  }, []);

  // Leader Retail kann bei eskalierten Tasks an die Mitarbeitenden des
  // jeweiligen Stores delegieren - diese Liste wird pro Store bei Bedarf nachgeladen.
  useEffect(() => {
    if (!isLeader || !tasks) return;
    const stores = [...new Set(tasks.filter((t) => t.escalated && t.status !== "erledigt").map((t) => t.store))];
    stores.forEach((s) => {
      if (!storeEmployees[s]) {
        api("/api/employees/" + encodeURIComponent(s)).then((list) => setStoreEmployees((p) => ({ ...p, [s]: list })));
      }
    });
  }, [tasks, isLeader]);

  async function delegate(id) {
    await api(`/api/tasks/${id}/delegate`, { method: "POST", body: { to: delegateTo[id] } });
    load();
  }
  async function markDone(id, comment, file) {
    const fd = new FormData();
    fd.append("comment", comment);
    if (file) fd.append("photo", file);
    await api(`/api/tasks/${id}/done`, { method: "POST", body: fd });
    setDoneOpenId(null);
    load();
  }
  async function deleteTask(id) {
    if (!window.confirm(t("deleteTaskConfirm"))) return;
    try {
      await api(`/api/tasks/${id}`, { method: "DELETE" });
      load();
    } catch (e) {
      window.alert(e.message);
    }
  }

  if (!tasks) return <div className="panel"><h1>{t("tasksTitle")}</h1><p className="sub">{t("loading")}</p></div>;
  const coordinators = employees.filter((e) => e.role === "Showroom Koordinator");

  const storeOptions = [...new Set(tasks.map((t2) => t2.store))].sort();
  const visibleTasks = tasks.filter((t2) => {
    if (isQuality && filterStore && t2.store !== filterStore) return false;
    if (isQuality && filterOverdueDays !== "") {
      const days = t2.due_at ? Math.floor((new Date(todayISO) - new Date(t2.due_at)) / 86400000) : -Infinity;
      if (!(days >= Number(filterOverdueDays))) return false;
    }
    return true;
  });

  return (
    <div className="panel">
      <h1>{t("tasksTitle")}</h1>
      <p className="sub">
        {session.role === "coordinator"
          ? t("tasksSubCoordinator")
          : isLeader
          ? t("tasksSubLeader")
          : t("tasksSubDefault")}
      </p>
      {isQuality && (
        <div className="task-filters">
          <div className="field inline">
            <label>{t("filterStoreLabel")}</label>
            <select value={filterStore} onChange={(e) => setFilterStore(e.target.value)}>
              <option value="">{t("filterAllStores")}</option>
              {storeOptions.map((s) => <option key={s} value={s}>{s}</option>)}
            </select>
          </div>
          <div className="field inline">
            <label>{t("filterOverdueLabel")}</label>
            <input type="number" min="0" value={filterOverdueDays} onChange={(e) => setFilterOverdueDays(e.target.value)} style={{ width: 70 }} />
          </div>
        </div>
      )}
      {visibleTasks.length === 0 && <div className="empty">{t("tasksEmpty")}</div>}
      <div className="task-list">
        {visibleTasks.map((t2) => {
          const ownStore = t2.store === session.store;
          const overdue = t2.status !== "erledigt" && t2.due_at && t2.due_at < todayISO;
          const canDelegate =
            (session.role === "shopmanager" && ownStore && t2.status === "offen") ||
            (isLeader && t2.escalated && t2.status !== "erledigt");
          const canMarkDone =
            (session.role === "shopmanager" && ownStore) ||
            session.role === "coordinator" ||
            (isLeader && t2.escalated);
          const delegateTargets = isLeader
            ? (storeEmployees[t2.store] || []).filter((e) => e.role === "Showroom Koordinator" || e.role === "Shop Manager")
            : coordinators;
          return (
            <div key={t2.id} className="task">
              <div className="task-row">
                <div className="task-main">
                  <div className="task-title">{trTaskTitle(t2, lang)}</div>
                  <div className="task-meta">
                    {t("taskMeta")(t2.store, t2.from_role, t2.assigned_to)}
                    {t2.due_at && t("taskDue")(t2.due_at)}
                    {overdue && !t2.escalated && <span className="tag-overdue">{t("overdue")}</span>}
                    {t2.escalated && <span className="tag-escalated">{t("escalated")}</span>}
                  </div>
                  {t2.status === "erledigt" && t2.done_comment && (
                    <div className="task-done-note">{t2.done_comment}</div>
                  )}
                </div>
                <div className="task-right">
                  <span className={"pill status-" + t2.status}>{taskStatusLabel(t2.status, S)}</span>
                  {canDelegate && t2.status !== "erledigt" && (
                    <div className="delegate">
                      <select value={delegateTo[t2.id] || ""} onChange={(e) => setDelegateTo((p) => ({ ...p, [t2.id]: e.target.value }))}>
                        <option value="" disabled>{t("delegateTo")}</option>
                        {delegateTargets.map((c) => <option key={c.id} value={c.name}>{c.name}</option>)}
                      </select>
                      <button className="secondary small" disabled={!delegateTo[t2.id]} onClick={() => delegate(t2.id)}><Icon name="send" size={13} /></button>
                    </div>
                  )}
                  {canMarkDone && t2.status !== "erledigt" && (
                    <button className="primary small" onClick={() => setDoneOpenId(doneOpenId === t2.id ? null : t2.id)}>
                      <Icon name="check" size={14} /> {t("doneButton")}
                    </button>
                  )}
                  {isQuality && (
                    <button className="secondary small danger" onClick={() => deleteTask(t2.id)}>
                      <Icon name="ban" size={13} /> {t("deleteButton")}
                    </button>
                  )}
                </div>
              </div>
              {doneOpenId === t2.id && (
                <TaskDoneForm
                  onSubmit={(comment, file) => markDone(t2.id, comment, file)}
                  onCancel={() => setDoneOpenId(null)}
                />
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ---------------- Passwort zurücksetzen (wiederverwendbar) ---------------- */
function ResetPasswordButton({ username }) {
  const { t } = useLang();
  const [result, setResult] = useState(null); // { password } | { error }
  const [busy, setBusy] = useState(false);

  async function reset() {
    if (!window.confirm(t("resetConfirm")(username))) return;
    setBusy(true);
    try {
      const r = await api(`/api/users/by-username/${encodeURIComponent(username)}/reset-password`, { method: "POST", body: {} });
      setResult({ password: r.newPassword });
    } catch (e) {
      setResult({ error: e.message });
    } finally {
      setBusy(false);
    }
  }

  if (result?.password) {
    return (
      <span className="reset-result">
        {t("newPasswordLabel")} <code>{result.password}</code>
        <button className="secondary small" onClick={() => setResult(null)}>{t("ok")}</button>
      </span>
    );
  }
  if (result?.error) {
    return (
      <span className="reset-result reset-error">
        {result.error}
        <button className="secondary small" onClick={() => setResult(null)}>{t("ok")}</button>
      </span>
    );
  }
  return (
    <button className="secondary small" disabled={busy} onClick={reset}>
      <Icon name="lock" size={13} /> {t("resetButton")}
    </button>
  );
}

/* ---------------- Mitarbeitende ---------------- */
function Mitarbeitende({ session, meta }) {
  const { t } = useLang();
  const isQuality = session.role === "quality";
  const storeList = accessibleStoreList(session, meta.stores);
  const [store, setStore] = useState(storeList[0]);
  const [name, setName] = useState("");
  const [role, setRole] = useState("Showroom Koordinator");
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [email, setEmail] = useState("");
  const [list, setList] = useState([]);
  const [error, setError] = useState("");

  function load(s) { api("/api/employees/" + encodeURIComponent(s)).then(setList); }
  useEffect(() => { load(store); }, [store]);

  const canEditThisStore = isQuality || (session.role === "shopmanager" && storeList.includes(store));

  async function add() {
    setError("");
    if (!name.trim()) return;
    try {
      await api("/api/employees/" + encodeURIComponent(store), {
        method: "POST",
        body: {
          name: name.trim(), role,
          username: username.trim() || undefined,
          password: password || undefined,
          email: email.trim() || undefined,
        },
      });
      setName(""); setUsername(""); setPassword(""); setEmail("");
      load(store);
    } catch (e) { setError(e.message); }
  }

  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h1>{t("mitarbeitendeTitle")}</h1>
          <p className="sub">{t("mitarbeitendeSub")(list.length, store)}</p>
        </div>
        {storeList.length > 1 && (
          <div className="field inline">
            <label>{t("storeLabel")}</label>
            <select value={store} onChange={(e) => setStore(e.target.value)}>
              {storeList.map((s) => <option key={s} value={s}>{s}</option>)}
            </select>
          </div>
        )}
      </div>

      {error && <div className="error-box">{error}</div>}

      {canEditThisStore && (
        <div className="add-employee">
          <input placeholder={t("namePlaceholder")} value={name} onChange={(e) => setName(e.target.value)} />
          <select value={role} onChange={(e) => setRole(e.target.value)}>
            <option>Showroom Koordinator</option>
            <option>Shop Manager</option>
          </select>
          <input placeholder={t("usernameOptional")} value={username} onChange={(e) => setUsername(e.target.value)} />
          <input placeholder={t("passwordOptional")} type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
          <input placeholder={t("emailOptional")} type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
          <button className="primary" onClick={add}><Icon name="userplus" size={15} /> {t("addButton")}</button>
        </div>
      )}

      <div className="employee-list">
        {list.map((e) => (
          <div key={e.id} className="employee-row">
            <span className="employee-name">{e.name}</span>
            <span className="employee-role">{e.role}</span>
            {e.username && <span className="employee-login">{t("loginLabel")(e.username)}</span>}
            {e.username && canEditThisStore && <ResetPasswordButton username={e.username} />}
          </div>
        ))}
        {list.length === 0 && <div className="empty">{t("mitarbeitendeEmpty")}</div>}
      </div>
    </div>
  );
}

/* ---------------- Benutzerverwaltung (nur Quality) ---------------- */
// Klick auf einen Kontonamen in der Benutzerverwaltung öffnet dieses Fenster:
// E-Mail nachträglich erfassen/ändern, sowie bei Shop Manager der primäre
// Store und zusätzlich angehängte Stores (mehrere Shops betreuen).
function EditUserModal({ user, meta, onClose, onSaved }) {
  const { t } = useLang();
  const isStoreRole = user.role === "shopmanager" || user.role === "coordinator";
  const [email, setEmail] = useState(user.email || "");
  const [store, setStore] = useState(user.store || meta.stores[0]);
  const [additionalStores, setAdditionalStores] = useState(user.additionalStores || []);
  const [error, setError] = useState("");
  const [busy, setBusy] = useState(false);

  function toggleAdditional(s) {
    setAdditionalStores((prev) => (prev.includes(s) ? prev.filter((x) => x !== s) : [...prev, s]));
  }

  async function save() {
    setError(""); setBusy(true);
    try {
      const body = { email: email.trim() || undefined };
      if (isStoreRole) body.store = store;
      if (user.role === "shopmanager") body.additionalStores = additionalStores.filter((s) => s !== store);
      await api(`/api/admin/users/${user.id}`, { method: "PUT", body });
      onSaved();
    } catch (e) {
      setError(e.message);
    } finally {
      setBusy(false);
    }
  }

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-panel" onClick={(e) => e.stopPropagation()}>
        <h2>{t("editUserTitle")(user.name)}</h2>
        {error && <div className="error-box">{error}</div>}
        <div className="field">
          <label>{t("emailLabel")}</label>
          <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder={t("emailPlaceholder")} />
        </div>
        {isStoreRole && (
          <div className="field">
            <label>{t("editUserPrimaryStoreLabel")}</label>
            <select value={store} onChange={(e) => setStore(e.target.value)}>
              {meta.stores.map((s) => <option key={s} value={s}>{s}</option>)}
            </select>
          </div>
        )}
        {user.role === "shopmanager" && (
          <div className="field">
            <label>{t("editUserAdditionalStoresLabel")}</label>
            <div className="store-checklist">
              {meta.stores.filter((s) => s !== store).map((s) => (
                <label key={s} className="store-check">
                  <input type="checkbox" checked={additionalStores.includes(s)} onChange={() => toggleAdditional(s)} />
                  {s}
                </label>
              ))}
            </div>
          </div>
        )}
        <div className="task-done-actions">
          <button className="secondary small" disabled={busy} onClick={onClose}>{t("doneCancel")}</button>
          <button className="primary small" disabled={busy} onClick={save}><Icon name="check" size={14} /> {t("editUserSave")}</button>
        </div>
      </div>
    </div>
  );
}

// Zwei Funktionen: (1) neue Konten für eine beliebige Rolle erstellen -
// Quality und Leader Retail haben sonst nirgends einen Erfassen-Weg, Shop
// Manager/Showroom Koordinator sind hier zusätzlich zu "Mitarbeitende"
// verfügbar, falls kein Store-Kontext nötig ist. (2) Deputy-Leader-Retail-
// Zusatzfunktion für bestehende Shop Manager aktivieren/deaktivieren.
const CREATABLE_ROLES = ["quality", "leader", "shopmanager", "coordinator"];
function Benutzerverwaltung({ meta }) {
  const { lang, t } = useLang();
  const S = STRINGS[lang];
  const [shopmanagers, setShopmanagers] = useState(null);
  const [qualityLeader, setQualityLeader] = useState(null);
  const [editing, setEditing] = useState(null);

  const [name, setName] = useState("");
  const [role, setRole] = useState("shopmanager");
  const [store, setStore] = useState(meta.stores[0]);
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [email, setEmail] = useState("");
  const [createError, setCreateError] = useState("");
  const [createOk, setCreateOk] = useState("");
  const [busy, setBusy] = useState(false);

  function loadShopmanagers() { api("/api/admin/shopmanagers").then(setShopmanagers); }
  function loadQualityLeader() { api("/api/admin/quality-leader").then(setQualityLeader); }
  useEffect(() => { loadShopmanagers(); loadQualityLeader(); }, []);

  async function toggleDeputy(id, value) {
    setShopmanagers((prev) => prev.map((s) => (s.id === id ? { ...s, deputyLeader: value } : s)));
    await api(`/api/admin/shopmanagers/${id}/deputy-leader`, { method: "POST", body: { deputyLeader: value } });
  }

  async function deleteUser(id, name) {
    if (!window.confirm(t("deleteUserConfirm")(name))) return;
    try {
      await api(`/api/admin/users/${id}`, { method: "DELETE" });
      loadShopmanagers(); loadQualityLeader();
    } catch (e) {
      window.alert(e.message);
    }
  }

  const needsStore = role === "shopmanager" || role === "coordinator";

  async function createUser() {
    setCreateError(""); setCreateOk("");
    if (!name.trim() || !username.trim() || !password) return;
    setBusy(true);
    try {
      await api("/api/admin/users", {
        method: "POST",
        body: {
          name: name.trim(), role,
          store: needsStore ? store : undefined,
          username: username.trim(), password,
          email: email.trim() || undefined,
        },
      });
      setCreateOk(t("createUserOk")(username.trim()));
      setName(""); setUsername(""); setPassword(""); setEmail("");
      loadShopmanagers(); loadQualityLeader();
    } catch (e) {
      setCreateError(e.message);
    } finally {
      setBusy(false);
    }
  }

  return (
    <div className="panel">
      <h1>{t("benutzerTitle")}</h1>
      <p className="sub">{t("benutzerSub")}</p>

      <div className="konto-section">
        <h2>{t("createUserTitle")}</h2>
        <p className="sub">{t("createUserExplain")}</p>
        {createError && <div className="error-box">{createError}</div>}
        {createOk && <div className="ok-box">{createOk}</div>}
        <div className="add-employee">
          <input placeholder={t("namePlaceholder")} value={name} onChange={(e) => setName(e.target.value)} />
          <select value={role} onChange={(e) => setRole(e.target.value)}>
            <option value="quality">Quality</option>
            <option value="leader">Leader Retail</option>
            <option value="shopmanager">Shop Manager</option>
            <option value="coordinator">{S.roleCoordinator}</option>
          </select>
          {needsStore && (
            <select value={store} onChange={(e) => setStore(e.target.value)}>
              {meta.stores.map((s) => <option key={s} value={s}>{s}</option>)}
            </select>
          )}
          <input placeholder={t("username")} value={username} onChange={(e) => setUsername(e.target.value)} />
          <input placeholder={t("password")} type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
          <input placeholder={t("emailOptional")} type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
          <button className="primary" disabled={busy || !name.trim() || !username.trim() || !password} onClick={createUser}>
            <Icon name="userplus" size={15} /> {t("createUserButton")}
          </button>
        </div>
      </div>

      <div className="konto-section">
        <h2>{t("qualityLeaderListTitle")}</h2>
        {!qualityLeader && <p className="sub">{t("loading")}</p>}
        <div className="admin-list">
          {qualityLeader && qualityLeader.map((u) => (
            <div key={u.id} className="admin-row">
              <div className="admin-row-main">
                <div className="admin-row-name clickable" onClick={() => setEditing(u)}>{u.name}</div>
                <div className="admin-row-meta">{roleLabel(u.role, S)} · {u.username}</div>
              </div>
              <div className="admin-row-actions">
                <ResetPasswordButton username={u.username} />
                <button className="secondary small danger" onClick={() => deleteUser(u.id, u.name)}>
                  <Icon name="ban" size={13} /> {t("deleteButton")}
                </button>
              </div>
            </div>
          ))}
          {qualityLeader && qualityLeader.length === 0 && <div className="empty">{t("noQualityLeader")}</div>}
        </div>
      </div>

      <div className="konto-section">
        <h2>{t("deputyTitle")}</h2>
        <p className="sub">{t("deputyExplain")}</p>
        {!shopmanagers && <p className="sub">{t("loading")}</p>}
        <div className="admin-list">
          {shopmanagers && shopmanagers.map((s) => (
            <div key={s.id} className="admin-row">
              <div className="admin-row-main">
                <div className="admin-row-name clickable" onClick={() => setEditing({ ...s, role: "shopmanager" })}>{s.name}</div>
                <div className="admin-row-meta">{s.store} · {s.username}</div>
              </div>
              <div className="admin-row-actions">
                <ResetPasswordButton username={s.username} />
                <button className="secondary small danger" onClick={() => deleteUser(s.id, s.name)}>
                  <Icon name="ban" size={13} /> {t("deleteButton")}
                </button>
              </div>
              <div className="deputy-control">
                <span className="deputy-label">{s.deputyLeader ? t("deputyActive") : t("deputyInactive")}</span>
                <button
                  className={"secondary small" + (s.deputyLeader ? " active" : "")}
                  onClick={() => toggleDeputy(s.id, !s.deputyLeader)}
                >
                  <Icon name={s.deputyLeader ? "check" : "userplus"} size={13} />
                  {s.deputyLeader ? t("deputyDeactivate") : t("deputyActivate")}
                </button>
              </div>
            </div>
          ))}
          {shopmanagers && shopmanagers.length === 0 && <div className="empty">{t("noShopmanagers")}</div>}
        </div>
      </div>

      {editing && (
        <EditUserModal
          user={editing}
          meta={meta}
          onClose={() => setEditing(null)}
          onSaved={() => { setEditing(null); loadShopmanagers(); loadQualityLeader(); }}
        />
      )}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<Root />);
