Dateien nach "frontend/src" hochladen
This commit is contained in:
@@ -609,9 +609,14 @@ function Notepad({ toast }) {
|
||||
return (
|
||||
<div style={{ ...S.card }}>
|
||||
<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>
|
||||
<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>
|
||||
</button>
|
||||
{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 ─────────────────────────────────────────────────────────────────
|
||||
export default function App() {
|
||||
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 [active,setActive]=useState('dashboard');
|
||||
const [toastMsg,setToastMsg]=useState({msg:'',type:'success'});
|
||||
@@ -1187,7 +1195,7 @@ export default function App() {
|
||||
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)}/>;
|
||||
|
||||
const activeTool = TOOLS.find(t=>t.id===active);
|
||||
|
||||
@@ -122,6 +122,9 @@ export default function CalendarWidget() {
|
||||
};
|
||||
|
||||
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]);
|
||||
|
||||
// Nächster oder aktueller Termin (für zugeklappte Ansicht)
|
||||
@@ -155,9 +158,27 @@ export default function CalendarWidget() {
|
||||
{/* Header */}
|
||||
<div style={{ display:'flex', justifyContent:'space-between', alignItems:'center' }}>
|
||||
<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>
|
||||
<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>
|
||||
</button>
|
||||
{isOpen && feeds.length > 0 && (
|
||||
@@ -170,23 +191,7 @@ export default function CalendarWidget() {
|
||||
)}
|
||||
</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 && (
|
||||
<div style={{ marginTop:12 }}>
|
||||
@@ -210,20 +215,18 @@ export default function CalendarWidget() {
|
||||
<span style={{ color:'#fff', fontFamily:"'Space Mono',monospace", fontSize:13, fontWeight:700 }}>
|
||||
{MONTHS[cur.m]} {cur.y}
|
||||
</span>
|
||||
{(cur.m !== today.getMonth() || cur.y !== today.getFullYear()) && (
|
||||
<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)',
|
||||
borderRadius:6, color:'#4ecdc4', fontFamily:'monospace', fontSize:9,
|
||||
cursor:'pointer', padding:'2px 8px' }}>Heute</button>
|
||||
)}
|
||||
</div>
|
||||
<button onClick={nextMonth} style={{ background:'transparent', border:'none', color:'rgba(255,255,255,0.5)',
|
||||
cursor:'pointer', fontSize:18, padding:'0 6px' }}>›</button>
|
||||
</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 */}
|
||||
<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 */}
|
||||
<div style={{ display:'grid', gridTemplateColumns:'repeat(7,1fr)', gap:2, marginBottom:4 }}>
|
||||
@@ -268,8 +271,8 @@ export default function CalendarWidget() {
|
||||
|
||||
</div>{/* Ende Kalender-Grid */}
|
||||
|
||||
{/* Termine – auf PC rechts, auf Mobile darunter */}
|
||||
<div style={{ flex:1, minWidth:0, ...(window.innerWidth<768 ? {width:'100%'} : {}) }}>
|
||||
{/* Termine */}
|
||||
<div style={{ flex:1, minWidth:0, width:'100%', marginTop: window.innerWidth>=768 ? 0 : 10 }}>
|
||||
{/* Ausgewählter Tag Events */}
|
||||
{selected && (
|
||||
<div style={{ marginTop:10, borderTop:'1px solid rgba(255,255,255,0.07)', paddingTop:10 }}>
|
||||
|
||||
Reference in New Issue
Block a user