etsy-app / frontend /components /ShopSelector.tsx
lethientien's picture
Upload 28 files
e4dfa4d verified
import React, { useState, useMemo } from 'react';
import { useShop } from '../contexts/ShopContext';
import { Shop } from '../types';
interface ShopSelectorProps {
compact?: boolean;
}
const ShopSelector: React.FC<ShopSelectorProps> = ({ compact = false }) => {
const { shops, currentShop, setCurrentShop, isLoading, isAllShopsMode } = useShop();
const [isOpen, setIsOpen] = useState(false);
// Calculate total orders across all shops
const totalOrders = useMemo(() => {
return shops.reduce((sum, shop) => sum + (shop.order_count || 0), 0);
}, [shops]);
if (isLoading) {
return (
<div className="flex items-center gap-2 px-3 py-2 rounded-lg bg-slate-100 animate-pulse">
<div className="w-8 h-8 rounded-full bg-slate-200"></div>
<div className="h-4 w-24 bg-slate-200 rounded"></div>
</div>
);
}
if (shops.length === 0) {
return (
<div className="flex items-center gap-2 px-3 py-2 rounded-lg bg-amber-50 border border-amber-200">
<span className="material-symbols-outlined text-amber-600">warning</span>
<span className="text-sm text-amber-700">No shops configured</span>
</div>
);
}
const handleSelect = (shop: Shop | null) => {
setCurrentShop(shop);
setIsOpen(false);
};
// Determine display text and styling based on mode
const displayName = isAllShopsMode ? 'All Shops' : (currentShop?.name || 'Select Shop');
const displayLabel = isAllShopsMode ? 'Viewing All' : 'Current Shop';
return (
<div className="relative">
<button
onClick={() => setIsOpen(!isOpen)}
className={`
flex items-center gap-2 px-3 py-2 rounded-lg
${isAllShopsMode
? 'bg-gradient-to-r from-purple-100 to-indigo-100 border border-purple-200 hover:border-purple-300'
: 'bg-gradient-to-r from-primary/10 to-primary/5 border border-primary/20 hover:border-primary/40'
}
transition-all duration-200 group
${compact ? 'pr-2' : 'min-w-[200px]'}
`}
>
{/* Shop Icon */}
<div className={`w-8 h-8 rounded-full flex items-center justify-center shadow-sm ${isAllShopsMode
? 'bg-gradient-to-br from-purple-500 to-indigo-500'
: 'bg-gradient-to-br from-primary to-primary/70'
}`}>
<span className="material-symbols-outlined text-white text-sm">
{isAllShopsMode ? 'dashboard' : 'storefront'}
</span>
</div>
{/* Shop Name */}
{!compact && (
<div className="flex-1 text-left overflow-hidden">
<p className="text-xs text-text-secondary font-medium">{displayLabel}</p>
<p className={`text-sm font-semibold truncate ${isAllShopsMode ? 'text-purple-700' : 'text-text-main'}`}>
{displayName}
</p>
</div>
)}
{/* Dropdown Arrow */}
<span className={`material-symbols-outlined text-text-secondary transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}>
expand_more
</span>
</button>
{/* Dropdown Menu */}
{isOpen && (
<>
{/* Backdrop */}
<div
className="fixed inset-0 z-40"
onClick={() => setIsOpen(false)}
/>
{/* Menu */}
<div className="absolute top-full left-0 right-0 mt-2 z-50 bg-white rounded-xl shadow-xl border border-border-light overflow-hidden min-w-[240px]">
<div className="p-2 border-b border-border-light bg-slate-50">
<p className="text-xs font-medium text-text-secondary px-2">Select View</p>
</div>
<div className="max-h-72 overflow-y-auto py-1">
{/* All Shops Option */}
<button
onClick={() => handleSelect(null)}
className={`
w-full flex items-center gap-3 px-3 py-3 text-left
transition-colors duration-150 border-b border-border-light
${isAllShopsMode
? 'bg-purple-50 text-purple-700'
: 'hover:bg-slate-50 text-text-main'
}
`}
>
<div className={`
w-9 h-9 rounded-full flex items-center justify-center
${isAllShopsMode
? 'bg-gradient-to-br from-purple-500 to-indigo-500 text-white'
: 'bg-gradient-to-br from-purple-100 to-indigo-100 text-purple-600'
}
`}>
<span className="material-symbols-outlined text-lg">dashboard</span>
</div>
<div className="flex-1 min-w-0">
<p className={`text-sm font-semibold ${isAllShopsMode ? 'text-purple-700' : 'text-text-main'}`}>
📊 All Shops
</p>
<p className="text-xs text-text-secondary">
{shops.length} shops • {totalOrders} total orders
</p>
</div>
{isAllShopsMode && (
<span className="material-symbols-outlined text-purple-600 text-lg">check_circle</span>
)}
</button>
{/* Divider with label */}
<div className="px-3 py-2 bg-slate-50">
<p className="text-[10px] font-semibold text-text-secondary uppercase tracking-wider">Individual Shops</p>
</div>
{/* Individual Shops */}
{shops.map((shop) => (
<button
key={shop.id}
onClick={() => handleSelect(shop)}
className={`
w-full flex items-center gap-3 px-3 py-2.5 text-left
transition-colors duration-150
${currentShop?.id === shop.id && !isAllShopsMode
? 'bg-primary/10 text-primary'
: 'hover:bg-slate-50 text-text-main'
}
`}
>
<div className={`
w-8 h-8 rounded-full flex items-center justify-center text-sm
${currentShop?.id === shop.id && !isAllShopsMode
? 'bg-primary text-white'
: 'bg-slate-200 text-text-secondary'
}
`}>
<span className="material-symbols-outlined text-sm">storefront</span>
</div>
<div className="flex-1 min-w-0">
<p className={`text-sm font-medium truncate ${currentShop?.id === shop.id && !isAllShopsMode ? 'text-primary' : ''}`}>
{shop.name}
</p>
<p className="text-xs text-text-secondary">
{shop.order_count || 0} orders
</p>
</div>
{currentShop?.id === shop.id && !isAllShopsMode && (
<span className="material-symbols-outlined text-primary text-lg">check_circle</span>
)}
</button>
))}
</div>
{/* Manage Shops Link */}
<div className="p-2 border-t border-border-light bg-slate-50">
<a
href="#/shops"
onClick={() => setIsOpen(false)}
className="flex items-center gap-2 px-2 py-1.5 text-sm text-primary hover:text-primary/80 transition-colors"
>
<span className="material-symbols-outlined text-sm">settings</span>
Manage Shops
</a>
</div>
</div>
</>
)}
</div>
);
};
export default ShopSelector;