v0.9.66 minor bug fixes
This commit is contained in:
@@ -610,7 +610,7 @@ function BulkImportPanel({ onImported, onCancel }) {
|
||||
}
|
||||
|
||||
// ── Calendar Views ────────────────────────────────────────────────────────────
|
||||
function ScheduleView({ events, selectedDate, onSelect, filterKeyword='', filterTypeId='' }) {
|
||||
function ScheduleView({ events, selectedDate, onSelect, filterKeyword='', filterTypeId='', isMobile=false }) {
|
||||
const y=selectedDate.getFullYear(), m=selectedDate.getMonth();
|
||||
const monthStart=new Date(y,m,1), monthEnd=new Date(y,m+1,0,23,59,59);
|
||||
const kw=filterKeyword.toLowerCase().trim();
|
||||
@@ -624,7 +624,15 @@ function ScheduleView({ events, selectedDate, onSelect, filterKeyword='', filter
|
||||
return true;
|
||||
});
|
||||
if(!filtered.length) return <div style={{textAlign:'center',padding:'60px 20px',color:'var(--text-tertiary)',fontSize:14}}>{kw||filterTypeId?'No events match your filters':'No events in'} {!kw&&!filterTypeId&&`${MONTHS[m]} ${y}`}</div>;
|
||||
return <>{filtered.map(e=>{const s=new Date(e.start_at);const col=e.event_type?.colour||'#9ca3af';return(<div key={e.id} onClick={()=>onSelect(e)} style={{display:'flex',alignItems:'center',gap:10,padding:'12px 14px',borderBottom:'1px solid var(--border)',cursor:'pointer'}} onMouseEnter={el=>el.currentTarget.style.background='var(--background)'} onMouseLeave={el=>el.currentTarget.style.background=''}><div style={{width:36,textAlign:'center',flexShrink:0}}><div style={{fontSize:20,fontWeight:700,lineHeight:1}}>{s.getDate()}</div><div style={{fontSize:10,color:'var(--text-tertiary)',textTransform:'uppercase'}}>{SHORT_MONTHS[s.getMonth()]}, {DAYS[s.getDay()]}</div></div><div style={{width:62,flexShrink:0,display:'flex',alignItems:'flex-start',gap:5,fontSize:11,color:'var(--text-secondary)'}}><span style={{width:8,height:8,borderRadius:'50%',background:col,flexShrink:0,marginTop:3}}/>{e.all_day?<span>All day</span>:<span style={{lineHeight:1.5}}>{fmtTime(e.start_at)} –<br/>{fmtTime(e.end_at)}</span>}</div><div style={{flex:1,minWidth:0}}><div style={{fontSize:14,fontWeight:600,display:'flex',alignItems:'center',gap:6,flexWrap:'nowrap'}}>{e.event_type?.name&&<span style={{fontSize:10,color:'var(--text-tertiary)',textTransform:'uppercase',letterSpacing:'0.5px',fontWeight:600,flexShrink:0}}>{e.event_type.name}:</span>}<span style={{minWidth:0,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{e.title}</span>{!!e.track_availability&&(
|
||||
return <>{filtered.map(e=>{const s=new Date(e.start_at);const col=e.event_type?.colour||'#9ca3af';
|
||||
// Desktop: original pre-v0.9.64 sizes. Mobile: compact sizes from v0.9.64
|
||||
const rowPad=isMobile?'12px 14px':'14px 20px';
|
||||
const rowGap=isMobile?10:20;
|
||||
const datW=isMobile?36:44; const datFs=isMobile?20:22; const datSFs=isMobile?10:11;
|
||||
const timeW=isMobile?62:100; const timeGap=isMobile?5:8; const timeFs=isMobile?11:13;
|
||||
const dotSz=isMobile?8:10;
|
||||
const typeFs=isMobile?10:11; const titleGap=isMobile?6:8;
|
||||
return(<div key={e.id} onClick={()=>onSelect(e)} style={{display:'flex',alignItems:'center',gap:rowGap,padding:rowPad,borderBottom:'1px solid var(--border)',cursor:'pointer'}} onMouseEnter={el=>el.currentTarget.style.background='var(--background)'} onMouseLeave={el=>el.currentTarget.style.background=''}><div style={{width:datW,textAlign:'center',flexShrink:0}}><div style={{fontSize:datFs,fontWeight:700,lineHeight:1}}>{s.getDate()}</div><div style={{fontSize:datSFs,color:'var(--text-tertiary)',textTransform:'uppercase'}}>{SHORT_MONTHS[s.getMonth()]}, {DAYS[s.getDay()]}</div></div><div style={{width:timeW,flexShrink:0,display:'flex',alignItems:'flex-start',gap:timeGap,fontSize:timeFs,color:'var(--text-secondary)'}}><span style={{width:dotSz,height:dotSz,borderRadius:'50%',background:col,flexShrink:0,marginTop:3}}/>{e.all_day?<span>All day</span>:<span style={{lineHeight:1.5}}>{fmtTime(e.start_at)} –<br/>{fmtTime(e.end_at)}</span>}</div><div style={{flex:1,minWidth:0}}><div style={{fontSize:14,fontWeight:600,display:'flex',alignItems:'center',gap:titleGap,flexWrap:'nowrap'}}>{e.event_type?.name&&<span style={{fontSize:typeFs,color:'var(--text-tertiary)',textTransform:'uppercase',letterSpacing:'0.5px',fontWeight:600,flexShrink:0}}>{e.event_type.name}:</span>}<span style={{minWidth:0,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{e.title}</span>{!!e.track_availability&&(
|
||||
e.my_response ? RESP_ICON[e.my_response](RESP_COLOR[e.my_response]) : BELL_ICON
|
||||
)}</div>{e.location&&<div style={{fontSize:12,color:'var(--text-tertiary)',marginTop:2}}>{e.location}</div>}</div></div>);})}</>;
|
||||
}
|
||||
@@ -885,7 +893,14 @@ export default function SchedulePage({ isToolManager, isMobile, onProfile, onHel
|
||||
const handleSaved = () => { load(); setPanel('calendar'); setEditingEvent(null); };
|
||||
const handleDelete = async e => {
|
||||
if (!confirm(`Delete "${e.title}"?`)) return;
|
||||
try { await api.deleteEvent(e.id); toast('Deleted','success'); load(); setDetailEvent(null); } catch(err) { toast(err.message,'error'); }
|
||||
try {
|
||||
await api.deleteEvent(e.id);
|
||||
toast('Deleted','success');
|
||||
setPanel('calendar');
|
||||
setEditingEvent(null);
|
||||
setDetailEvent(null);
|
||||
load(); // reload list so deleted event disappears immediately
|
||||
} catch(err) { toast(err.message,'error'); }
|
||||
};
|
||||
|
||||
if (loading) return <div style={{display:'flex',alignItems:'center',justifyContent:'center',flex:1,color:'var(--text-tertiary)',fontSize:14}}>Loading schedule…</div>;
|
||||
@@ -969,7 +984,11 @@ export default function SchedulePage({ isToolManager, isMobile, onProfile, onHel
|
||||
<div style={{ display:'flex', alignItems:'center', gap:8, padding:'8px 16px', borderBottom:'1px solid var(--border)', background:'var(--surface)', flexShrink:0, flexWrap:'nowrap' }}>
|
||||
{/* Mobile title + create */}
|
||||
{isMobile && (
|
||||
<span style={{ fontSize:15, fontWeight:700, flex:1 }}>Team Schedule</span>
|
||||
<>
|
||||
<span style={{ fontSize:15, fontWeight:700, flex:1 }}>Team Schedule</span>
|
||||
{/* User footer trigger on mobile — compact avatar button */}
|
||||
<UserFooter onProfile={onProfile} onHelp={onHelp} onAbout={onAbout} mobileCompact={true} />
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isMobile && (
|
||||
@@ -1004,7 +1023,7 @@ export default function SchedulePage({ isToolManager, isMobile, onProfile, onHel
|
||||
|
||||
{/* Calendar or panel content */}
|
||||
<div style={{ flex:1, overflowY:'auto', overflowX: panel==='eventForm'?'auto':'hidden' }}>
|
||||
{panel === 'calendar' && view === 'schedule' && <ScheduleView events={events} selectedDate={selDate} onSelect={openDetail} filterKeyword={filterKeyword} filterTypeId={filterTypeId}/>}
|
||||
{panel === 'calendar' && view === 'schedule' && <ScheduleView events={events} selectedDate={selDate} onSelect={openDetail} filterKeyword={filterKeyword} filterTypeId={filterTypeId} isMobile={isMobile}/>}
|
||||
{panel === 'calendar' && view === 'day' && <DayView events={events} selectedDate={selDate} onSelect={openDetail}/>}
|
||||
{panel === 'calendar' && view === 'week' && <WeekView events={events} selectedDate={selDate} onSelect={openDetail}/>}
|
||||
{panel === 'calendar' && view === 'month' && <MonthView events={events} selectedDate={selDate} onSelect={openDetail} onSelectDay={d=>{setSelDate(d);setView('schedule');}}/>}
|
||||
|
||||
@@ -11,7 +11,7 @@ function useTheme() {
|
||||
return [dark, setDark];
|
||||
}
|
||||
|
||||
export default function UserFooter({ onProfile, onHelp, onAbout }) {
|
||||
export default function UserFooter({ onProfile, onHelp, onAbout, mobileCompact=false }) {
|
||||
const { user, logout } = useAuth();
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
const [dark, setDark] = useTheme();
|
||||
@@ -32,6 +32,26 @@ export default function UserFooter({ onProfile, onHelp, onAbout }) {
|
||||
|
||||
const handleLogout = async () => { await logout(); };
|
||||
|
||||
if (mobileCompact) return (
|
||||
<div style={{ position:'relative' }}>
|
||||
<button ref={btnRef} onClick={() => setShowMenu(!showMenu)} style={{ background:'none',border:'none',cursor:'pointer',padding:2,display:'flex',alignItems:'center' }}>
|
||||
<Avatar user={user} size="sm" />
|
||||
</button>
|
||||
{showMenu && (
|
||||
<div ref={menuRef} style={{ position:'absolute',right:0,top:'calc(100% + 4px)',background:'var(--surface)',border:'1px solid var(--border)',borderRadius:'var(--radius)',boxShadow:'0 4px 16px rgba(0,0,0,0.15)',minWidth:180,zIndex:200 }}>
|
||||
<div style={{ padding:'10px 14px',borderBottom:'1px solid var(--border)',fontSize:13,fontWeight:600 }}>{user?.display_name||user?.name}</div>
|
||||
{[['Profile',()=>{setShowMenu(false);onProfile?.();}],['Help',()=>{setShowMenu(false);onHelp?.();}],['About',()=>{setShowMenu(false);onAbout?.();}]].map(([label,action])=>(
|
||||
<button key={label} onClick={action} style={{ display:'block',width:'100%',padding:'11px 14px',textAlign:'left',fontSize:14,background:'none',border:'none',cursor:'pointer',color:'var(--text-primary)' }}
|
||||
onMouseEnter={e=>e.currentTarget.style.background='var(--background)'} onMouseLeave={e=>e.currentTarget.style.background=''}>{label}</button>
|
||||
))}
|
||||
<div style={{ borderTop:'1px solid var(--border)' }}>
|
||||
<button onClick={handleLogout} style={{ display:'block',width:'100%',padding:'11px 14px',textAlign:'left',fontSize:14,background:'none',border:'none',cursor:'pointer',color:'var(--error)' }}>Sign out</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="sidebar-footer">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
|
||||
Reference in New Issue
Block a user