fix: remove role gate from Settings, all strings in English
This commit is contained in:
12
src/App.tsx
12
src/App.tsx
@ -92,10 +92,10 @@ export default function App() {
|
||||
saveSession(urlToken, user);
|
||||
setCurrentUser(user);
|
||||
} else {
|
||||
setOauthError('Anmeldung fehlgeschlagen. Bitte erneut versuchen.');
|
||||
setOauthError('Login failed. Please try again.');
|
||||
}
|
||||
} catch {
|
||||
setOauthError('Server nicht erreichbar.');
|
||||
setOauthError('Server unreachable. Please try again.');
|
||||
} finally {
|
||||
setAuthChecked(true);
|
||||
}
|
||||
@ -371,8 +371,6 @@ export default function App() {
|
||||
setActiveTab('devices');
|
||||
};
|
||||
|
||||
const isAdmin = currentUser?.role?.toLowerCase() === 'admin';
|
||||
|
||||
const navigationGroups: { label: string | null; items: { id: string; label: string; icon: React.ReactNode }[] }[] = [
|
||||
{
|
||||
label: null,
|
||||
@ -401,12 +399,12 @@ export default function App() {
|
||||
{ id: 'logs', label: 'Logbook', icon: <History className="w-4 h-4 shrink-0" /> },
|
||||
],
|
||||
},
|
||||
...(isAdmin ? [{
|
||||
{
|
||||
label: 'System',
|
||||
items: [
|
||||
{ id: 'settings', label: 'Einstellungen', icon: <Settings2 className="w-4 h-4 shrink-0" /> },
|
||||
{ id: 'settings', label: 'Settings', icon: <Settings2 className="w-4 h-4 shrink-0" /> },
|
||||
],
|
||||
}] : []),
|
||||
},
|
||||
];
|
||||
|
||||
// Startup check not done yet
|
||||
|
||||
@ -150,7 +150,7 @@ export default function LoginPage({ onLogin, onNavigateToRegister, authError }:
|
||||
<>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex-1 h-px bg-slate-800" />
|
||||
<span className="text-[10px] font-mono text-slate-500 uppercase tracking-widest">oder</span>
|
||||
<span className="text-[10px] font-mono text-slate-500 uppercase tracking-widest">or</span>
|
||||
<div className="flex-1 h-px bg-slate-800" />
|
||||
</div>
|
||||
<button
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import { authFetch } from '../lib/auth';
|
||||
import { User } from '../types';
|
||||
import {
|
||||
Shield, Activity, Save, CheckCircle, AlertCircle, Eye, EyeOff, Settings2, Lock,
|
||||
Shield, Activity, Save, CheckCircle, AlertCircle, Eye, EyeOff, Settings2,
|
||||
} from 'lucide-react';
|
||||
|
||||
const SECRET_SENTINEL = '__SET__';
|
||||
@ -50,14 +50,13 @@ function TextInput({
|
||||
}
|
||||
|
||||
function SecretInput({
|
||||
value, onChange, alreadySet, show, onToggleShow, placeholder,
|
||||
value, onChange, alreadySet, show, onToggleShow,
|
||||
}: {
|
||||
value: string;
|
||||
onChange: (v: string) => void;
|
||||
alreadySet: boolean;
|
||||
show: boolean;
|
||||
onToggleShow: () => void;
|
||||
placeholder?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="relative">
|
||||
@ -65,7 +64,7 @@ function SecretInput({
|
||||
type={show ? 'text' : 'password'}
|
||||
value={value}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
placeholder={alreadySet ? 'Bereits konfiguriert – leer lassen zum Behalten' : (placeholder ?? 'Neuen Wert eingeben')}
|
||||
placeholder={alreadySet ? 'Already configured – leave blank to keep' : 'Enter value'}
|
||||
className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2.5 pr-10 text-sm text-white placeholder-slate-500 font-mono focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500/50 transition-all"
|
||||
/>
|
||||
<button
|
||||
@ -80,7 +79,7 @@ function SecretInput({
|
||||
);
|
||||
}
|
||||
|
||||
export default function Settings({ currentUser }: SettingsProps) {
|
||||
export default function Settings({ currentUser: _currentUser }: SettingsProps) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
@ -109,7 +108,7 @@ export default function Settings({ currentUser }: SettingsProps) {
|
||||
setError('');
|
||||
try {
|
||||
const res = await authFetch('/api/settings');
|
||||
if (!res.ok) { setError('Einstellungen konnten nicht geladen werden.'); return; }
|
||||
if (!res.ok) { setError('Failed to load settings.'); return; }
|
||||
const data: RawSettings = await res.json();
|
||||
setAzureEnabled(data.azure_enabled === 'true');
|
||||
setAzureClientId(data.azure_client_id || '');
|
||||
@ -122,7 +121,7 @@ export default function Settings({ currentUser }: SettingsProps) {
|
||||
setCheckmkApiSecret('');
|
||||
setCheckmkSyncInterval(data.checkmk_sync_interval_ms || '60000');
|
||||
} catch {
|
||||
setError('Netzwerkfehler beim Laden der Einstellungen.');
|
||||
setError('Network error loading settings.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@ -146,7 +145,7 @@ export default function Settings({ currentUser }: SettingsProps) {
|
||||
const res = await authFetch('/api/settings', { method: 'PUT', body: JSON.stringify(payload) });
|
||||
if (!res.ok) {
|
||||
const d = await res.json();
|
||||
setError(d.error || 'Speichern fehlgeschlagen.');
|
||||
setError(d.error || 'Failed to save settings.');
|
||||
return;
|
||||
}
|
||||
const data: RawSettings = await res.json();
|
||||
@ -154,24 +153,15 @@ export default function Settings({ currentUser }: SettingsProps) {
|
||||
setCheckmkSecretSet(data.checkmk_api_secret === SECRET_SENTINEL);
|
||||
setAzureClientSecret('');
|
||||
setCheckmkApiSecret('');
|
||||
setSuccessMsg('Einstellungen gespeichert.');
|
||||
setSuccessMsg('Settings saved.');
|
||||
setTimeout(() => setSuccessMsg(''), 4000);
|
||||
} catch {
|
||||
setError('Netzwerkfehler beim Speichern.');
|
||||
setError('Network error saving settings.');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentUser.role.toLowerCase() !== 'admin') {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-64 space-y-3">
|
||||
<Lock className="w-10 h-10 text-slate-600" />
|
||||
<p className="text-slate-400 text-sm">Einstellungen sind nur für Administratoren zugänglich.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
@ -189,8 +179,8 @@ export default function Settings({ currentUser }: SettingsProps) {
|
||||
<Settings2 className="w-5 h-5 text-cyan-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-lg font-bold text-white">Einstellungen</h1>
|
||||
<p className="text-xs text-slate-400">Integrationen und Dienst-Konfigurationen</p>
|
||||
<h1 className="text-lg font-bold text-white">Settings</h1>
|
||||
<p className="text-xs text-slate-400">Integrations and service configuration</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -217,15 +207,14 @@ export default function Settings({ currentUser }: SettingsProps) {
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-white">Microsoft Entra ID</h3>
|
||||
<p className="text-xs text-slate-500">OAuth 2.0 Login für Organisationsaccounts</p>
|
||||
<p className="text-xs text-slate-500">OAuth 2.0 login for organizational accounts</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Toggle */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setAzureEnabled(v => !v)}
|
||||
className={`relative w-11 h-6 rounded-full transition-colors ${azureEnabled ? 'bg-blue-600' : 'bg-slate-700'}`}
|
||||
title={azureEnabled ? 'Deaktivieren' : 'Aktivieren'}
|
||||
title={azureEnabled ? 'Disable' : 'Enable'}
|
||||
>
|
||||
<span className={`absolute top-0.5 left-0.5 w-5 h-5 bg-white rounded-full shadow transition-transform ${azureEnabled ? 'translate-x-5' : 'translate-x-0'}`} />
|
||||
</button>
|
||||
@ -251,7 +240,7 @@ export default function Settings({ currentUser }: SettingsProps) {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FieldLabel>Redirect URI <span className="text-slate-500 font-normal">(leer = automatisch)</span></FieldLabel>
|
||||
<FieldLabel>Redirect URI <span className="text-slate-500 font-normal">(leave blank for auto)</span></FieldLabel>
|
||||
<TextInput value={azureRedirectUri} onChange={setAzureRedirectUri} placeholder="https://ghostgrid.internal/api/auth/azure/callback" monospace />
|
||||
</div>
|
||||
</div>
|
||||
@ -265,7 +254,7 @@ export default function Settings({ currentUser }: SettingsProps) {
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-white">CheckMK</h3>
|
||||
<p className="text-xs text-slate-500">Geräte-Status-Synchronisation über die CheckMK REST API</p>
|
||||
<p className="text-xs text-slate-500">Device status synchronization via the CheckMK REST API</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -290,7 +279,7 @@ export default function Settings({ currentUser }: SettingsProps) {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FieldLabel>Sync-Intervall (ms)</FieldLabel>
|
||||
<FieldLabel>Sync Interval (ms)</FieldLabel>
|
||||
<TextInput value={checkmkSyncInterval} onChange={setCheckmkSyncInterval} placeholder="60000" monospace />
|
||||
</div>
|
||||
</div>
|
||||
@ -308,7 +297,7 @@ export default function Settings({ currentUser }: SettingsProps) {
|
||||
) : (
|
||||
<Save className="w-4 h-4" />
|
||||
)}
|
||||
{saving ? 'Speichert…' : 'Einstellungen speichern'}
|
||||
{saving ? 'Saving…' : 'Save Settings'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user