feat: Add shop details tab displaying schedule, payment methods, social links, and contacts.

This commit is contained in:
2026-03-17 16:09:07 +00:00
parent 996bc91e86
commit 005c2da275
3 changed files with 153 additions and 2 deletions

View File

@@ -0,0 +1,131 @@
import { BarberShop } from '../types';
import { Copy, Facebook, Instagram, Phone, Smartphone } from 'lucide-react';
export const ShopDetailsTab = ({ shop }: { shop: BarberShop }) => {
// Mock data if not provided (matches screenshot vibe)
const schedule = shop.schedule || [
{ day: 'Segunda-feira', open: '09:00', close: '19:30' },
{ day: 'Terça-feira', open: '09:00', close: '19:30' },
{ day: 'Quarta-feira', open: '09:00', close: '19:30' },
{ day: 'Quinta-feira', open: '09:00', close: '19:30' },
{ day: 'Sexta-feira', open: '09:00', close: '19:30' },
{ day: 'Sábado', open: '09:00', close: '19:00' },
{ day: 'Domingo', open: '', close: '', closed: true },
];
const paymentMethods = shop.paymentMethods || ['Dinheiro', 'Cartão de Crédito', 'Cartão de Débito'];
const socials = shop.socialNetworks || {
whatsapp: 'https://wa.me/351900000000',
instagram: 'https://instagram.com',
facebook: 'https://facebook.com'
};
const contacts = shop.contacts || {
phone1: '252 048 754',
phone2: '252 048 754'
};
const currentDayIndex = new Date().getDay() === 0 ? 6 : new Date().getDay() - 1; // 0 for Monday, 6 for Sunday
const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text);
};
return (
<div className="flex flex-col gap-8 text-slate-100">
{/* Horário de atendimento */}
<section>
<h3 className="text-xl font-bold mb-4">Horário de atendimento</h3>
<div className="flex flex-col gap-3">
{schedule.map((slot, i) => (
<div key={i} className="flex items-center justify-between text-base">
<div className="flex items-center gap-2">
<span className={i === currentDayIndex ? 'text-slate-100' : 'text-slate-400'}>{slot.day}</span>
{i === currentDayIndex && (
<span className="px-2 py-0.5 bg-emerald-900/40 text-emerald-400 rounded-md text-xs font-medium">Hoje</span>
)}
</div>
<span className="text-slate-300">
{slot.closed ? 'Fechado' : `${slot.open} - ${slot.close}`}
</span>
</div>
))}
</div>
</section>
<hr className="border-white/10" />
{/* Formas de pagamento */}
<section>
<h3 className="text-xl font-bold mb-4">Formas de pagamento</h3>
<div className="flex flex-wrap gap-3">
{paymentMethods.map((method, i) => (
<span key={i} className="px-4 py-2 bg-white/5 border border-white/10 rounded-full text-sm text-slate-300 font-medium">
{method}
</span>
))}
</div>
</section>
<hr className="border-white/10" />
{/* Redes Sociais */}
<section>
<h3 className="text-xl font-bold mb-4">Redes Sociais</h3>
<div className="flex gap-4">
{socials.whatsapp && (
<a href={socials.whatsapp} target="_blank" rel="noreferrer" className="w-12 h-12 flex items-center justify-center rounded-full bg-white/5 border border-white/10 text-slate-300 hover:bg-white/10 transition-colors">
<Smartphone size={22} />
</a>
)}
{socials.instagram && (
<a href={socials.instagram} target="_blank" rel="noreferrer" className="w-12 h-12 flex items-center justify-center rounded-full bg-white/5 border border-white/10 text-slate-300 hover:bg-white/10 transition-colors">
<Instagram size={22} />
</a>
)}
{socials.facebook && (
<a href={socials.facebook} target="_blank" rel="noreferrer" className="w-12 h-12 flex items-center justify-center rounded-full bg-white/5 border border-white/10 text-slate-300 hover:bg-white/10 transition-colors">
<Facebook size={22} />
</a>
)}
</div>
</section>
<hr className="border-white/10" />
{/* Contacto */}
<section>
<h3 className="text-xl font-bold mb-4">Contacto</h3>
<div className="flex flex-col gap-3">
{contacts.phone1 && (
<div className="flex items-center justify-between p-4 bg-white/5 border border-white/10 rounded-2xl">
<div className="flex items-center gap-4">
<div className="w-10 h-10 flex items-center justify-center bg-white/10 rounded-full text-slate-300">
<Smartphone size={20} />
</div>
<span className="text-slate-200 font-medium text-lg">{contacts.phone1}</span>
</div>
<button onClick={() => copyToClipboard(contacts.phone1!)} className="text-slate-400 hover:text-white transition-colors p-2">
<Copy size={20} />
</button>
</div>
)}
{contacts.phone2 && (
<div className="flex items-center justify-between p-4 bg-white/5 border border-white/10 rounded-2xl">
<div className="flex items-center gap-4">
<div className="w-10 h-10 flex items-center justify-center bg-white/10 rounded-full text-slate-300">
<Phone size={20} />
</div>
<span className="text-slate-200 font-medium text-lg">{contacts.phone2}</span>
</div>
<button onClick={() => copyToClipboard(contacts.phone2!)} className="text-slate-400 hover:text-white transition-colors p-2">
<Copy size={20} />
</button>
</div>
)}
</div>
</section>
</div>
);
};

View File

@@ -10,6 +10,7 @@ import { Tabs } from '../components/ui/tabs';
import { ServiceList } from '../components/ServiceList';
import { ProductList } from '../components/ProductList';
import { BarberList } from '../components/BarberList';
import { ShopDetailsTab } from '../components/ShopDetailsTab';
import { Button } from '../components/ui/button';
import { useApp } from '../context/AppContext';
import { Dialog } from '../components/ui/dialog';
@@ -19,7 +20,7 @@ export default function ShopDetails() {
const { id } = useParams<{ id: string }>();
const { shops, shopsReady, addToCart, toggleFavorite, isFavorite } = useApp();
const shop = useMemo(() => shops.find((s) => s.id === id), [shops, id]);
const [tab, setTab] = useState<'servicos' | 'produtos' | 'barbeiros'>('servicos');
const [tab, setTab] = useState<'servicos' | 'produtos' | 'barbeiros' | 'detalhes'>('servicos');
const [imageOpen, setImageOpen] = useState(false);
if (!shopsReady) {
@@ -95,6 +96,7 @@ export default function ShopDetails() {
{ id: 'servicos', label: 'Serviços' },
{ id: 'barbeiros', label: 'Barbeiros' },
{ id: 'produtos', label: 'Produtos' },
{ id: 'detalhes', label: 'Detalhes' },
]}
active={tab}
onChange={(v) => setTab(v as typeof tab)}
@@ -117,6 +119,10 @@ export default function ShopDetails() {
<div className="bg-white/30 backdrop-blur-md p-8 rounded-[3rem] border border-white/50">
<BarberList barbers={shop.barbers} />
</div>
) : tab === 'detalhes' ? (
<div className="bg-[#111] p-8 rounded-[3rem] border border-slate-800 shadow-2xl">
<ShopDetailsTab shop={shop} />
</div>
) : (
<ProductList products={shop.products} onAdd={(pid) => addToCart({ shopId: shop.id, type: 'product', refId: pid, qty: 1 })} />
)}

View File

@@ -1,7 +1,21 @@
export type Barber = { id: string; name: string; imageUrl?: string; specialties: string[]; schedule: { day: string; slots: string[] }[] };
export type Service = { id: string; name: string; price: number; duration: number; barberIds: string[] };
export type Product = { id: string; name: string; price: number; stock: number };
export type BarberShop = { id: string; name: string; address: string; rating: number; services: Service[]; products: Product[]; barbers: Barber[]; imageUrl?: string };
export type ShopSchedule = { day: string; open: string; close: string; closed?: boolean };
export type BarberShop = {
id: string;
name: string;
address: string;
rating: number;
services: Service[];
products: Product[];
barbers: Barber[];
imageUrl?: string;
schedule?: ShopSchedule[];
paymentMethods?: string[];
socialNetworks?: { whatsapp?: string; instagram?: string; facebook?: string };
contacts?: { phone1?: string; phone2?: string };
};
export type AppointmentStatus = 'pendente' | 'confirmado' | 'concluido' | 'cancelado';
export type OrderStatus = 'pendente' | 'confirmado' | 'concluido' | 'cancelado';
export type Appointment = { id: string; shopId: string; serviceId: string; barberId: string; customerId: string; date: string; status: AppointmentStatus; total: number };