From edaf8611b2c37c9e6b25e69474546b11310b58af Mon Sep 17 00:00:00 2001 From: Dicken Date: Mon, 25 May 2026 23:03:27 +0200 Subject: [PATCH] =?UTF-8?q?frontend/src/App.jsx=20gel=C3=B6scht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.jsx | 733 ------------------------------------------- 1 file changed, 733 deletions(-) delete mode 100644 frontend/src/App.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx deleted file mode 100644 index dac49df..0000000 --- a/frontend/src/App.jsx +++ /dev/null @@ -1,733 +0,0 @@ -import { useState, useEffect, useRef, useCallback } from 'react'; -import { api, S } from './lib.js'; -import { TOOLS, getGroupedTools } from './toolRegistry.js'; - -const ICONS = ['๐Ÿ”—','๐ŸŒ','๐Ÿ“Š','๐Ÿ“','๐Ÿ› ','๐Ÿ“ง','๐Ÿ—“','๐Ÿ’พ','๐Ÿ–ฅ','๐Ÿ“ก','๐Ÿ”’','๐Ÿ“–','๐ŸŽฏ','โšก','๐Ÿ ','๐Ÿ”ง','๐Ÿ“ฑ','๐ŸŽฎ','๐Ÿš€','๐Ÿ’ก']; - -// โ”€โ”€ Responsive Hook โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -function useIsMobile() { - const [mobile, setMobile] = useState(window.innerWidth < 768); - useEffect(() => { - // Nur auf Breite reagieren, nicht auf Keyboard-Open (Hรถhenรคnderung) - const fn = () => { - const isMob = window.innerWidth < 768; - setMobile(prev => prev === isMob ? prev : isMob); - }; - window.addEventListener('resize', fn); - return () => window.removeEventListener('resize', fn); - }, []); - return mobile; -} - -// โ”€โ”€ Toast โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -function Toast({ msg, type }) { - if (!msg) return null; - const c = type === 'error' ? '#ff6b9d' : '#4ecdc4'; - return ( -
- {type==='error'?'โœ• ':'โœ“ '}{msg} -
- ); -} - -// โ”€โ”€ Modal โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -function Modal({ title, onClose, children }) { - return ( -
e.target===e.currentTarget&&onClose()}> -
-
-
- {title} - -
- {children} -
-
- ); -} - -// โ”€โ”€ Login โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -function Login({ onLogin }) { - const [u,setU]=useState(''); const [p,setP]=useState(''); - const [err,setErr]=useState(''); const [busy,setBusy]=useState(false); - const go = async () => { - setErr(''); setBusy(true); - try { const d=await api('/auth/login',{body:{username:u,password:p}}); localStorage.setItem('sk_token',d.token); onLogin(d.user); } - catch(e) { setErr(e.message); } finally { setBusy(false); } - }; - return ( -
-
-
-
โš’
-
- DICKENDOCK -
-
- {[['BENUTZERNAME',u,setU,'text'],['PASSWORT',p,setP,'password']].map(([l,v,s,t])=>( -
- - s(e.target.value)} - onKeyDown={e=>e.key==='Enter'&&go()} - style={{ ...S.inp, fontSize:16, padding:'13px 14px', borderRadius:10, - border:'1px solid rgba(255,255,255,0.12)' }} - autoCapitalize="none" autoCorrect="off" /> -
- ))} - {err &&
โœ• {err}
} - -
-
- ); -} - -// โ”€โ”€ Update Modal โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -function UpdateModal({ data, onClose }) { - return ( - -
- - Installiert: {data.currentVersion} - {' โ†’ '}{data.latestVersion} - -
- {(data.releases||[]).map((r,i)=>( -
-
- {r.name||r.version} - - {r.publishedAt?new Date(r.publishedAt).toLocaleDateString('de-DE'):''} - -
-
{r.body}
-
- ))} -
- Update: docker compose up -d --build -
-
- ); -} - -// โ”€โ”€ Desktop Sidebar โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -function Sidebar({ active, setActive, user, onLogout, updateInfo, onUpdateClick }) { - const [appsOpen, setAppsOpen] = useState(true); - - const NavBtn = ({ item, sub=false }) => ( - - ); - - return ( - - ); -} - -// โ”€โ”€ Mobile Bottom Navigation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -function BottomNav({ active, setActive, user, onLogout, updateInfo, onUpdateClick }) { - const [menuOpen, setMenuOpen] = useState(false); - const [appsOpen, setAppsOpen] = useState(false); - const NAV_H = 56; - - const MI = ({icon, label, onClick, color='#fff'}) => ( - - ); - - const Sheet = ({open, onClose, children}) => !open ? null : ( -
-
e.stopPropagation()}> -
- {children} -
-
-
- ); - - const mainNav = [ - { id:'dashboard', icon:'โฌก', label:'Home' }, - { id:'_apps', icon:'โ—ซ', label:'Apps' }, - ...(user?.role==='admin' ? [{ id:'admin', icon:'โš™', label:'Admin' }] : []), - { id:'_mehr', icon:'โ‹ฏ', label:'Mehr' }, - ]; - - return ( - <> - {/* Apps Sheet mit Gruppenรผberschriften */} - setAppsOpen(false)}> -
- {getGroupedTools().map(([group, tools]) => ( -
-
{group.toUpperCase()}
- {tools.map(t => ( - { setActive(t.id); setAppsOpen(false); }} /> - ))} -
- ))} -
-
- - {/* Mehr Sheet */} - setMenuOpen(false)}> - {updateInfo?.hasUpdate && {setMenuOpen(false);onUpdateClick();}} color="#ffe66d"/>} -
- {}} color="rgba(255,255,255,0.35)"/> - {onLogout();setMenuOpen(false);}} color="#ff6b9d"/> - - - {/* Bottom Bar */} - - - ); -} - - -// โ”€โ”€ Dashboard Widgets โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -function QuickLinks({ toast, mobile }) { - const [links, setLinks] = useState([]); - const [editMode, setEdit] = useState(false); - const [showForm, setForm] = useState(false); - const [form, setF] = useState({ title:'', url:'', icon:'๐Ÿ”—' }); - const [editId, setEditId] = useState(null); - - useEffect(() => { api('/dashboard/links').then(setLinks).catch(() => {}); }, []); - - 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; - try { - if (editId) { - const u = await api(`/dashboard/links/${editId}`, { method:'PUT', body:{...form, url} }); - setLinks(p => p.map(l => l.id===editId ? u : l)); - } else { - const n = await api('/dashboard/links', { body:{...form, 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 => { - 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 ( -
-
-
SCHNELLZUGRIFF
-
- {editMode && } - -
-
- - {/* Link-Grid */} - {links.length === 0 && !editMode - ?
Tippe auf "โœŽ" um Links hinzuzufรผgen.
- :
- {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' }}> - {l.icon} - {l.title} - -
- ))} -
- } - - {/* Formular */} - {showForm && ( -
- {/* Icon-Picker */} -
-
ICON
-
- {ICONS.map(ic => ( - - ))} -
-
-
-
- - setF(f => ({...f, title:e.target.value}))} - style={{ ...S.inp, fontSize:16 }} placeholder="Mein Link" /> -
-
- - setF(f => ({...f, url:e.target.value}))} - onKeyDown={e => e.key==='Enter' && save()} - style={{ ...S.inp, fontSize:16 }} placeholder="https://โ€ฆ" inputMode="url" autoCapitalize="none" /> -
-
-
- - -
-
- )} -
- ); -} - -function TodoList({ toast }) { - const [items, setItems] = useState([]); - const [text, setText] = useState(''); - useEffect(() => { api('/dashboard/todos').then(setItems).catch(() => {}); }, []); - - const add = async () => { - if (!text.trim()) return; - try { const n = await api('/dashboard/todos', { body:{ text:text.trim() } }); setItems(p => [...p, n]); setText(''); } - catch(e) { toast(e.message, 'error'); } - }; - const toggle = async item => { - try { const u = await api(`/dashboard/todos/${item.id}`, { method:'PUT', body:{ done:!item.done } }); setItems(p => p.map(i => i.id===item.id ? u : i)); } - catch(e) { toast(e.message, 'error'); } - }; - const del = async id => { - try { await api(`/dashboard/todos/${id}`, { method:'DELETE' }); setItems(p => p.filter(i => i.id!==id)); } - catch(e) { toast(e.message, 'error'); } - }; - - const open = items.filter(i => !i.done); - const done = items.filter(i => i.done); - - return ( -
-
TO-DO
-
- setText(e.target.value)} - onKeyDown={e => e.key==='Enter' && add()} - style={{ ...S.inp, flex:1, fontSize:16 }} placeholder="Neue Aufgabeโ€ฆ" /> - -
-
- {open.length === 0 && done.length === 0 && ( -
Noch keine Aufgaben.
- )} - {open.map(item => ( -
- -
- ))} - {done.length > 0 && ( - <> -
ERLEDIGT
- {done.map(item => ( -
- - {item.text} - -
- ))} - - )} -
-
- ); -} - -function Notepad({ toast }) { - const [content, setContent] = useState(''); - const [saved, setSaved] = useState(true); - const [saveTime, setSaveTime] = useState(null); - const tmr = useRef(null); - - useEffect(() => { - api('/dashboard/note').then(d => { setContent(d.content || ''); setSaveTime(d.updated_at); }).catch(() => {}); - }, []); - - const change = val => { - setContent(val); setSaved(false); clearTimeout(tmr.current); - tmr.current = setTimeout(async () => { - try { const r = await api('/dashboard/note', { method:'PUT', body:{ content:val } }); setSaved(true); setSaveTime(r.updated_at); } - catch(e) { toast(e.message, 'error'); } - }, 900); - }; - - return ( -
-
-
NOTIZEN
-
- {saved ? (saveTime ? `โœ“ ${new Date(saveTime).toLocaleTimeString('de-DE', { hour:'2-digit', minute:'2-digit' })}` : 'โœ“') : 'โ€ฆ'} -
-
-