diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx
index f47fb62..d04fd57 100644
--- a/frontend/src/pages/Settings.jsx
+++ b/frontend/src/pages/Settings.jsx
@@ -96,6 +96,26 @@ const TAB_SCOPES = {
team: null,
};
+/** „vor 3 Minuten" statt rohem Zeitstempel */
+function vorWieLange(iso) {
+ // Die Datenbank speichert UTC ohne Kennzeichnung — sonst rechnet der
+ // Browser sie als Ortszeit und alles sieht Stunden alt aus.
+ const min = Math.floor((Date.now() - new Date(iso.replace(' ', 'T') + 'Z').getTime()) / 60000);
+ if (min < 1) return 'gerade eben';
+ if (min < 60) return `vor ${min} Min`;
+ const std = Math.floor(min / 60);
+ if (std < 24) return `vor ${std} Std`;
+ const tage = Math.floor(std / 24);
+ return tage === 1 ? 'gestern' : `vor ${tage} Tagen`;
+}
+
+/** Avatar mit Anfangsbuchstaben als Rückfallebene.
+ * Bewusst außerhalb der Komponente: als Inline-Komponente wäre der Typ bei
+ * jedem Rendern neu und React würde die Bilder jedes Mal neu einhängen. */
+const Avatar = ({ src, name, klein }) => (src
+ ?
+ : {(name ?? '?').slice(0, 1).toUpperCase()});
+
const TEAM_SCOPE_LABELS = {
content: 'Composer, Tags, Triggers & geplante Posts',
community: 'Playtester & Alpha-Keys',
@@ -302,6 +322,8 @@ export default function Settings({ me }) {
const [botDesc, setBotDesc] = useState('');
const [team, setTeam] = useState([]);
const [audit, setAudit] = useState([]);
+ const [owner, setOwner] = useState(null);
+ const [auditAlle, setAuditAlle] = useState(false);
// Module (An/Aus für alle Funktionen)
const [modules, setModules] = useState([]);
const [moduleGroups, setModuleGroups] = useState([]);
@@ -420,7 +442,9 @@ export default function Settings({ me }) {
apiGet('/api/appforms').then((d) => setAppForms(d.forms)).catch(() => {});
apiGet('/api/triggers').then((d) => setTriggers(d.triggers)).catch(() => {});
apiGet('/api/gameservers').then((d) => setServers(d.servers)).catch(() => {});
- apiGet('/api/webadmins').then((d) => setTeam(d.admins)).catch(() => {});
+ apiGet('/api/webadmins')
+ .then((d) => { setTeam(d.admins); setOwner(d.owner); })
+ .catch(() => {});
apiGet('/api/auditlog').then((d) => setAudit(d.entries)).catch(() => {});
apiGet('/api/ssoapps').then((d) => setSsoApps(d.apps)).catch(() => {});
apiGet('/api/services').then((d) => setServices(d.services)).catch(() => {});
@@ -2791,77 +2815,137 @@ export default function Settings({ me }) {
);
+ const auditSichtbar = auditAlle ? audit : audit.slice(0, 12);
+ const teamBearbeitet = team.some((t) => t.user_id === teamDraft.user_id);
+
const tabTeam = (
+ <>
// Team
- Gib Team-Mitgliedern gezielten Zugriff aufs Webinterface — sie loggen sich ganz
- normal mit Discord ein und sehen nur ihre Bereiche. Brand, System, API-Keys
- und diese Team-Verwaltung bleiben immer dir vorbehalten.
+ Team-Mitglieder loggen sich ganz normal mit Discord ein und sehen nur ihre
+ Bereiche. Brand, System, API-Keys und diese Verwaltung bleiben immer dir
+ vorbehalten.
- {team.length > 0 && (
-
- {team.map((t) => (
-
- {t.username ?? t.user_id}
- {t.scopes}
-
-
+
+ {owner && (
+
+
+
+ {owner.name ?? me.user?.username ?? 'Du'}
+ Alle Bereiche
+