feat: implement hide app name feature with toggle in admin settings and update branding context
Some checks failed
Build & Push Docker Image / build (push) Has been cancelled

This commit is contained in:
2026-03-04 10:11:35 +01:00
parent bac4e8ae7c
commit ce2cf499dc
11 changed files with 627 additions and 61 deletions

View File

@@ -16,7 +16,7 @@ import toast from 'react-hot-toast';
export default function Admin() {
const { user } = useAuth();
const { t, language } = useLanguage();
const { appName, hasLogo, logoUrl, defaultTheme, registrationMode, imprintUrl, privacyUrl, refreshBranding } = useBranding();
const { appName, hasLogo, logoUrl, defaultTheme, registrationMode, imprintUrl, privacyUrl, hideAppName, refreshBranding } = useBranding();
const navigate = useNavigate();
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
@@ -47,6 +47,7 @@ export default function Admin() {
const [savingImprintUrl, setSavingImprintUrl] = useState(false);
const [editPrivacyUrl, setEditPrivacyUrl] = useState('');
const [savingPrivacyUrl, setSavingPrivacyUrl] = useState(false);
const [savingHideAppName, setSavingHideAppName] = useState(false);
// OAuth state
const [oauthConfig, setOauthConfig] = useState(null);
@@ -168,6 +169,18 @@ export default function Admin() {
}
};
const handleHideAppNameToggle = async (value) => {
setSavingHideAppName(true);
try {
await api.put('/branding/hide-app-name', { hideAppName: value });
refreshBranding();
} catch {
toast.error(t('admin.hideAppNameFailed'));
} finally {
setSavingHideAppName(false);
}
};
const handleAppNameSave = async () => {
if (!editAppName.trim()) return;
setSavingName(true);
@@ -447,6 +460,28 @@ export default function Admin() {
{savingName ? <Loader2 size={14} className="animate-spin" /> : t('common.save')}
</button>
</div>
{hasLogo && (
<div className="flex items-center justify-between mt-3 p-3 rounded-lg bg-th-bg-s border border-th-border">
<div className="min-w-0">
<p className="text-sm font-medium text-th-text">{t('admin.hideAppNameLabel')}</p>
<p className="text-xs text-th-text-s mt-0.5">{t('admin.hideAppNameHint')}</p>
</div>
<button
type="button"
disabled={savingHideAppName}
onClick={() => handleHideAppNameToggle(!hideAppName)}
className={`relative inline-flex h-5 w-9 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-th-ring focus:ring-offset-1 disabled:opacity-50 ml-4 ${
hideAppName ? 'bg-th-accent' : 'bg-th-border'
}`}
aria-checked={hideAppName}
role="switch"
>
<span className={`pointer-events-none inline-block h-4 w-4 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
hideAppName ? 'translate-x-4' : 'translate-x-0'
}`} />
</button>
</div>
)}
</div>
</div>

View File

@@ -1,12 +1,13 @@
import { useState, useEffect, useMemo } from 'react';
import {
ChevronLeft, ChevronRight, Plus, Calendar as CalendarIcon, Clock, Video,
ChevronLeft, ChevronRight, Plus, Clock, Video,
Loader2, Download, Share2, Globe, Trash2, Edit, X, UserPlus, Send, ExternalLink,
} from 'lucide-react';
import api from '../services/api';
import { useAuth } from '../contexts/AuthContext';
import { useLanguage } from '../contexts/LanguageContext';
import Modal from '../components/Modal';
import DateTimePicker from '../components/DateTimePicker';
import toast from 'react-hot-toast';
const COLORS = ['#6366f1', '#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#8b5cf6', '#ec4899', '#14b8a6'];
@@ -533,30 +534,21 @@ export default function Calendar() {
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-th-text mb-1.5">{t('calendar.startTime')} *</label>
<div className="datetime-picker">
<CalendarIcon size={16} className="datetime-icon" />
<input
type="datetime-local"
value={form.start_time}
onChange={e => setForm({ ...form, start_time: e.target.value })}
required
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-th-text mb-1.5">{t('calendar.endTime')} *</label>
<div className="datetime-picker">
<Clock size={16} className="datetime-icon" />
<input
type="datetime-local"
value={form.end_time}
onChange={e => setForm({ ...form, end_time: e.target.value })}
required
/>
</div>
</div>
<DateTimePicker
label={t('calendar.startTime')}
value={form.start_time}
onChange={v => setForm({ ...form, start_time: v })}
required
icon="calendar"
/>
<DateTimePicker
label={t('calendar.endTime')}
value={form.end_time}
onChange={v => setForm({ ...form, end_time: v })}
required
icon="clock"
minDate={form.start_time ? new Date(form.start_time) : null}
/>
</div>
<div className="flex items-center gap-1.5 -mt-2 text-xs text-th-text-s">
<Globe size={12} className="flex-shrink-0" />