mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 12:15:23 +02:00
feat: full mobile responsive optimization for Android/iPhone
- Add bottom navigation bar (6 tabs) for mobile, hidden on desktop - Compact header with smaller logo and reduced padding on mobile - Collapsible filter bar with active filter count badge on mobile - Bottom-sheet style dropdowns with overlay and larger touch targets - Fullscreen AI chat on mobile, floating window on desktop - Responsive dashboard cards with always-visible expand button - Smaller table text and touch-friendly scroll on all data grids - iPhone safe-area support and thinner scrollbars on mobile Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
fecc7264c8
commit
9895545910
+41
-12
@@ -1,7 +1,8 @@
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { FilterState } from '../types';
|
||||
import MultiSelectDropdown from './MultiSelectDropdown';
|
||||
import { FunnelIcon } from './Icons';
|
||||
|
||||
interface FilterBarProps {
|
||||
filters: FilterState;
|
||||
@@ -20,36 +21,64 @@ interface FilterBarProps {
|
||||
}
|
||||
|
||||
const FilterBar: React.FC<FilterBarProps> = ({ filters, onFilterChange, options }) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const activeFilterCount = useMemo(() => {
|
||||
return filters.customer.length + filters.year.length + filters.month.length +
|
||||
filters.week.length + filters.line.length + filters.sku.length +
|
||||
filters.title.length + filters.asin.length + filters.stock.length;
|
||||
}, [filters]);
|
||||
|
||||
return (
|
||||
<div className="bg-slate-950 border-b border-border sticky top-0 z-[60] p-4 shadow-xl">
|
||||
<div className="max-w-7xl mx-auto flex flex-wrap gap-4 items-end">
|
||||
<div className="bg-slate-950 border-b border-border sticky top-0 z-[60] p-2 md:p-4 shadow-xl">
|
||||
{/* Mobile toggle button */}
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="md:hidden w-full flex items-center justify-between px-3 py-2 bg-surface border border-border rounded-lg text-sm text-slate-300 active:bg-slate-800 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FunnelIcon />
|
||||
<span className="font-medium">Filters</span>
|
||||
{activeFilterCount > 0 && (
|
||||
<span className="bg-primary text-white text-[10px] font-bold px-1.5 py-0.5 rounded-full min-w-[20px] text-center">
|
||||
{activeFilterCount}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<svg className={`w-4 h-4 transition-transform ${isExpanded ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Filter dropdowns - always visible on desktop, toggle on mobile */}
|
||||
<div className={`max-w-7xl mx-auto flex-wrap gap-2 md:gap-4 items-end ${isExpanded ? 'flex flex-col md:flex-row mt-3' : 'hidden md:flex md:flex-row'}`}>
|
||||
<MultiSelectDropdown
|
||||
label="Customer"
|
||||
selected={filters.customer}
|
||||
options={options.customer}
|
||||
onChange={(v) => onFilterChange('customer', v)}
|
||||
className="flex-1"
|
||||
className="w-full md:w-auto md:flex-1"
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
label="Year"
|
||||
selected={filters.year}
|
||||
options={options.year}
|
||||
onChange={(v) => onFilterChange('year', v)}
|
||||
className="flex-1"
|
||||
className="w-full md:w-auto md:flex-1"
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
label="Month"
|
||||
selected={filters.month}
|
||||
options={options.month}
|
||||
onChange={(v) => onFilterChange('month', v)}
|
||||
className="flex-1"
|
||||
className="w-full md:w-auto md:flex-1"
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
label="Week"
|
||||
selected={filters.week}
|
||||
options={options.week}
|
||||
onChange={(v) => onFilterChange('week', v)}
|
||||
className="flex-1"
|
||||
className="w-full md:w-auto md:flex-1"
|
||||
enableRangeSelect={true}
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
@@ -57,35 +86,35 @@ const FilterBar: React.FC<FilterBarProps> = ({ filters, onFilterChange, options
|
||||
selected={filters.line}
|
||||
options={options.line}
|
||||
onChange={(v) => onFilterChange('line', v)}
|
||||
className="flex-1"
|
||||
className="w-full md:w-auto md:flex-1"
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
label="SKU"
|
||||
selected={filters.sku}
|
||||
options={options.sku}
|
||||
onChange={(v) => onFilterChange('sku', v)}
|
||||
className="flex-1"
|
||||
className="w-full md:w-auto md:flex-1"
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
label="Title"
|
||||
selected={filters.title}
|
||||
options={options.title}
|
||||
onChange={(v) => onFilterChange('title', v)}
|
||||
className="flex-1"
|
||||
className="w-full md:w-auto md:flex-1"
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
label="ASIN"
|
||||
selected={filters.asin}
|
||||
options={options.asin}
|
||||
onChange={(v) => onFilterChange('asin', v)}
|
||||
className="flex-1"
|
||||
className="w-full md:w-auto md:flex-1"
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
label="Stock"
|
||||
selected={filters.stock}
|
||||
options={options.stock}
|
||||
onChange={(v) => onFilterChange('stock', v)}
|
||||
className="flex-1"
|
||||
className="w-full md:w-auto md:flex-1"
|
||||
enableRangeSelect={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user