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
Some checks failed
Build & Push Docker Image / build (push) Has been cancelled
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user