From 75d86340910af8eafa0a08ead2493c2784bda381 Mon Sep 17 00:00:00 2001 From: 230417 <230417@epvc.pt> Date: Thu, 12 Mar 2026 15:32:50 +0000 Subject: [PATCH] . --- web/src/components/ShopCard.tsx | 154 +++++++++++++++++--------------- web/src/pages/Explore.tsx | 139 +++++++++++++--------------- 2 files changed, 145 insertions(+), 148 deletions(-) diff --git a/web/src/components/ShopCard.tsx b/web/src/components/ShopCard.tsx index 3b63d67..40bc38f 100644 --- a/web/src/components/ShopCard.tsx +++ b/web/src/components/ShopCard.tsx @@ -1,90 +1,102 @@ import { Link } from 'react-router-dom'; -import { Star, MapPin, Scissors, Heart, Calendar } from 'lucide-react'; +import { Star, MapPin, Scissors, Heart, Calendar, Users } from 'lucide-react'; import { BarberShop } from '../types'; import { useApp } from '../context/AppContext'; -export const ShopCard = ({ shop, compact = false }: { shop: BarberShop; compact?: boolean }) => { +// Paleta de gradientes para barbearias sem foto +const gradients = [ + 'from-violet-600 to-indigo-700', + 'from-amber-500 to-orange-600', + 'from-emerald-500 to-teal-700', + 'from-rose-500 to-pink-700', + 'from-sky-500 to-blue-700', + 'from-slate-600 to-slate-800', +]; + +function shopGradient(name: string) { + const idx = name.charCodeAt(0) % gradients.length; + return gradients[idx]; +} + +export const ShopCard = ({ shop }: { shop: BarberShop }) => { const { toggleFavorite, isFavorite } = useApp(); const favorite = isFavorite(shop.id); + const initials = shop.name.slice(0, 2).toUpperCase(); return ( -
- {/* Cover image */} -
- {shop.imageUrl ? ( - {shop.name} - ) : ( -
- -

Sem foto de capa

+
+ {/* Top accent strip + avatar */} +
+ {/* Avatar */} +
+
+ {shop.imageUrl ? ( + {shop.name} + ) : ( + {initials} + )} +
+ {/* Rating pill */} +
+ + + {shop.rating ? shop.rating.toFixed(1) : '—'} +
- )} - {/* Gradient overlay */} -
- - {/* Rating badge */} -
- - - {shop.rating ? shop.rating.toFixed(1) : '—'} -
- {/* Favorite button */} - + {/* Info */} +
+
+

+ {shop.name} +

+ {/* Favorite */} + +
- {/* Shop name on image */} -
-

- {shop.name} -

+
+ +

{shop.address || 'Endereço não disponível'}

+
+ +
+ + + {(shop.services || []).length} serviços + + + + {(shop.barbers || []).length} barbeiros + +
- {/* Info section */} -
-
- -

{shop.address || 'Endereço não disponível'}

-
+ {/* Separator */} +
-
- - - {(shop.services || []).length} serviços - - - {(shop.barbers || []).length} barbeiros -
- - {/* Action buttons */} -
- - Ver detalhes - - - - Agendar - -
+ {/* Actions */} +
+ + Ver detalhes + + + + Agendar +
); diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index bb72084..ace7f3b 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -4,17 +4,15 @@ import { useMemo, useState } from 'react'; import { ShopCard } from '../components/ShopCard'; import { useApp } from '../context/AppContext'; -import { Search, Heart, Compass, SlidersHorizontal } from 'lucide-react'; +import { Search, Heart, Scissors, SlidersHorizontal, X } from 'lucide-react'; export default function Explore() { const { shops, favorites } = useApp(); const [query, setQuery] = useState(''); - const [filter, setFilter] = useState<'todas' | 'top' | 'produtos' | 'barbeiros' | 'favoritas'>('todas'); + const [filter, setFilter] = useState<'todas' | 'top' | 'produtos' | 'favoritas'>('todas'); const [sortBy, setSortBy] = useState<'relevancia' | 'avaliacao' | 'preco' | 'servicos'>('relevancia'); - const favoriteShops = useMemo(() => shops.filter((s) => favorites.includes(s.id)), [shops, favorites]); - const filtered = useMemo(() => { const normalized = query.trim().toLowerCase(); const matchesQuery = (name: string, address: string) => @@ -24,7 +22,6 @@ export default function Explore() { if (filter === 'favoritas') return favorites.includes(shop.id); if (filter === 'top') return (shop.rating || 0) >= 4; if (filter === 'produtos') return (shop.products || []).length > 0; - if (filter === 'barbeiros') return (shop.barbers || []).length >= 2; return true; }; @@ -44,100 +41,88 @@ export default function Explore() { }); }, [shops, query, filter, sortBy, favorites]); - const chips: { id: typeof filter; label: string; icon?: React.ReactNode }[] = [ + const chips: { id: typeof filter; label: string }[] = [ { id: 'todas', label: 'Todas' }, - { id: 'favoritas', label: `Favoritas${favorites.length > 0 ? ` (${favorites.length})` : ''}`, icon: }, - { id: 'top', label: 'Top avaliadas' }, - { id: 'produtos', label: 'Com produtos' }, - { id: 'barbeiros', label: 'Mais barbeiros' }, + { id: 'favoritas', label: `❤️ Favoritas${favorites.length > 0 ? ` (${favorites.length})` : ''}` }, + { id: 'top', label: '⭐ Top avaliadas' }, + { id: 'produtos', label: '🛍️ Com produtos' }, ]; return ( -
- {/* Hero Header */} -
-
-
-
- - Explorar +
+ {/* Page header */} +
+
+
+ + Explorar
-

Barbearias

-

Encontre a sua favorita e agende em minutos.

+

Barbearias

+ + {filtered.length} {filtered.length === 1 ? 'resultado' : 'resultados'} +
- {/* Search & Sort bar */} -
-
-
- - setQuery(e.target.value)} - placeholder="Pesquisar por nome ou endereço..." - className="w-full pl-10 pr-4 py-2.5 rounded-xl border border-slate-200 text-sm focus:outline-none focus:ring-2 focus:ring-amber-400 focus:border-transparent placeholder:text-slate-400" - /> -
-
- - -
-
- - {/* Filter chips */} -
- {chips.map((chip) => ( - - ))} -
+ {/* Search bar */} +
+ + setQuery(e.target.value)} + placeholder="Pesquisar por nome ou endereço..." + className="w-full pl-10 pr-10 py-3 rounded-2xl border border-slate-200 bg-white text-sm focus:outline-none focus:ring-2 focus:ring-amber-400 focus:border-transparent placeholder:text-slate-400 shadow-sm" + /> + {query && ( + + )}
- {/* Results count */} -
-

- {filtered.length}{' '} - {filtered.length === 1 ? 'barbearia encontrada' : 'barbearias encontradas'} -

+ {/* Filters row */} +
+ {chips.map((chip) => ( + + ))} + +
+ + +
{/* Grid */} {filtered.length === 0 ? (
- {filter === 'favoritas' - ? - : - } + {filter === 'favoritas' ? : }

{filter === 'favoritas' ? 'Ainda não tem favoritas' : 'Nenhuma barbearia encontrada'}

{filter === 'favoritas' - ? 'Clique no ❤️ em qualquer barbearia para a guardar aqui.' - : 'Tente ajustar a pesquisa ou os filtros.'} + ? 'Clique no ❤️ de qualquer barbearia para guardar aqui.' + : 'Tente ajustar os filtros ou a pesquisa.'}

) : (