fix: mobile UX overhaul — collapsible filters, settings nav, responsive layout
Deploy HiHala Dashboard / deploy (push) Successful in 8s

- Add Settings link to desktop nav bar for admin users
- Rewrite Settings page from table layout to responsive card list (fixes unusable mobile state)
- Filter bar (Dashboard + Comparison): collapsible panel on mobile via display:contents trick; stacked full-width dropdowns replace horizontal scroll
- Active filter count badge shown in collapsed filter header
- AltMultiSelect dropdowns go full-width on mobile to prevent viewport overflow
- Chart control separators hidden on mobile to avoid crowding
- Metric grid: 2-col at ≤700px, 1-col at ≤480px
- Comparison period cards: smaller font and tighter padding at ≤680px
- Page shell padding reduced on mobile (48px→20px top, 24px→16px sides)
- Settings page gets correct 80px bottom padding for mobile nav

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
fahed
2026-04-28 12:22:07 +03:00
parent 30cdb5064a
commit c9cfb58896
5 changed files with 447 additions and 218 deletions
+19 -7
View File
@@ -200,6 +200,8 @@ export default function PeriodSelectorDemo({ data, seasons, includeVAT, allowedM
const prevCapture = prevPilgrims ? prevM.visitors/prevPilgrims*100 : null;
const hasFilters = selDistricts.length>0 || selChannels.length>0 || selMuseums.length>0;
const activeFilterCount = selDistricts.length + selChannels.length + selMuseums.length;
const [filtersOpen, setFiltersOpen] = useState(false);
return (
<div
@@ -231,13 +233,23 @@ export default function PeriodSelectorDemo({ data, seasons, includeVAT, allowedM
onChange={(s,e) => { setPrevStart(s); setPrevEnd(e); }} availableYears={availableYears} L={L} />
</div>
<div className="alt-filter-bar">
<span className="alt-filter-label">{L.filter}</span>
<div className="alt-filter-sep" />
<AltMultiSelect value={selDistricts} options={districts} onChange={setSelDistricts} allLabel={L.allDistricts} countLabel={L.countDistricts} clearLabel={L.clearSel} />
<AltMultiSelect value={selChannels} options={channels} onChange={setSelChannels} allLabel={L.allChannels} countLabel={L.countChannels} clearLabel={L.clearSel} />
<AltMultiSelect value={selMuseums} options={museums} onChange={setSelMuseums} allLabel={L.allMuseums} countLabel={L.countMuseums} clearLabel={L.clearSel} />
{hasFilters && <button type="button" className="alt-filter-reset" onClick={() => { setSelDistricts([]); setSelChannels([]); setSelMuseums([]); }}>{L.reset}</button>}
<div className={`alt-filter-bar${filtersOpen ? ' alt-filter-bar--open' : ''}`}>
<div className="alt-filter-head">
<span className="alt-filter-label">{L.filter}</span>
{activeFilterCount > 0 && <span className="alt-filter-badge">{activeFilterCount}</span>}
<button type="button" className="alt-filter-toggle" onClick={() => setFiltersOpen(v => !v)} aria-expanded={filtersOpen} aria-label="Toggle filters">
<svg className={`alt-filter-chevron${filtersOpen ? ' alt-filter-chevron--open' : ''}`} width="14" height="14" viewBox="0 0 10 10" fill="none">
<path d="M2 3.5L5 6.5L8 3.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
</div>
<div className="alt-filter-body">
<div className="alt-filter-sep" />
<AltMultiSelect value={selDistricts} options={districts} onChange={setSelDistricts} allLabel={L.allDistricts} countLabel={L.countDistricts} clearLabel={L.clearSel} />
<AltMultiSelect value={selChannels} options={channels} onChange={setSelChannels} allLabel={L.allChannels} countLabel={L.countChannels} clearLabel={L.clearSel} />
<AltMultiSelect value={selMuseums} options={museums} onChange={setSelMuseums} allLabel={L.allMuseums} countLabel={L.countMuseums} clearLabel={L.clearSel} />
{hasFilters && <button type="button" className="alt-filter-reset" onClick={() => { setSelDistricts([]); setSelChannels([]); setSelMuseums([]); }}>{L.reset}</button>}
</div>
</div>
<div className="alt-section-heading"><h2>{L.keyMetrics}</h2></div>