diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 414a38d..17dcc00 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -55,6 +55,32 @@ function Modal({ title, onClose, children }) { ); } +// ── PWA Splash Screen ───────────────────────────────────────────────────────── +function SplashScreen({ onDone }) { + const [fade, setFade] = useState(false); + useEffect(() => { + const t1 = setTimeout(() => setFade(true), 1200); + const t2 = setTimeout(() => onDone(), 1700); + return () => { clearTimeout(t1); clearTimeout(t2); }; + }, []); + return ( +
+
+ DICKENDOCK +
+
+
+ ); +} + // ── Login ───────────────────────────────────────────────────────────────────── function Login({ onLogin }) { const [u,setU]=useState(''); const [p,setP]=useState(''); @@ -142,17 +168,19 @@ function Sidebar({ active, setActive, user, onLogout, updateInfo, onUpdateClick const NavBtn = ({ item, sub=false }) => { const ic = active===item.id ? '#4ecdc4' : 'rgba(255,255,255,0.55)'; return ( - @@ -507,7 +535,7 @@ function TodoList({ toast }) {
{open.length === 0 && done.length === 0 && ( -
Noch keine Aufgaben.
+
Noch keine Aufgaben.
)} {open.map(item => (
@@ -636,7 +664,7 @@ function Dashboard({ toast, mobile }) {

Dashboard

-

+

{new Date().toLocaleDateString('de-DE', { weekday:'long', day:'numeric', month:'long' })}

@@ -721,93 +749,93 @@ function AdminPanel({ toast, mobile }) {

Einstellungen

- {/* Subnavigation */}
{[['profil','Profil'],['backup','Backup']].map(([k,l]) => ( ))}
- {section === 'profil' &&
- -
-
- {avatar - ? Avatar - : } -
-
- - { - const file = e.target.files?.[0]; if(!file) return; - const reader = new FileReader(); - reader.onload = ev => { - const canvas = document.createElement('canvas'); - const img = new Image(); img.onload = () => { - const size = 120; - canvas.width = size; canvas.height = size; - const ctx = canvas.getContext('2d'); - ctx.beginPath(); ctx.arc(size/2,size/2,size/2,0,Math.PI*2); - ctx.clip(); - const min = Math.min(img.width,img.height); - ctx.drawImage(img,(img.width-min)/2,(img.height-min)/2,min,min,0,0,size,size); - const data = canvas.toDataURL('image/jpeg',0.85); - setAvatar(data); localStorage.setItem('dd_avatar',data); toast('Avatar gespeichert'); - }; img.src = ev.target.result; - }; reader.readAsDataURL(file); - }} style={{...S.inp,fontSize:13,padding:'7px 10px',cursor:'pointer'}}/> -
+ {section === 'profil' && ( +
+ +
+
+ {avatar + ? Avatar + : } +
+
+ + { + const file = e.target.files?.[0]; if(!file) return; + const reader = new FileReader(); + reader.onload = ev => { + const canvas = document.createElement('canvas'); + const img = new Image(); img.onload = () => { + const size = 120; + canvas.width = size; canvas.height = size; + const ctx = canvas.getContext('2d'); + ctx.beginPath(); ctx.arc(size/2,size/2,size/2,0,Math.PI*2); + ctx.clip(); + const min = Math.min(img.width,img.height); + ctx.drawImage(img,(img.width-min)/2,(img.height-min)/2,min,min,0,0,size,size); + const data = canvas.toDataURL('image/jpeg',0.85); + setAvatar(data); localStorage.setItem('dd_avatar',data); toast('Avatar gespeichert'); + }; img.src = ev.target.result; + }; reader.readAsDataURL(file); + }} style={{...S.inp,fontSize:13,padding:'7px 10px',cursor:'pointer'}}/> +
+
+ {avatar && } +
+ + setUn(p=>({...p,newUsername:e.target.value}))} /> + setUn(p=>({...p,unPw:e.target.value}))} /> + + + + setPw(p=>({...p,current:e.target.value}))} /> + setPw(p=>({...p,newPw:e.target.value}))} /> + setPw(p=>({...p,confirm:e.target.value}))} /> + +
- {avatar && } - + )} - - setUn(p=>({...p,newUsername:e.target.value}))} /> - setUn(p=>({...p,unPw:e.target.value}))} /> - - - - - setPw(p=>({...p,current:e.target.value}))} /> - setPw(p=>({...p,newPw:e.target.value}))} /> - setPw(p=>({...p,confirm:e.target.value}))} /> - - - -
} - - {section === 'backup' &&
- -

- Vollständige SQLite-Datenbank sichern oder wiederherstellen. -

- - -
- setFile(e.target.files[0])} - style={{ ...S.inp, flex:1, padding:'9px 10px', cursor:'pointer', fontSize:13 }} /> - + {section === 'backup' && ( +
+ +

+ Vollständige SQLite-Datenbank sichern oder wiederherstellen. +

+ + +
+ setFile(e.target.files[0])} + style={{ ...S.inp, flex:1, padding:'9px 10px', cursor:'pointer', fontSize:13 }} /> + +
+ {file &&
{file.name}
} +
- {file &&
{file.name}
} - -
} + )}
); } @@ -816,6 +844,7 @@ function AdminPanel({ toast, mobile }) { // ── App Shell ───────────────────────────────────────────────────────────────── export default function App() { const mobile = useIsMobile(); + const [splash, setSplash] = useState(() => window.matchMedia('(display-mode: standalone)').matches); const [user,setUser]=useState(null); const [active,setActive]=useState('dashboard'); const [toastMsg,setToastMsg]=useState({msg:'',type:'success'}); @@ -844,6 +873,7 @@ export default function App() { setTimeout(()=>setToastMsg({msg:'',type:'success'}),3500); },[]); + if(splash) return setSplash(false)}/>; if(!user) return setUser(u)}/>; const activeTool = TOOLS.find(t=>t.id===active);