Dateien nach "frontend/src" hochladen

This commit is contained in:
2026-05-27 08:13:57 +02:00
parent cf63623fd6
commit e02e125806
2 changed files with 45 additions and 34 deletions

View File

@@ -609,9 +609,14 @@ function Notepad({ toast }) {
return ( return (
<div style={{ ...S.card }}> <div style={{ ...S.card }}>
<button onClick={()=>setIsOpen(v=>!v)} style={{width:'100%',background:'transparent',border:'none', <button onClick={()=>setIsOpen(v=>!v)} style={{width:'100%',background:'transparent',border:'none',
display:'flex',justifyContent:'space-between',alignItems:'center',cursor:'pointer',padding:0}}> display:'flex',alignItems:'center',gap:8,cursor:'pointer',padding:0}}>
<div style={S.head}>NOTIZEN</div> <div style={S.head}>NOTIZEN</div>
<span style={{color:'rgba(255,255,255,0.4)',fontSize:11,display:'inline-block', {!isOpen && content.length > 0 && (
<span style={{color:'rgba(255,255,255,0.45)',fontFamily:'monospace',fontSize:10}}>
{content.length} Zeichen
</span>
)}
<span style={{color:'rgba(255,255,255,0.4)',fontSize:11,display:'inline-block',marginLeft:'auto',
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 && <div style={{ color: saved ? 'rgba(78,205,196,0.5)' : 'rgba(255,230,109,0.6)', {isOpen && <div style={{ color: saved ? 'rgba(78,205,196,0.5)' : 'rgba(255,230,109,0.6)',
@@ -1158,7 +1163,10 @@ function AdminPanel({ toast, mobile, user }) {
// ── App Shell ───────────────────────────────────────────────────────────────── // ── App Shell ─────────────────────────────────────────────────────────────────
export default function App() { export default function App() {
const mobile = useIsMobile(); const mobile = useIsMobile();
const [splash, setSplash] = useState(() => window.matchMedia('(display-mode: standalone)').matches); const [splash, setSplash] = useState(() => {
// Only show splash in standalone PWA mode, and only once per session
return window.matchMedia('(display-mode: standalone)').matches && !sessionStorage.getItem('splash_shown');
});
const [user,setUser]=useState(null); const [user,setUser]=useState(null);
const [active,setActive]=useState('dashboard'); const [active,setActive]=useState('dashboard');
const [toastMsg,setToastMsg]=useState({msg:'',type:'success'}); const [toastMsg,setToastMsg]=useState({msg:'',type:'success'});
@@ -1187,7 +1195,7 @@ export default function App() {
setTimeout(()=>setToastMsg({msg:'',type:'success'}),3500); setTimeout(()=>setToastMsg({msg:'',type:'success'}),3500);
},[]); },[]);
if(splash) return <SplashScreen onDone={()=>setSplash(false)}/>; if(splash) return <SplashScreen onDone={()=>{ setSplash(false); sessionStorage.setItem('splash_shown','1'); }}/>;
if(!user) return <Login onLogin={u=>setUser(u)}/>; if(!user) return <Login onLogin={u=>setUser(u)}/>;
const activeTool = TOOLS.find(t=>t.id===active); const activeTool = TOOLS.find(t=>t.id===active);

View File

@@ -122,6 +122,9 @@ export default function CalendarWidget() {
}; };
const refresh = () => { api('/calendar/feeds').then(f=>{ setFeeds(f); loadFeeds(f); }).catch(()=>{}); }; const refresh = () => { api('/calendar/feeds').then(f=>{ setFeeds(f); loadFeeds(f); }).catch(()=>{}); };
// Load on mount so next event shows without opening
useEffect(() => { refresh(); }, []);
// Reload when opened
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)
@@ -155,9 +158,27 @@ export default function CalendarWidget() {
{/* Header */} {/* Header */}
<div style={{ display:'flex', justifyContent:'space-between', alignItems:'center' }}> <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center' }}>
<button onClick={()=>setIsOpen(v=>!v)} style={{ flex:1, background:'transparent', border:'none', <button onClick={()=>setIsOpen(v=>!v)} style={{ flex:1, background:'transparent', border:'none',
display:'flex', justifyContent:'space-between', alignItems:'center', cursor:'pointer', padding:0 }}> display:'flex', justifyContent:'space-between', alignItems:'center', cursor:'pointer', padding:0, minWidth:0 }}>
<div style={{ display:'flex', alignItems:'center', gap:10, flex:1, minWidth:0 }}>
<div style={S.head}>KALENDER</div> <div style={S.head}>KALENDER</div>
<span style={{ color:'rgba(255,255,255,0.4)', fontSize:11, display:'inline-block', {!isOpen && nextEvent && (
<div style={{ display:'flex', alignItems:'center', gap:6, flex:1, minWidth:0, overflow:'hidden' }}>
<span style={{ width:6, height:6, borderRadius:'50%', background:nextEvent.color||'#4ecdc4',
flexShrink:0, display:'inline-block' }}/>
<span style={{ color:'rgba(255,255,255,0.6)', fontFamily:'monospace', fontSize:10,
overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
{nextEvent.summary}
</span>
<span style={{ color:'rgba(255,255,255,0.35)', fontFamily:'monospace', fontSize:9, flexShrink:0 }}>
{nextEvent.allDay
? nextEvent.dtstart.toLocaleDateString('de-DE',{day:'numeric',month:'short'})
: nextEvent.dtstart.toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'})
}
</span>
</div>
)}
</div>
<span style={{ color:'rgba(255,255,255,0.4)', fontSize:11, display:'inline-block', flexShrink:0,
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 && feeds.length > 0 && ( {isOpen && feeds.length > 0 && (
@@ -170,23 +191,7 @@ export default function CalendarWidget() {
)} )}
</div> </div>
{/* Nächster Termin auch im zugeklappten Zustand */}
{!isOpen && nextEvent && (
<div style={{ marginTop:8, display:'flex', alignItems:'center', gap:8 }}>
<span style={{ width:6, height:6, borderRadius:'50%', background:nextEvent.color||'#4ecdc4',
flexShrink:0, display:'inline-block' }}/>
<div style={{ flex:1, minWidth:0 }}>
<div style={{ color:'rgba(255,255,255,0.75)', fontFamily:'monospace', fontSize:11,
overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{nextEvent.summary}</div>
<div style={{ color:'rgba(255,255,255,0.4)', fontFamily:'monospace', fontSize:9 }}>
{nextEvent.allDay
? nextEvent.dtstart.toLocaleDateString('de-DE',{weekday:'short',day:'numeric',month:'short'})
: `${nextEvent.dtstart.toLocaleDateString('de-DE',{weekday:'short',day:'numeric',month:'short'})} ${nextEvent.dtstart.toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'})}`
}
</div>
</div>
</div>
)}
{isOpen && ( {isOpen && (
<div style={{ marginTop:12 }}> <div style={{ marginTop:12 }}>
@@ -210,20 +215,18 @@ export default function CalendarWidget() {
<span style={{ color:'#fff', fontFamily:"'Space Mono',monospace", fontSize:13, fontWeight:700 }}> <span style={{ color:'#fff', fontFamily:"'Space Mono',monospace", fontSize:13, fontWeight:700 }}>
{MONTHS[cur.m]} {cur.y} {MONTHS[cur.m]} {cur.y}
</span> </span>
{(cur.m !== today.getMonth() || cur.y !== today.getFullYear()) && (
<button onClick={() => { setCur({ y:today.getFullYear(), m:today.getMonth() }); setSelected(new Date()); }} <button onClick={() => { setCur({ y:today.getFullYear(), m:today.getMonth() }); setSelected(new Date()); }}
style={{ background:'rgba(78,205,196,0.15)', border:'1px solid rgba(78,205,196,0.3)', style={{ background:'rgba(78,205,196,0.15)', border:'1px solid rgba(78,205,196,0.3)',
borderRadius:6, color:'#4ecdc4', fontFamily:'monospace', fontSize:9, borderRadius:6, color:'#4ecdc4', fontFamily:'monospace', fontSize:9,
cursor:'pointer', padding:'2px 8px' }}>Heute</button> cursor:'pointer', padding:'2px 8px' }}>Heute</button>
)}
</div> </div>
<button onClick={nextMonth} style={{ background:'transparent', border:'none', color:'rgba(255,255,255,0.5)', <button onClick={nextMonth} style={{ background:'transparent', border:'none', color:'rgba(255,255,255,0.5)',
cursor:'pointer', fontSize:18, padding:'0 6px' }}></button> cursor:'pointer', fontSize:18, padding:'0 6px' }}></button>
</div> </div>
<div style={{ display:'flex', gap:16, alignItems:'flex-start' }}> <div style={{ display: window.innerWidth>=768 ? 'flex' : 'block', gap:16, alignItems:'flex-start' }}>
{/* Kalender-Grid */} {/* Kalender-Grid */}
<div style={{ flex:'0 0 auto', width: window.innerWidth>=768 ? 'min(320px,50%)' : '100%' }}> <div style={{ flex:'0 0 auto', width: window.innerWidth>=768 ? 'min(300px,50%)' : '100%' }}>
{/* Wochentage */} {/* Wochentage */}
<div style={{ display:'grid', gridTemplateColumns:'repeat(7,1fr)', gap:2, marginBottom:4 }}> <div style={{ display:'grid', gridTemplateColumns:'repeat(7,1fr)', gap:2, marginBottom:4 }}>
@@ -268,8 +271,8 @@ export default function CalendarWidget() {
</div>{/* Ende Kalender-Grid */} </div>{/* Ende Kalender-Grid */}
{/* Termine auf PC rechts, auf Mobile darunter */} {/* Termine */}
<div style={{ flex:1, minWidth:0, ...(window.innerWidth<768 ? {width:'100%'} : {}) }}> <div style={{ flex:1, minWidth:0, width:'100%', marginTop: window.innerWidth>=768 ? 0 : 10 }}>
{/* Ausgewählter Tag Events */} {/* Ausgewählter Tag Events */}
{selected && ( {selected && (
<div style={{ marginTop:10, borderTop:'1px solid rgba(255,255,255,0.07)', paddingTop:10 }}> <div style={{ marginTop:10, borderTop:'1px solid rgba(255,255,255,0.07)', paddingTop:10 }}>