Restore collapsible filter accordions alongside new sort chip
Status and Filters dropdowns are back as expanding chips. Sort chip sits inline to the right of the two accordion headers as a peer element in a flex filter-row. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
513565ecc8
commit
03d1cd2bb6
2 changed files with 95 additions and 38 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Plus, RefreshCw, Camera, BedDouble, Layers, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react'
|
||||
import { Plus, RefreshCw, Camera, BedDouble, Layers, ChevronDown, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react'
|
||||
import type { Task, Category, Location, AppConfig, TaskStatus, Priority } from '../types'
|
||||
import { STATUS_LABELS, can } from '../types'
|
||||
import { fetchTasks, fetchLocations, fetchConfig, fetchOccupancy } from '../api'
|
||||
|
|
@ -27,6 +27,9 @@ export default function Summary() {
|
|||
const [roomsView, setRoomsView] = useState(false)
|
||||
const [groupByLocation, setGroupByLocation] = useState(false)
|
||||
|
||||
const [statusOpen, setStatusOpen] = useState(false)
|
||||
const [filtersOpen, setFiltersOpen] = useState(false)
|
||||
|
||||
type SortMode = 'default' | 'priority_desc' | 'priority_asc' | 'age_asc' | 'age_desc'
|
||||
const [sortMode, setSortMode] = useState<SortMode>('default')
|
||||
|
||||
|
|
@ -146,41 +149,72 @@ export default function Summary() {
|
|||
|
||||
{error && <div className="error-banner">{error}</div>}
|
||||
|
||||
<div className="chip-bar" style={{ marginBottom: 12 }}>
|
||||
<button className={`chip ${statusFilter === null ? 'active' : ''}`} onClick={() => setStatusFilter(null)}>
|
||||
All open
|
||||
</button>
|
||||
{OPEN_STATUSES.map(s => (
|
||||
<button key={s} className={`chip ${statusFilter === s ? 'active' : ''}`} onClick={() => setStatusFilter(statusFilter === s ? null : s)}>
|
||||
{STATUS_LABELS[s]}{counts[s] ? ` (${counts[s]})` : ''}
|
||||
</button>
|
||||
))}
|
||||
<span className="chip-divider" />
|
||||
{!roomsView && categories.map(c => (
|
||||
<button key={c.id} className={`chip ${categoryFilter === c.id ? 'active' : ''}`} onClick={() => setCategoryFilter(categoryFilter === c.id ? null : c.id)}>
|
||||
{c.name}{categoryCounts[c.id] ? ` (${categoryCounts[c.id]})` : ''}
|
||||
</button>
|
||||
))}
|
||||
<button className={`chip ${mineOnly ? 'active' : ''}`} onClick={() => setMineOnly(!mineOnly)}>
|
||||
Mine
|
||||
</button>
|
||||
{roomsCategoryExists && (
|
||||
<button
|
||||
className={`chip ${roomsView ? 'active' : ''}`}
|
||||
title="Show all rooms tasks — greyed tasks have an in-house guest so access may be limited"
|
||||
onClick={toggleRoomsView}
|
||||
>
|
||||
<BedDouble size={13} strokeWidth={1.75} />
|
||||
{roomsView && roomsCounts
|
||||
? `Rooms (${roomsCounts.free} free${roomsCounts.arriving ? ` • ${roomsCounts.arriving} arriving` : ''}${roomsCounts.occupied ? ` • ${roomsCounts.occupied} occupied` : ''})`
|
||||
: 'Rooms'}
|
||||
</button>
|
||||
)}
|
||||
<button className={`chip ${groupByLocation ? 'active' : ''}`} onClick={() => setGroupByLocation(v => !v)}>
|
||||
<Layers size={13} strokeWidth={1.75} /> Group
|
||||
</button>
|
||||
<span className="chip-divider" />
|
||||
<button className={`chip ${sortMode !== 'default' ? 'active' : ''}`} onClick={cycleSortMode}>
|
||||
<div className="filter-row">
|
||||
<div className="filter-section">
|
||||
<div className="filter-section-header" onClick={() => setStatusOpen(v => !v)}>
|
||||
<span className="filter-section-label">Status</span>
|
||||
<span className={`filter-section-summary${statusFilter === null ? ' muted' : ''}`}>
|
||||
{statusFilter === null ? 'All open' : `${STATUS_LABELS[statusFilter]}${counts[statusFilter] ? ` (${counts[statusFilter]})` : ''}`}
|
||||
</span>
|
||||
<ChevronDown size={14} strokeWidth={1.75} className={`filter-section-chevron${statusOpen ? ' open' : ''}`} />
|
||||
</div>
|
||||
{statusOpen && (
|
||||
<div className="chip-bar">
|
||||
<button className={`chip ${statusFilter === null ? 'active' : ''}`} onClick={() => setStatusFilter(null)}>
|
||||
All open
|
||||
</button>
|
||||
{OPEN_STATUSES.map(s => (
|
||||
<button key={s} className={`chip ${statusFilter === s ? 'active' : ''}`} onClick={() => setStatusFilter(statusFilter === s ? null : s)}>
|
||||
{STATUS_LABELS[s]}{counts[s] ? ` (${counts[s]})` : ''}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="filter-section">
|
||||
<div className="filter-section-header" onClick={() => setFiltersOpen(v => !v)}>
|
||||
<span className="filter-section-label">Filters</span>
|
||||
<span className={`filter-section-summary${!categoryFilter && !mineOnly && !roomsView && !groupByLocation ? ' muted' : ''}`}>
|
||||
{[
|
||||
!roomsView && categoryFilter ? categories.find(c => c.id === categoryFilter)?.name : null,
|
||||
roomsView ? 'Rooms view' : null,
|
||||
mineOnly ? 'Mine only' : null,
|
||||
groupByLocation ? 'Grouped' : null,
|
||||
].filter(Boolean).join(' · ') || 'All categories'}
|
||||
</span>
|
||||
<ChevronDown size={14} strokeWidth={1.75} className={`filter-section-chevron${filtersOpen ? ' open' : ''}`} />
|
||||
</div>
|
||||
{filtersOpen && (
|
||||
<div className="chip-bar">
|
||||
{!roomsView && categories.map(c => (
|
||||
<button key={c.id} className={`chip ${categoryFilter === c.id ? 'active' : ''}`} onClick={() => setCategoryFilter(categoryFilter === c.id ? null : c.id)}>
|
||||
{c.name}{categoryCounts[c.id] ? ` (${categoryCounts[c.id]})` : ''}
|
||||
</button>
|
||||
))}
|
||||
<button className={`chip ${mineOnly ? 'active' : ''}`} onClick={() => setMineOnly(!mineOnly)}>
|
||||
Mine
|
||||
</button>
|
||||
{roomsCategoryExists && (
|
||||
<button
|
||||
className={`chip ${roomsView ? 'active' : ''}`}
|
||||
title="Show all rooms tasks — greyed tasks have an in-house guest so access may be limited"
|
||||
onClick={toggleRoomsView}
|
||||
>
|
||||
<BedDouble size={13} strokeWidth={1.75} />
|
||||
{roomsView && roomsCounts
|
||||
? `Rooms (${roomsCounts.free} free${roomsCounts.arriving ? ` • ${roomsCounts.arriving} arriving` : ''}${roomsCounts.occupied ? ` • ${roomsCounts.occupied} occupied` : ''})`
|
||||
: 'Rooms Accessible View'}
|
||||
</button>
|
||||
)}
|
||||
<button className={`chip ${groupByLocation ? 'active' : ''}`} onClick={() => setGroupByLocation(v => !v)}>
|
||||
<Layers size={13} strokeWidth={1.75} /> Group by location
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button className={`chip sort-chip ${sortMode !== 'default' ? 'active' : ''}`} onClick={cycleSortMode}>
|
||||
{sortMode === 'default' && <ArrowUpDown size={13} strokeWidth={1.75} />}
|
||||
{(sortMode === 'priority_desc' || sortMode === 'age_asc') && <ArrowDown size={13} strokeWidth={1.75} />}
|
||||
{(sortMode === 'priority_asc' || sortMode === 'age_desc') && <ArrowUp size={13} strokeWidth={1.75} />}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue