feat: Add shop details tab displaying schedule, payment methods, social links, and contacts.
This commit is contained in:
131
web/src/components/ShopDetailsTab.tsx
Normal file
131
web/src/components/ShopDetailsTab.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@@ -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 })} />
|
||||
)}
|
||||
|
||||
@@ -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 };
|
||||
|
||||
Reference in New Issue
Block a user