// Sidebar — sessions, collections, smart filters
// On mobile (< 768px): slide-over drawer with backdrop
// On desktop: inline sidebar
function Sidebar({ open, setOpen,
events, activeEvent, setActiveEvent, activeShootId, onCreateEvent, onActivateEvent, onDeleteEvent,
collections, activeCollection, setActiveCollection, onDeleteCollection,
tagFilter, setTagFilter, ratingFilter, setRatingFilter,
flagFilter, setFlagFilter, fileTypeFilter, setFileTypeFilter,
tagCounts, stats, onNewCollection, shareCount,
onOpenShares, onOpenWatermark, onOpenShortcuts, onOpenBranding,
displayName, profilePhotoUrl, onOpenProfile, onLogout }) {
const [isMobile, setIsMobile] = React.useState(() => window.innerWidth < 768);
React.useEffect(() => {
const fn = () => setIsMobile(window.innerWidth < 768);
window.addEventListener('resize', fn);
return () => window.removeEventListener('resize', fn);
}, []);
// Auto-close on mobile when a filter row is clicked
const mobileClose = () => { if (isMobile) setOpen(false); };
const inner = (
{/* Brand */}
Live
{/* Close button — mobile only */}
{isMobile && (
)}
{/* Events */}
}>
{ setActiveEvent(null); mobileClose(); }}
icon={} label="All events"
badge={{stats.total}}/>
{stats.unassigned > 0 && (
{ setActiveEvent('_unassigned'); mobileClose(); }}
icon={}
label="Unassigned"
badge={{stats.unassigned}}/>
)}
{(events || []).map(e => (
{ setActiveEvent(e.id); mobileClose(); }}
onActivate={() => onActivateEvent(e.id)}
onDeactivate={() => onActivateEvent(null)}
onDelete={() => onDeleteEvent(e.id)}
/>
))}
{(!events || events.length === 0) && (
No events yet — click + to create one
)}
{/* Flag filters */}
{ setFlagFilter('all'); mobileClose(); }} icon={} label="All photos" badge={{stats.total}}/>
{ setFlagFilter('picks'); mobileClose(); }}
icon={} label="Picks"
badge={{stats.picks}}/>
{ setFlagFilter('rejects'); mobileClose(); }}
icon={} label="Rejects"
badge={{stats.rejects}}/>
{ setFlagFilter('unflagged'); mobileClose(); }}
icon={} label="Unflagged"
badge={{stats.unflagged}}/>
{[0,1,2,3,4,5].map(r => (
))}
{['all','RAW','JPG'].map(t => (
))}
{/* Collections */}
}>
{ setActiveCollection(null); mobileClose(); }}
icon={} label="None"/>
{collections.map(c => (
{ setActiveCollection(c.name); mobileClose(); }}
icon={} label={c.name}
badge={{c.count || (c.files && c.files.length) || 0}}
onDelete={onDeleteCollection ? () => onDeleteCollection(c.name) : null}/>
))}
{/* Tags */}
{Object.keys(tagCounts).length > 0 && (
{Object.entries(tagCounts).sort((a,b) => b[1] - a[1]).map(([tag, count]) => (
))}
)}
{/* Footer actions */}
} label={`Share links · ${shareCount}`} onClick={() => { onOpenShares(); mobileClose(); }}/>
} label="Watermark" onClick={() => { onOpenWatermark(); mobileClose(); }}/>
} label="Branding" onClick={() => { onOpenBranding(); mobileClose(); }}/>
} label="Shortcuts" onClick={() => { onOpenShortcuts(); mobileClose(); }}/>
{/* Profile footer */}
);
// ── Mobile: slide-over drawer ──────────────────────────────────────────────
if (isMobile) {
return (
<>
{/* Backdrop */}
{open && (
setOpen(false)} style={{
position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.55)',
zIndex: 290, backdropFilter: 'blur(2px)',
animation: 'fade-in 160ms',
}}/>
)}
{/* Drawer */}
>
);
}
// ── Desktop: inline sidebar ────────────────────────────────────────────────
return (
);
}
function Section({ title, action, children }) {
return (
{title}
{action}
{children}
);
}
function Row({ icon, label, badge, active, onClick }) {
return (
);
}
function DeletableRow({ icon, label, badge, active, onClick, onDelete }) {
const [hover, setHover] = React.useState(false);
return (
setHover(true)} onMouseLeave={() => setHover(false)}
style={{ display: 'flex', alignItems: 'center', margin: '1px 8px', borderRadius: 4,
background: active ? 'var(--bg-3)' : hover ? 'var(--bg-2)' : 'transparent',
transition: 'background 100ms', cursor: 'pointer' }}
onClick={onClick}>
{icon}
{label}
{badge && !hover && badge}
{hover && onDelete && (
)}
);
}
function EventRow({ event, active, isLive, onClick, onActivate, onDeactivate, onDelete }) {
const [hover, setHover] = React.useState(false);
return (
setHover(true)}
onMouseLeave={() => setHover(false)}
onClick={onClick}
style={{
position: 'relative', display: 'flex', alignItems: 'center',
margin: '1px 8px', borderRadius: 4,
background: active ? 'var(--bg-3)' : hover ? 'var(--bg-2)' : 'transparent',
transition: 'background 100ms', cursor: 'pointer',
}}>
{isLive
?
:
}
{isLive && Live}
{event.name}
{event.date} · {event.count || 0}
{hover && (
e.stopPropagation()}>
{isLive ? (
) : (
)}
)}
);
}
Object.assign(window, { Sidebar });