Dateien nach "frontend/src" hochladen

This commit is contained in:
2026-05-26 15:54:15 +02:00
parent 9b26faf71a
commit ec290bb5a4

View File

@@ -88,9 +88,7 @@ export default function CalendarWidget() {
const [today] = useState(new Date()); const [today] = useState(new Date());
const [cur, setCur] = useState({ y: today.getFullYear(), m: today.getMonth() }); const [cur, setCur] = useState({ y: today.getFullYear(), m: today.getMonth() });
const [events, setEvents] = useState([]); const [events, setEvents] = useState([]);
// Feeds immer frisch aus localStorage lesen const [feeds, setFeeds] = useState([]);
const getFeeds = () => { try { return JSON.parse(localStorage.getItem('dd_cal_feeds') || '[]'); } catch { return []; } };
const [feedCount, setFeedCount] = useState(0); // trigger re-render
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [selected, setSelected] = useState(new Date()); // default: heute const [selected, setSelected] = useState(new Date()); // default: heute
@@ -123,7 +121,7 @@ export default function CalendarWidget() {
setLoading(false); setLoading(false);
}; };
const refresh = () => { const f = getFeeds(); if (f.length > 0) loadFeeds(f); setFeedCount(f.length); }; const refresh = () => { api('/calendar/feeds').then(f=>{ setFeeds(f); loadFeeds(f); }).catch(()=>{}); };
useEffect(() => { if (isOpen) refresh(); }, [isOpen]); useEffect(() => { if (isOpen) refresh(); }, [isOpen]);
// Nächster oder aktueller Termin (für zugeklappte Ansicht) // Nächster oder aktueller Termin (für zugeklappte Ansicht)
@@ -162,7 +160,7 @@ export default function CalendarWidget() {
<span style={{ color:'rgba(255,255,255,0.4)', fontSize:11, display:'inline-block', <span style={{ color:'rgba(255,255,255,0.4)', fontSize:11, display:'inline-block',
transform:isOpen?'rotate(90deg)':'rotate(0)', transition:'transform 0.2s' }}></span> transform:isOpen?'rotate(90deg)':'rotate(0)', transition:'transform 0.2s' }}></span>
</button> </button>
{isOpen && getFeeds().length > 0 && ( {isOpen && feeds.length > 0 && (
<button onClick={refresh} disabled={loading} <button onClick={refresh} disabled={loading}
style={{ background:'transparent', border:'none', color:'rgba(255,255,255,0.4)', style={{ background:'transparent', border:'none', color:'rgba(255,255,255,0.4)',
cursor:'pointer', fontSize:13, padding:'0 0 0 8px', marginLeft:8 }} cursor:'pointer', fontSize:13, padding:'0 0 0 8px', marginLeft:8 }}
@@ -302,7 +300,7 @@ export default function CalendarWidget() {
</div>{/* Ende Termine */} </div>{/* Ende Termine */}
</div>{/* Ende Flex-Layout */} </div>{/* Ende Flex-Layout */}
{getFeeds().length === 0 && ( {feeds.length === 0 && (
<div style={{ marginTop:10, color:'rgba(255,255,255,0.3)', fontFamily:'monospace', fontSize:11, textAlign:'center' }}> <div style={{ marginTop:10, color:'rgba(255,255,255,0.3)', fontFamily:'monospace', fontSize:11, textAlign:'center' }}>
Noch keine Kalender abonniert. Füge Kalender in den Einstellungen hinzu. Noch keine Kalender abonniert. Füge Kalender in den Einstellungen hinzu.
</div> </div>
@@ -317,16 +315,11 @@ export default function CalendarWidget() {
// iCal-Einstellungen (für Einstellungen-Seite) // iCal-Einstellungen (für Einstellungen-Seite)
export function CalendarSettings({ toast }) { export function CalendarSettings({ toast }) {
const [feeds, setFeeds] = useState(() => { const [feeds, setFeeds] = useState([]);
try { return JSON.parse(localStorage.getItem('dd_cal_feeds') || '[]'); } catch { return []; } const [form, setForm] = useState({ name:'', url:'', color:'#4ecdc4' });
});
const [form, setForm] = useState({ name:'', url:'', color:'#4ecdc4' });
const [testing, setTesting] = useState(false); const [testing, setTesting] = useState(false);
const save = (newFeeds) => { useEffect(() => { api('/calendar/feeds').then(setFeeds).catch(()=>{}); }, []);
setFeeds(newFeeds);
localStorage.setItem('dd_cal_feeds', JSON.stringify(newFeeds));
};
const add = async () => { const add = async () => {
if (!form.name.trim() || !form.url.trim()) { toast('Name und URL erforderlich', 'error'); return; } if (!form.name.trim() || !form.url.trim()) { toast('Name und URL erforderlich', 'error'); return; }
@@ -336,13 +329,17 @@ export function CalendarSettings({ toast }) {
headers: { Authorization: `Bearer ${localStorage.getItem('sk_token')}` } headers: { Authorization: `Bearer ${localStorage.getItem('sk_token')}` }
}); });
if (!res.ok) { const d=await res.json(); toast(d.error||'Feed-Fehler','error'); return; } if (!res.ok) { const d=await res.json(); toast(d.error||'Feed-Fehler','error'); return; }
save([...feeds, { ...form, id: Date.now() }]); const newFeed = await api('/calendar/feeds', { body: { name:form.name.trim(), url:form.url.trim(), color:form.color } });
setFeeds(p => [...p, newFeed]);
setForm({ name:'', url:'', color:'#4ecdc4' }); setForm({ name:'', url:'', color:'#4ecdc4' });
toast('Kalender hinzugefügt ✓'); toast('Kalender hinzugefügt ✓');
} catch(e) { toast(e.message,'error'); } finally { setTesting(false); } } catch(e) { toast(e.message,'error'); } finally { setTesting(false); }
}; };
const remove = (id) => save(feeds.filter(f => f.id !== id)); const remove = async (id) => {
try { await api(`/calendar/feeds/${id}`, {method:'DELETE'}); setFeeds(p=>p.filter(f=>f.id!==id)); toast('Entfernt'); }
catch(e) { toast(e.message,'error'); }
};
const COLORS = ['#4ecdc4','#ff6b9d','#ffe66d','#6bcb77','#a78bfa','#60a5fa','#fb923c']; const COLORS = ['#4ecdc4','#ff6b9d','#ffe66d','#6bcb77','#a78bfa','#60a5fa','#fb923c'];