diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index c139e4c..d50d22e 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -445,14 +445,8 @@ function QuickLinks({ toast, mobile }) { return (
-
+
SCHNELLZUGRIFF
-
- {editMode && } - -
{/* Link-Grid */} @@ -461,20 +455,13 @@ function QuickLinks({ toast, mobile }) { :
{links.map(l => (
- {editMode && ( -
- - -
- )} + e.preventDefault() : undefined} + style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:4, padding:'10px 14px', background:'rgba(255,255,255,0.04)', border:'1px solid rgba(255,255,255,0.08)', borderRadius:10, - textDecoration:'none', minWidth:60, cursor:editMode ? 'default' : 'pointer' }}> + textDecoration:'none', minWidth:60, cursor:'pointer' }}> {l.icon && l.icon.startsWith('http') ? e.target.style.display='none'}/> : {l.icon}} @@ -754,6 +741,107 @@ function DeleteAccountSection({ toast, user }) { ); } +// โ”€โ”€ QuickLinks Einstellungen โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +function QuickLinksSettings({ toast }) { + const [links, setLinks] = useState([]); + const [showForm, setForm] = useState(false); + const [form, setF] = useState({ title:'', url:'', icon:'๐Ÿ”—' }); + const [editId, setEditId] = useState(null); + const { confirm, ConfirmDialog } = useConfirm(); + + useEffect(() => { api('/dashboard/links').then(setLinks).catch(() => {}); }, []); + + const getFavicon = url => { + try { return `https://www.google.com/s2/favicons?sz=64&domain=${new URL(url).hostname}`; } + catch { return null; } + }; + + const save = async () => { + if (!form.title.trim() || !form.url.trim()) { toast('Titel und URL erforderlich', 'error'); return; } + let url = form.url.trim(); + if (!/^https?:\/\//i.test(url)) url = 'https://' + url; + const icon = form.icon === '๐Ÿ”—' ? (getFavicon(url) || '๐Ÿ”—') : form.icon; + try { + if (editId) { + const u = await api(`/dashboard/links/${editId}`, { method:'PUT', body:{...form, icon, url} }); + setLinks(p => p.map(l => l.id===editId ? u : l)); + } else { + const n = await api('/dashboard/links', { body:{...form, icon, url} }); + setLinks(p => [...p, n]); + } + toast(editId ? 'Aktualisiert' : 'Gespeichert'); + setF({ title:'', url:'', icon:'๐Ÿ”—' }); setForm(false); setEditId(null); + } catch(e) { toast(e.message, 'error'); } + }; + + const del = async id => { + if (!await confirm('Link wirklich lรถschen?')) return; + try { await api(`/dashboard/links/${id}`, { method:'DELETE' }); setLinks(p => p.filter(l => l.id!==id)); toast('Gelรถscht'); } + catch(e) { toast(e.message, 'error'); } + }; + + return ( +
+ + {/* Link-Liste */} + {links.length > 0 && ( +
+ {links.map(l => ( +
+ + {l.icon && l.icon.startsWith('http') + ? e.target.style.display='none'}/> + : l.icon} + +
+
{l.title}
+
{l.url}
+
+
+ + +
+
+ ))} +
+ )} + + {/* Formular */} + {showForm ? ( +
+
+
ICON
+
+ {['๐Ÿ”—','๐ŸŒ','๐Ÿ“Š','๐Ÿ“','๐Ÿ› ','๐Ÿ“ง','๐Ÿ—“','๐Ÿ’พ','๐Ÿ–ฅ','๐Ÿ“ก','๐Ÿ”’','๐Ÿ“–','๐ŸŽฏ','โšก','๐Ÿ ','๐Ÿ”ง','๐Ÿ“ฑ','๐ŸŽฎ','๐Ÿš€','๐Ÿ’ก'].map(ic => ( + + ))} +
+
+ setF(f => ({...f, title:e.target.value}))} placeholder="Mein Link"/> + setF(f => ({...f, url:e.target.value}))} placeholder="https://โ€ฆ" autoCapitalize="none"/> +
+ + +
+
+ ) : ( + + )} +
+ ); +} + // โ”€โ”€ Benutzerverwaltung โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ function UserManagement({ toast }) { const [users, setUsers] = useState([]); @@ -1025,6 +1113,9 @@ function AdminPanel({ toast, mobile, user }) { {section === 'dashboard' && (
+ + + diff --git a/frontend/src/calendar.jsx b/frontend/src/calendar.jsx index fc296f4..3cae87a 100644 --- a/frontend/src/calendar.jsx +++ b/frontend/src/calendar.jsx @@ -9,8 +9,10 @@ function parseIcal(raw) { // 2. Alle Properties als Map extrahieren function getProps(block) { + // VALARM-Blรถcke entfernen (haben eigene SUMMARY/DESCRIPTION die nicht zum Termin gehรถren) + const stripped = block.replace(/BEGIN:VALARM[\s\S]*?END:VALARM/g, ''); const props = {}; - for (const line of block.split('\n')) { + for (const line of stripped.split('\n')) { const col = line.indexOf(':'); if (col < 0) continue; const namepart = line.slice(0, col).toUpperCase(); @@ -206,9 +208,17 @@ export default function CalendarWidget() {
- - {MONTHS[cur.m]} {cur.y} - +
+ + {MONTHS[cur.m]} {cur.y} + + {(cur.m !== today.getMonth() || cur.y !== today.getFullYear()) && ( + + )} +