atualização
This commit is contained in:
parent
d087cefb1b
commit
2f7cdd08b8
6 changed files with 738 additions and 254 deletions
|
|
@ -2,6 +2,40 @@
|
|||
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
||||
import { EventData, EventStatus, EventType, Institution } from '../types';
|
||||
|
||||
interface Photographer {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
avatar: string;
|
||||
specialties: string[];
|
||||
rating: number;
|
||||
eventsCompleted: number;
|
||||
}
|
||||
|
||||
const MOCK_PHOTOGRAPHERS: Photographer[] = [
|
||||
{
|
||||
id: 'photographer-1',
|
||||
name: 'Ana Paula Silva',
|
||||
email: 'ana.silva@photum.com',
|
||||
phone: '(51) 99999-1234',
|
||||
avatar: 'https://i.pravatar.cc/150?img=5',
|
||||
specialties: ['Formaturas', 'Eventos Universitários', 'Colações de Grau'],
|
||||
rating: 4.9,
|
||||
eventsCompleted: 87
|
||||
},
|
||||
{
|
||||
id: 'photographer-2',
|
||||
name: 'Carlos Mendes',
|
||||
email: 'carlos.mendes@photum.com',
|
||||
phone: '(51) 99888-5678',
|
||||
avatar: 'https://i.pravatar.cc/150?img=12',
|
||||
specialties: ['Formaturas', 'Eventos Corporativos'],
|
||||
rating: 4.8,
|
||||
eventsCompleted: 65
|
||||
}
|
||||
];
|
||||
|
||||
// Initial Mock Data
|
||||
const INITIAL_INSTITUTIONS: Institution[] = [
|
||||
{
|
||||
|
|
@ -25,10 +59,10 @@ const INITIAL_INSTITUTIONS: Institution[] = [
|
|||
const INITIAL_EVENTS: EventData[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Casamento Juliana & Marcos',
|
||||
name: 'Formatura Medicina UFRGS',
|
||||
date: '2024-10-15',
|
||||
time: '16:00',
|
||||
type: EventType.WEDDING,
|
||||
time: '19:00',
|
||||
type: EventType.GRADUATION,
|
||||
status: EventStatus.CONFIRMED,
|
||||
address: {
|
||||
street: 'Av. das Hortênsias',
|
||||
|
|
@ -37,8 +71,8 @@ const INITIAL_EVENTS: EventData[] = [
|
|||
state: 'RS',
|
||||
zip: '95670-000'
|
||||
},
|
||||
briefing: 'Cerimônia ao pôr do sol. Foco em fotos espontâneas dos noivos e pais.',
|
||||
coverImage: 'https://picsum.photos/id/1059/800/400',
|
||||
briefing: 'Cerimônia de formatura no Teatro Guaíra. Foco em fotos da colação de grau, formandos e familiares.',
|
||||
coverImage: 'https://images.unsplash.com/photo-1523050854058-8df90110c9f1?w=800&h=400&fit=crop',
|
||||
contacts: [{ id: 'c1', name: 'Cerimonial Silva', role: 'Cerimonialista', phone: '9999-9999', email: 'c@teste.com'}],
|
||||
checklist: [],
|
||||
ownerId: 'client-1',
|
||||
|
|
@ -71,6 +105,7 @@ const INITIAL_EVENTS: EventData[] = [
|
|||
interface DataContextType {
|
||||
events: EventData[];
|
||||
institutions: Institution[];
|
||||
photographers: Photographer[];
|
||||
addEvent: (event: EventData) => void;
|
||||
updateEventStatus: (id: string, status: EventStatus) => void;
|
||||
assignPhotographer: (eventId: string, photographerId: string) => void;
|
||||
|
|
@ -79,6 +114,7 @@ interface DataContextType {
|
|||
updateInstitution: (id: string, institution: Partial<Institution>) => void;
|
||||
getInstitutionsByUserId: (userId: string) => Institution[];
|
||||
getInstitutionById: (id: string) => Institution | undefined;
|
||||
getPhotographerById: (id: string) => Photographer | undefined;
|
||||
}
|
||||
|
||||
const DataContext = createContext<DataContextType | undefined>(undefined);
|
||||
|
|
@ -86,6 +122,7 @@ const DataContext = createContext<DataContextType | undefined>(undefined);
|
|||
export const DataProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||
const [events, setEvents] = useState<EventData[]>(INITIAL_EVENTS);
|
||||
const [institutions, setInstitutions] = useState<Institution[]>(INITIAL_INSTITUTIONS);
|
||||
const [photographers] = useState<Photographer[]>(MOCK_PHOTOGRAPHERS);
|
||||
|
||||
const addEvent = (event: EventData) => {
|
||||
setEvents(prev => [event, ...prev]);
|
||||
|
|
@ -138,10 +175,15 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
|||
return institutions.find(inst => inst.id === id);
|
||||
};
|
||||
|
||||
const getPhotographerById = (id: string) => {
|
||||
return photographers.find(p => p.id === id);
|
||||
};
|
||||
|
||||
return (
|
||||
<DataContext.Provider value={{
|
||||
events,
|
||||
institutions,
|
||||
photographers,
|
||||
addEvent,
|
||||
updateEventStatus,
|
||||
assignPhotographer,
|
||||
|
|
@ -149,7 +191,8 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
|||
addInstitution,
|
||||
updateInstitution,
|
||||
getInstitutionsByUserId,
|
||||
getInstitutionById
|
||||
getInstitutionById,
|
||||
getPhotographerById
|
||||
}}>
|
||||
{children}
|
||||
</DataContext.Provider>
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ const MOCK_EVENTS: Event[] = [
|
|||
export const CalendarPage: React.FC = () => {
|
||||
const [selectedMonth, setSelectedMonth] = useState(new Date());
|
||||
const [selectedEvent, setSelectedEvent] = useState<Event | null>(null);
|
||||
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
||||
|
||||
const getStatusColor = (status: Event['status']) => {
|
||||
switch (status) {
|
||||
|
|
@ -145,6 +146,50 @@ export const CalendarPage: React.FC = () => {
|
|||
year: 'numeric'
|
||||
});
|
||||
|
||||
// Generate calendar days
|
||||
const generateCalendarDays = () => {
|
||||
const year = selectedMonth.getFullYear();
|
||||
const month = selectedMonth.getMonth();
|
||||
|
||||
const firstDay = new Date(year, month, 1);
|
||||
const lastDay = new Date(year, month + 1, 0);
|
||||
|
||||
const firstDayOfWeek = firstDay.getDay();
|
||||
const daysInMonth = lastDay.getDate();
|
||||
|
||||
const days: (Date | null)[] = [];
|
||||
|
||||
// Add empty cells for days before month starts
|
||||
for (let i = 0; i < firstDayOfWeek; i++) {
|
||||
days.push(null);
|
||||
}
|
||||
|
||||
// Add all days of the month
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
days.push(new Date(year, month, day));
|
||||
}
|
||||
|
||||
return days;
|
||||
};
|
||||
|
||||
const getEventsForDate = (date: Date) => {
|
||||
return MOCK_EVENTS.filter(event => {
|
||||
const eventDate = new Date(event.date + 'T00:00:00');
|
||||
return eventDate.getDate() === date.getDate() &&
|
||||
eventDate.getMonth() === date.getMonth() &&
|
||||
eventDate.getFullYear() === date.getFullYear();
|
||||
});
|
||||
};
|
||||
|
||||
const isToday = (date: Date) => {
|
||||
const today = new Date();
|
||||
return date.getDate() === today.getDate() &&
|
||||
date.getMonth() === today.getMonth() &&
|
||||
date.getFullYear() === today.getFullYear();
|
||||
};
|
||||
|
||||
const calendarDays = generateCalendarDays();
|
||||
|
||||
// Filter events for selected month
|
||||
const monthEvents = MOCK_EVENTS.filter(event => {
|
||||
const eventDate = new Date(event.date + 'T00:00:00');
|
||||
|
|
@ -158,121 +203,242 @@ export const CalendarPage: React.FC = () => {
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 pt-32 pb-12">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="min-h-screen bg-gray-50 pt-20 sm:pt-24 md:pt-32 pb-8 sm:pb-12">
|
||||
<div className="max-w-7xl mx-auto px-3 sm:px-4 md:px-6 lg:px-8">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-serif font-bold text-brand-black mb-2">
|
||||
<div className="mb-6 sm:mb-8">
|
||||
<h1 className="text-2xl sm:text-3xl font-serif font-bold text-brand-black mb-1 sm:mb-2">
|
||||
Minha Agenda
|
||||
</h1>
|
||||
<p className="text-gray-600">
|
||||
<p className="text-sm sm:text-base text-gray-600">
|
||||
Gerencie seus eventos e compromissos fotográficos
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* Calendar Navigation */}
|
||||
<div className="lg:col-span-1">
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6">
|
||||
{/* Full Calendar Grid */}
|
||||
<div className="lg:col-span-2">
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3 sm:p-4">
|
||||
<div className="flex items-center justify-between mb-3 sm:mb-4">
|
||||
<button
|
||||
onClick={prevMonth}
|
||||
className="p-2 hover:bg-gray-100 rounded-full transition-colors"
|
||||
className="p-1 sm:p-1.5 hover:bg-gray-100 rounded-full transition-colors"
|
||||
>
|
||||
<ChevronLeft size={20} />
|
||||
<ChevronLeft className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||
</button>
|
||||
<h2 className="text-lg font-semibold capitalize">
|
||||
<h2 className="text-base sm:text-lg font-bold capitalize" style={{ color: '#B9CF33' }}>
|
||||
{currentMonthName}
|
||||
</h2>
|
||||
<button
|
||||
onClick={nextMonth}
|
||||
className="p-2 hover:bg-gray-100 rounded-full transition-colors"
|
||||
className="p-1 sm:p-1.5 hover:bg-gray-100 rounded-full transition-colors"
|
||||
>
|
||||
<ChevronRight size={20} />
|
||||
<ChevronRight className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-600">Eventos este mês:</span>
|
||||
<span className="font-semibold text-brand-gold">{monthEvents.length}</span>
|
||||
{/* Calendar Grid */}
|
||||
<div className="mb-3 sm:mb-4">
|
||||
{/* Week Days Header */}
|
||||
<div className="grid grid-cols-7 gap-0.5 sm:gap-1 mb-1">
|
||||
{['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'].map((day) => (
|
||||
<div
|
||||
key={day}
|
||||
className="text-center text-[10px] sm:text-xs font-semibold text-gray-600 py-1"
|
||||
>
|
||||
{day}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-600">Total de eventos:</span>
|
||||
<span className="font-semibold">{MOCK_EVENTS.length}</span>
|
||||
|
||||
{/* Calendar Days */}
|
||||
<div className="grid grid-cols-7 gap-0.5 sm:gap-1">
|
||||
{calendarDays.map((date, index) => {
|
||||
if (!date) {
|
||||
return (
|
||||
<div
|
||||
key={`empty-${index}`}
|
||||
className="h-10 sm:h-12 bg-gray-50 rounded"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const dayEvents = getEventsForDate(date);
|
||||
const hasEvents = dayEvents.length > 0;
|
||||
const today = isToday(date);
|
||||
|
||||
return (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => setSelectedDate(date)}
|
||||
className={`
|
||||
h-10 sm:h-12 rounded p-0.5 sm:p-1 transition-all duration-200
|
||||
${today ? 'bg-[#B9CF33] text-white font-bold' : 'hover:bg-gray-100 active:bg-gray-200'}
|
||||
${hasEvents && !today ? 'bg-blue-50 border border-[#B9CF33] sm:border-2' : 'bg-white border border-gray-200'}
|
||||
relative group
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col items-center justify-center h-full">
|
||||
<span className={`text-[10px] sm:text-xs ${today ? 'text-white' : 'text-gray-700'}`}>
|
||||
{date.getDate()}
|
||||
</span>
|
||||
{hasEvents && (
|
||||
<div className="flex gap-0.5 mt-0.5">
|
||||
{dayEvents.slice(0, 3).map((event, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={`w-0.5 h-0.5 sm:w-1 sm:h-1 rounded-full ${
|
||||
event.status === 'confirmed' ? 'bg-green-500' :
|
||||
event.status === 'pending' ? 'bg-yellow-500' :
|
||||
'bg-gray-400'
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Tooltip on hover - hide on mobile */}
|
||||
{hasEvents && (
|
||||
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 hidden sm:group-hover:block z-10">
|
||||
<div className="bg-gray-900 text-white text-xs rounded py-1 px-2 whitespace-nowrap">
|
||||
{dayEvents.length} evento{dayEvents.length > 1 ? 's' : ''}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 pt-6 border-t border-gray-200">
|
||||
<h3 className="text-sm font-semibold mb-3">Legenda</h3>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<div className="w-3 h-3 rounded-full bg-green-500"></div>
|
||||
<span>Confirmado</span>
|
||||
<div className="mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-gray-200">
|
||||
<div className="grid grid-cols-2 gap-2 text-xs">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between">
|
||||
<span className="text-gray-600 text-[10px] sm:text-xs">Eventos este mês:</span>
|
||||
<span className="font-bold text-sm sm:text-base" style={{ color: '#B9CF33' }}>{monthEvents.length}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500"></div>
|
||||
<span>Pendente</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<div className="w-3 h-3 rounded-full bg-gray-500"></div>
|
||||
<span>Concluído</span>
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between">
|
||||
<span className="text-gray-600 text-[10px] sm:text-xs">Total:</span>
|
||||
<span className="font-semibold text-sm sm:text-base">{MOCK_EVENTS.length}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Legend and Info Sidebar */}
|
||||
<div className="lg:col-span-1">
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3 sm:p-4 lg:sticky lg:top-24">
|
||||
<h3 className="text-sm font-bold mb-2 sm:mb-3" style={{ color: '#492E61' }}>Legenda</h3>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-1 gap-2 mb-3 sm:mb-4">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<div className="w-3 h-3 rounded-full bg-green-500 flex-shrink-0"></div>
|
||||
<span>Confirmado</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500 flex-shrink-0"></div>
|
||||
<span>Pendente</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<div className="w-3 h-3 rounded-full bg-gray-500 flex-shrink-0"></div>
|
||||
<span>Concluído</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<div className="w-3 h-3 rounded flex-shrink-0" style={{ backgroundColor: '#B9CF33' }}></div>
|
||||
<span>Hoje</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs col-span-2 sm:col-span-1">
|
||||
<div className="w-3 h-3 rounded bg-blue-50 border-2 flex-shrink-0" style={{ borderColor: '#B9CF33' }}></div>
|
||||
<span>Com eventos</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedDate && (
|
||||
<div className="pt-3 sm:pt-4 border-t border-gray-200">
|
||||
<h3 className="text-xs font-bold mb-2" style={{ color: '#492E61' }}>
|
||||
{selectedDate.toLocaleDateString('pt-BR', {
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
})}
|
||||
</h3>
|
||||
{getEventsForDate(selectedDate).length > 0 ? (
|
||||
<div className="space-y-2">
|
||||
{getEventsForDate(selectedDate).map(event => (
|
||||
<div
|
||||
key={event.id}
|
||||
className="p-2 bg-gray-50 rounded cursor-pointer hover:bg-gray-100 active:bg-gray-200 transition-colors"
|
||||
onClick={() => setSelectedEvent(event)}
|
||||
>
|
||||
<div className="font-medium text-xs text-gray-900 mb-1 line-clamp-1">
|
||||
{event.title}
|
||||
</div>
|
||||
<div className="text-xs text-gray-600 flex items-center">
|
||||
<Clock size={10} className="inline mr-1 flex-shrink-0" />
|
||||
{event.time}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-xs text-gray-500">Nenhum evento neste dia</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Events List */}
|
||||
<div className="lg:col-span-2">
|
||||
<div className="lg:col-span-3 mt-4 sm:mt-6">
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div className="p-6 border-b border-gray-200">
|
||||
<h2 className="text-xl font-semibold">Próximos Eventos</h2>
|
||||
<div className="p-4 sm:p-6 border-b border-gray-200">
|
||||
<h2 className="text-lg sm:text-xl font-bold" style={{ color: '#492E61' }}>Próximos Eventos</h2>
|
||||
</div>
|
||||
|
||||
<div className="divide-y divide-gray-200">
|
||||
{sortedEvents.length === 0 ? (
|
||||
<div className="p-12 text-center text-gray-500">
|
||||
<Calendar size={48} className="mx-auto mb-4 text-gray-300" />
|
||||
<p>Nenhum evento agendado</p>
|
||||
<div className="p-8 sm:p-12 text-center text-gray-500">
|
||||
<Calendar className="w-10 h-10 sm:w-12 sm:h-12 mx-auto mb-3 sm:mb-4 text-gray-300" />
|
||||
<p className="text-sm sm:text-base">Nenhum evento agendado</p>
|
||||
</div>
|
||||
) : (
|
||||
sortedEvents.map((event) => (
|
||||
<div
|
||||
key={event.id}
|
||||
className="p-6 hover:bg-gray-50 transition-colors cursor-pointer"
|
||||
className="p-4 sm:p-6 hover:bg-gray-50 active:bg-gray-100 transition-colors cursor-pointer"
|
||||
onClick={() => setSelectedEvent(event)}
|
||||
>
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<h3 className="text-lg font-semibold text-brand-black">
|
||||
<div className="flex items-start justify-between mb-2 sm:mb-3 gap-3 sm:gap-4">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-start sm:items-center gap-2 mb-2 flex-col sm:flex-row">
|
||||
<h3 className="text-base sm:text-lg font-semibold text-brand-black line-clamp-2">
|
||||
{event.title}
|
||||
</h3>
|
||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${getTypeColor(event.type)}`}>
|
||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${getTypeColor(event.type)} whitespace-nowrap`}>
|
||||
{getTypeLabel(event.type)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center text-sm text-gray-600">
|
||||
<Calendar size={16} className="mr-2 text-brand-gold" />
|
||||
{formatDate(event.date)}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-1.5 sm:gap-2">
|
||||
<div className="flex items-center text-xs sm:text-sm text-gray-600">
|
||||
<Calendar className="w-3 h-3 sm:w-4 sm:h-4 mr-1.5 sm:mr-2 flex-shrink-0" style={{ color: '#B9CF33' }} />
|
||||
<span className="truncate">{formatDate(event.date)}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-gray-600">
|
||||
<Clock size={16} className="mr-2 text-brand-gold" />
|
||||
{event.time}
|
||||
<div className="flex items-center text-xs sm:text-sm text-gray-600">
|
||||
<Clock className="w-3 h-3 sm:w-4 sm:h-4 mr-1.5 sm:mr-2 flex-shrink-0" style={{ color: '#B9CF33' }} />
|
||||
<span>{event.time}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-gray-600">
|
||||
<MapPin size={16} className="mr-2 text-brand-gold" />
|
||||
{event.location}
|
||||
<div className="flex items-center text-xs sm:text-sm text-gray-600">
|
||||
<MapPin className="w-3 h-3 sm:w-4 sm:h-4 mr-1.5 sm:mr-2 flex-shrink-0" style={{ color: '#B9CF33' }} />
|
||||
<span className="truncate">{event.location}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-gray-600">
|
||||
<User size={16} className="mr-2 text-brand-gold" />
|
||||
{event.client}
|
||||
<div className="flex items-center text-xs sm:text-sm text-gray-600">
|
||||
<User className="w-3 h-3 sm:w-4 sm:h-4 mr-1.5 sm:mr-2 flex-shrink-0" style={{ color: '#B9CF33' }} />
|
||||
<span className="truncate">{event.client}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${getStatusColor(event.status)}`}>
|
||||
<span className={`px-2 sm:px-3 py-1 rounded-full text-[10px] sm:text-xs font-medium whitespace-nowrap ${getStatusColor(event.status)} flex-shrink-0`}>
|
||||
{getStatusLabel(event.status)}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -285,23 +451,31 @@ export const CalendarPage: React.FC = () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Event Detail Modal */}
|
||||
{/* Event Detail Modal - Improved & Centered */}
|
||||
{selectedEvent && (
|
||||
<div
|
||||
className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"
|
||||
className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4 animate-fadeIn"
|
||||
onClick={() => setSelectedEvent(null)}
|
||||
>
|
||||
<div
|
||||
className="bg-white rounded-lg max-w-2xl w-full p-8"
|
||||
className="bg-white rounded-2xl max-w-lg w-full shadow-2xl transform transition-all duration-300 animate-slideUp overflow-hidden"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-start justify-between mb-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-serif font-bold text-brand-black mb-2">
|
||||
{/* Header with gradient */}
|
||||
<div className="relative p-6 pb-8 bg-gradient-to-br from-[#B9CF33] to-[#a5bd2e]">
|
||||
<button
|
||||
onClick={() => setSelectedEvent(null)}
|
||||
className="absolute top-4 right-4 w-8 h-8 flex items-center justify-center rounded-full bg-white/20 hover:bg-white/30 text-white transition-colors"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
|
||||
<div className="text-white">
|
||||
<h2 className="text-xl sm:text-2xl font-bold mb-3 pr-8">
|
||||
{selectedEvent.title}
|
||||
</h2>
|
||||
<div className="flex gap-2">
|
||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${getTypeColor(selectedEvent.type)}`}>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<span className="px-3 py-1 rounded-full text-xs font-medium bg-white/90 text-gray-800">
|
||||
{getTypeLabel(selectedEvent.type)}
|
||||
</span>
|
||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${getStatusColor(selectedEvent.status)}`}>
|
||||
|
|
@ -309,49 +483,101 @@ export const CalendarPage: React.FC = () => {
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setSelectedEvent(null)}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center text-gray-700">
|
||||
<Calendar size={20} className="mr-3 text-brand-gold" />
|
||||
<span className="font-medium">{formatDate(selectedEvent.date)}</span>
|
||||
{/* Content */}
|
||||
<div className="p-6 space-y-4">
|
||||
{/* Date */}
|
||||
<div className="flex items-center gap-4 p-4 bg-gray-50 rounded-xl hover:bg-gray-100 transition-colors group">
|
||||
<div className="w-12 h-12 rounded-xl bg-[#B9CF33]/10 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<Calendar className="w-6 h-6" style={{ color: '#B9CF33' }} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-xs text-gray-500 mb-0.5">Data</p>
|
||||
<p className="font-semibold text-gray-900">{formatDate(selectedEvent.date)}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center text-gray-700">
|
||||
<Clock size={20} className="mr-3 text-brand-gold" />
|
||||
<span>{selectedEvent.time}</span>
|
||||
|
||||
{/* Time */}
|
||||
<div className="flex items-center gap-4 p-4 bg-gray-50 rounded-xl hover:bg-gray-100 transition-colors group">
|
||||
<div className="w-12 h-12 rounded-xl bg-[#B9CF33]/10 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<Clock className="w-6 h-6" style={{ color: '#B9CF33' }} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-xs text-gray-500 mb-0.5">Horário</p>
|
||||
<p className="font-semibold text-gray-900">{selectedEvent.time}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center text-gray-700">
|
||||
<MapPin size={20} className="mr-3 text-brand-gold" />
|
||||
<span>{selectedEvent.location}</span>
|
||||
|
||||
{/* Location */}
|
||||
<div className="flex items-center gap-4 p-4 bg-gray-50 rounded-xl hover:bg-gray-100 transition-colors group">
|
||||
<div className="w-12 h-12 rounded-xl bg-[#B9CF33]/10 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<MapPin className="w-6 h-6" style={{ color: '#B9CF33' }} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-xs text-gray-500 mb-0.5">Local</p>
|
||||
<p className="font-semibold text-gray-900">{selectedEvent.location}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center text-gray-700">
|
||||
<User size={20} className="mr-3 text-brand-gold" />
|
||||
<span>{selectedEvent.client}</span>
|
||||
|
||||
{/* Client */}
|
||||
<div className="flex items-center gap-4 p-4 bg-gray-50 rounded-xl hover:bg-gray-100 transition-colors group">
|
||||
<div className="w-12 h-12 rounded-xl bg-[#B9CF33]/10 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<User className="w-6 h-6" style={{ color: '#B9CF33' }} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-xs text-gray-500 mb-0.5">Cliente</p>
|
||||
<p className="font-semibold text-gray-900">{selectedEvent.client}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 pt-6 border-t border-gray-200 flex gap-3">
|
||||
{/* Actions */}
|
||||
<div className="p-6 pt-4 bg-gray-50 flex flex-col sm:flex-row gap-3">
|
||||
<button
|
||||
onClick={() => setSelectedEvent(null)}
|
||||
className="flex-1 px-6 py-3 bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200 transition-colors font-medium"
|
||||
className="flex-1 px-6 py-3 bg-white border-2 border-gray-200 text-gray-700 rounded-xl hover:bg-gray-50 hover:border-gray-300 active:scale-95 transition-all font-medium"
|
||||
>
|
||||
Fechar
|
||||
</button>
|
||||
<button
|
||||
className="flex-1 px-6 py-3 bg-brand-gold text-white rounded-md hover:bg-[#a5bd2e] transition-colors font-medium"
|
||||
className="flex-1 px-6 py-3 text-white rounded-xl transition-all font-medium shadow-lg hover:shadow-xl active:scale-95"
|
||||
style={{ backgroundColor: '#B9CF33' }}
|
||||
onMouseEnter={(e) => e.currentTarget.style.backgroundColor = '#a5bd2e'}
|
||||
onMouseLeave={(e) => e.currentTarget.style.backgroundColor = '#B9CF33'}
|
||||
>
|
||||
Ver Detalhes do Evento
|
||||
Ver Detalhes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<style>{`
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px) scale(0.95);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fadeIn {
|
||||
animation: fadeIn 0.2s ease-out;
|
||||
}
|
||||
|
||||
.animate-slideUp {
|
||||
animation: slideUp 0.3s ease-out;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { UserRole, EventData, EventStatus, EventType } from '../types';
|
|||
import { EventCard } from '../components/EventCard';
|
||||
import { EventForm } from '../components/EventForm';
|
||||
import { Button } from '../components/Button';
|
||||
import { PlusCircle, Search, CheckCircle, Clock, Edit, Users, Map, Building2 } from 'lucide-react';
|
||||
import { PlusCircle, Search, CheckCircle, Clock, Edit, Users, Map, Building2, Phone, Mail, MapPin, FileText, Camera, Star } from 'lucide-react';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useData } from '../contexts/DataContext';
|
||||
import { STATUS_COLORS } from '../constants';
|
||||
|
|
@ -15,7 +15,7 @@ interface DashboardProps {
|
|||
|
||||
export const Dashboard: React.FC<DashboardProps> = ({ initialView = 'list' }) => {
|
||||
const { user } = useAuth();
|
||||
const { events, getEventsByRole, addEvent, updateEventStatus, assignPhotographer, getInstitutionById } = useData();
|
||||
const { events, getEventsByRole, addEvent, updateEventStatus, assignPhotographer, addAttachment, getPhotographerById, getInstitutionById } = useData();
|
||||
const [view, setView] = useState<'list' | 'create' | 'edit' | 'details'>(initialView);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [selectedEvent, setSelectedEvent] = useState<EventData | null>(null);
|
||||
|
|
@ -244,11 +244,21 @@ export const Dashboard: React.FC<DashboardProps> = ({ initialView = 'list' }) =>
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className="bg-white border rounded-lg overflow-hidden shadow-sm">
|
||||
<div className="h-64 w-full relative">
|
||||
<img src={selectedEvent.coverImage} className="w-full h-full object-cover" alt="Cover" />
|
||||
<div className="absolute inset-0 bg-black/40 flex items-center justify-center">
|
||||
<h1 className="text-4xl font-serif text-white font-bold text-center px-4 drop-shadow-lg">{selectedEvent.name}</h1>
|
||||
<div className="bg-white rounded-2xl overflow-hidden shadow-2xl animate-slideUp">
|
||||
<div className="h-80 w-full relative group overflow-hidden">
|
||||
<img src={selectedEvent.coverImage} className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700" alt="Cover" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent flex items-center justify-center">
|
||||
<div className="text-center px-6">
|
||||
<h1 className="text-5xl font-serif text-white font-bold drop-shadow-2xl mb-3 animate-fadeIn">{selectedEvent.name}</h1>
|
||||
<div className="flex items-center justify-center gap-3 animate-fadeIn" style={{ animationDelay: '0.2s' }}>
|
||||
<span className="px-4 py-2 bg-white/90 backdrop-blur-sm rounded-full text-sm font-semibold text-gray-800 shadow-lg">
|
||||
{selectedEvent.type}
|
||||
</span>
|
||||
<span className="px-4 py-2 bg-[#B9CF33] backdrop-blur-sm rounded-full text-sm font-semibold text-white shadow-lg">
|
||||
{new Date(selectedEvent.date).toLocaleDateString('pt-BR', { day: '2-digit', month: 'short', year: 'numeric' })}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -279,35 +289,62 @@ export const Dashboard: React.FC<DashboardProps> = ({ initialView = 'list' }) =>
|
|||
const institution = getInstitutionById(selectedEvent.institutionId);
|
||||
if (institution) {
|
||||
return (
|
||||
<section className="bg-gradient-to-br from-brand-gold/10 to-transparent border border-brand-gold/30 rounded-sm p-6">
|
||||
<div className="flex items-start space-x-4">
|
||||
<div className="bg-brand-gold/20 p-3 rounded-full">
|
||||
<Building2 className="text-brand-gold" size={24} />
|
||||
<section className="relative bg-gradient-to-br from-[#B9CF33]/5 via-white to-[#C2388B]/5 border-2 border-gray-100 rounded-2xl p-8 hover:shadow-lg transition-all duration-300 group overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-[#B9CF33]/0 to-[#C2388B]/0 group-hover:from-[#B9CF33]/5 group-hover:to-[#C2388B]/5 transition-all duration-500" />
|
||||
<div className="relative flex items-start gap-5">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="w-20 h-20 bg-gradient-to-br from-[#B9CF33] to-[#a5bd2e] rounded-2xl flex items-center justify-center shadow-xl group-hover:scale-110 group-hover:rotate-6 transition-transform duration-300">
|
||||
<Building2 className="text-white" size={36} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-bold text-brand-black mb-1">{institution.name}</h3>
|
||||
<p className="text-sm text-brand-gold uppercase tracking-wide font-medium mb-3">{institution.type}</p>
|
||||
<h3 className="text-2xl font-bold text-brand-black mb-2 group-hover:text-[#492E61] transition-colors">{institution.name}</h3>
|
||||
<span className="inline-block px-4 py-1.5 bg-gradient-to-r from-[#B9CF33] to-[#a5bd2e] text-white text-xs uppercase tracking-wide font-bold rounded-full mb-5 shadow-md">
|
||||
{institution.type}
|
||||
</span>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 text-sm">
|
||||
<div>
|
||||
<p className="text-gray-500 text-xs uppercase tracking-wide">Contato</p>
|
||||
<p className="text-gray-700 font-medium">{institution.phone}</p>
|
||||
<p className="text-gray-600">{institution.email}</p>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-5">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-10 h-10 rounded-xl bg-[#B9CF33]/10 flex items-center justify-center flex-shrink-0">
|
||||
<Phone className="text-[#B9CF33]" size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 uppercase tracking-wide font-semibold mb-1">Contato</p>
|
||||
<p className="text-gray-800 font-semibold">{institution.phone}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-10 h-10 rounded-xl bg-[#B9CF33]/10 flex items-center justify-center flex-shrink-0">
|
||||
<Mail className="text-[#B9CF33]" size={20} />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-xs text-gray-500 uppercase tracking-wide font-semibold mb-1">Email</p>
|
||||
<p className="text-gray-800 font-medium truncate">{institution.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{institution.address && (
|
||||
<div>
|
||||
<p className="text-gray-500 text-xs uppercase tracking-wide">Endereço</p>
|
||||
<p className="text-gray-700">{institution.address.street}, {institution.address.number}</p>
|
||||
<p className="text-gray-600">{institution.address.city} - {institution.address.state}</p>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-10 h-10 rounded-xl bg-[#B9CF33]/10 flex items-center justify-center flex-shrink-0">
|
||||
<MapPin className="text-[#B9CF33]" size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 uppercase tracking-wide font-semibold mb-1">Endereço</p>
|
||||
<p className="text-gray-800 font-medium">{institution.address.street}, {institution.address.number}</p>
|
||||
<p className="text-gray-600">{institution.address.city} - {institution.address.state}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{institution.description && (
|
||||
<p className="text-gray-600 text-sm mt-3 italic border-t border-brand-gold/20 pt-3">
|
||||
{institution.description}
|
||||
</p>
|
||||
<div className="mt-5 pt-5 border-t border-gray-200">
|
||||
<p className="text-gray-600 text-sm leading-relaxed italic">
|
||||
{institution.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -317,20 +354,40 @@ export const Dashboard: React.FC<DashboardProps> = ({ initialView = 'list' }) =>
|
|||
return null;
|
||||
})()}
|
||||
|
||||
<section>
|
||||
<h3 className="text-lg font-bold border-b pb-2 mb-4 text-brand-black">Sobre o Evento</h3>
|
||||
<p className="text-gray-600 leading-relaxed whitespace-pre-wrap">{selectedEvent.briefing || "Sem briefing detalhado."}</p>
|
||||
<section className="bg-gradient-to-br from-gray-50 to-white rounded-2xl p-6 border border-gray-100 hover:shadow-lg transition-all duration-300">
|
||||
<div className="flex items-center gap-3 mb-5">
|
||||
<div className="w-10 h-10 rounded-xl bg-[#B9CF33]/10 flex items-center justify-center">
|
||||
<FileText className="text-[#B9CF33]" size={20} />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-brand-black">Sobre o Evento</h3>
|
||||
</div>
|
||||
<p className="text-gray-700 leading-relaxed whitespace-pre-wrap text-base">{selectedEvent.briefing || "Sem briefing detalhado."}</p>
|
||||
</section>
|
||||
|
||||
{selectedEvent.contacts.length > 0 && (
|
||||
<section>
|
||||
<h3 className="text-lg font-bold border-b pb-2 mb-4 text-brand-black">Contatos & Responsáveis</h3>
|
||||
<div className="flex items-center gap-3 mb-5">
|
||||
<div className="w-10 h-10 rounded-xl bg-[#B9CF33]/10 flex items-center justify-center">
|
||||
<Users className="text-[#B9CF33]" size={20} />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-brand-black">Contatos & Responsáveis</h3>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{selectedEvent.contacts.map((c, i) => (
|
||||
<div key={i} className="bg-gray-50 p-4 rounded-sm border border-gray-100">
|
||||
<p className="font-bold text-sm">{c.name}</p>
|
||||
<p className="text-xs text-brand-gold uppercase tracking-wide">{c.role}</p>
|
||||
<p className="text-sm text-gray-500 mt-1">{c.phone}</p>
|
||||
<div key={i} className="group bg-gradient-to-br from-white to-gray-50 p-5 rounded-xl border-2 border-gray-100 hover:border-[#B9CF33] hover:shadow-lg transition-all duration-300">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-[#492E61] to-[#C2388B] flex items-center justify-center text-white font-bold text-lg flex-shrink-0 group-hover:scale-110 transition-transform">
|
||||
{c.name.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-bold text-base text-gray-800 mb-1">{c.name}</p>
|
||||
<span className="inline-block px-3 py-1 bg-[#B9CF33]/10 text-[#B9CF33] text-xs uppercase tracking-wide font-bold rounded-full mb-2">{c.role}</span>
|
||||
<div className="flex items-center gap-2 text-gray-600 mt-2">
|
||||
<Phone size={14} className="text-[#B9CF33]" />
|
||||
<p className="text-sm font-medium">{c.phone}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -339,48 +396,96 @@ export const Dashboard: React.FC<DashboardProps> = ({ initialView = 'list' }) =>
|
|||
</div>
|
||||
|
||||
<div className="col-span-1 space-y-6">
|
||||
<div className={`p-6 rounded-sm border ${STATUS_COLORS[selectedEvent.status]} bg-opacity-10`}>
|
||||
<h4 className="font-bold uppercase tracking-widest text-xs mb-2 opacity-70">Status Atual</h4>
|
||||
<p className="text-xl font-serif font-bold">{selectedEvent.status}</p>
|
||||
<div className="relative overflow-hidden rounded-2xl p-6 bg-gradient-to-br from-white to-gray-50 border-2 border-gray-100 hover:shadow-xl transition-all duration-300 group">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-[#B9CF33]/0 to-[#B9CF33]/0 group-hover:from-[#B9CF33]/5 group-hover:to-[#B9CF33]/5 transition-all" />
|
||||
<div className="relative">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-[#B9CF33]/10 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<CheckCircle className="text-[#B9CF33]" size={20} />
|
||||
</div>
|
||||
<h4 className="font-bold uppercase tracking-widest text-xs text-gray-500">Status Atual</h4>
|
||||
</div>
|
||||
<span className={`inline-block px-4 py-2 rounded-xl text-sm font-bold ${STATUS_COLORS[selectedEvent.status]} shadow-sm`}>
|
||||
{selectedEvent.status}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border p-6 rounded-sm bg-gray-50">
|
||||
<h4 className="font-bold uppercase tracking-widest text-xs mb-4 text-gray-400">Localização</h4>
|
||||
<p className="font-medium text-lg">{selectedEvent.address.street}, {selectedEvent.address.number}</p>
|
||||
<p className="text-gray-500 mb-4">{selectedEvent.address.city} - {selectedEvent.address.state}</p>
|
||||
<div className="rounded-2xl bg-gradient-to-br from-white to-gray-50 p-6 border-2 border-gray-100 hover:shadow-xl transition-all duration-300 group">
|
||||
<div className="flex items-center gap-3 mb-5">
|
||||
<div className="w-10 h-10 rounded-xl bg-[#B9CF33]/10 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<MapPin className="text-[#B9CF33]" size={20} />
|
||||
</div>
|
||||
<h4 className="font-bold uppercase tracking-widest text-xs text-gray-500">Localização</h4>
|
||||
</div>
|
||||
<p className="font-bold text-base text-gray-800 mb-1">{selectedEvent.address.street}, {selectedEvent.address.number}</p>
|
||||
<p className="text-gray-600 mb-5 text-sm">{selectedEvent.address.city} - {selectedEvent.address.state}</p>
|
||||
|
||||
{selectedEvent.address.mapLink ? (
|
||||
<Button variant="secondary" size="sm" className="w-full" onClick={handleOpenMaps}>
|
||||
<Map size={16} className="mr-2"/> Abrir no Google Maps
|
||||
</Button>
|
||||
<button
|
||||
onClick={handleOpenMaps}
|
||||
className="w-full px-4 py-3 bg-gradient-to-r from-[#B9CF33] to-[#a5bd2e] text-white rounded-xl font-semibold hover:shadow-lg hover:scale-105 active:scale-95 transition-all flex items-center justify-center gap-2"
|
||||
>
|
||||
<Map size={18} /> Abrir no Google Maps
|
||||
</button>
|
||||
) : (
|
||||
<Button variant="outline" size="sm" className="w-full bg-white" onClick={handleOpenMaps}>
|
||||
<Map size={16} className="mr-2"/> Buscar no Maps
|
||||
</Button>
|
||||
<button
|
||||
onClick={handleOpenMaps}
|
||||
className="w-full px-4 py-3 bg-white border-2 border-gray-200 text-gray-700 rounded-xl font-semibold hover:border-[#B9CF33] hover:text-[#B9CF33] hover:shadow-lg transition-all flex items-center justify-center gap-2"
|
||||
>
|
||||
<Map size={18} /> Buscar no Maps
|
||||
</button>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
{(selectedEvent.photographerIds.length > 0 || user.role === UserRole.BUSINESS_OWNER) && (
|
||||
<div className="border p-6 rounded-sm">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h4 className="font-bold uppercase tracking-widest text-xs text-gray-400">Equipe Designada</h4>
|
||||
<div className="rounded-2xl bg-gradient-to-br from-white to-gray-50 p-6 border-2 border-gray-100 hover:shadow-xl transition-all duration-300 group">
|
||||
<div className="flex justify-between items-center mb-5">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-xl bg-[#B9CF33]/10 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<Camera className="text-[#B9CF33]" size={20} />
|
||||
</div>
|
||||
<h4 className="font-bold uppercase tracking-widest text-xs text-gray-500">Equipe Designada</h4>
|
||||
</div>
|
||||
{(user.role === UserRole.BUSINESS_OWNER || user.role === UserRole.SUPERADMIN) && (
|
||||
<button onClick={handleManageTeam} className="text-brand-gold hover:text-brand-black"><PlusCircle size={16}/></button>
|
||||
<button
|
||||
onClick={handleManageTeam}
|
||||
className="w-8 h-8 rounded-lg bg-[#B9CF33]/10 hover:bg-[#B9CF33] text-[#B9CF33] hover:text-white transition-all flex items-center justify-center hover:scale-110"
|
||||
>
|
||||
<PlusCircle size={18}/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{selectedEvent.photographerIds.length > 0 ? (
|
||||
<div className="flex -space-x-2">
|
||||
{selectedEvent.photographerIds.map((id, idx) => (
|
||||
<div key={id} className="w-10 h-10 rounded-full border-2 border-white bg-gray-300"
|
||||
style={{backgroundImage: `url(https://i.pravatar.cc/100?u=${id})`, backgroundSize: 'cover'}}
|
||||
title={id}
|
||||
></div>
|
||||
))}
|
||||
<div className="space-y-3">
|
||||
{selectedEvent.photographerIds.map((id) => {
|
||||
const photographer = getPhotographerById(id);
|
||||
return (
|
||||
<div
|
||||
key={id}
|
||||
className="flex items-center gap-3 p-3 bg-white rounded-xl border border-gray-200 hover:border-[#B9CF33] hover:shadow-md transition-all group/item"
|
||||
>
|
||||
<div className="relative flex-shrink-0">
|
||||
<img
|
||||
src={photographer?.avatar || `https://i.pravatar.cc/100?u=${id}`}
|
||||
alt={photographer?.name || id}
|
||||
className="w-12 h-12 rounded-xl object-cover ring-2 ring-gray-100 group-hover/item:ring-[#B9CF33] transition-all"
|
||||
/>
|
||||
<div className="absolute -bottom-1 -right-1 w-4 h-4 bg-green-500 rounded-full border-2 border-white" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-semibold text-gray-800 text-sm truncate group-hover/item:text-[#B9CF33] transition-colors">
|
||||
{photographer?.name || 'Fotógrafo'}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">Fotógrafo</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-gray-400 italic">Nenhum profissional atribuído.</p>
|
||||
<p className="text-sm text-gray-500 italic bg-gray-50 p-3 rounded-lg border border-dashed border-gray-200">Nenhum profissional atribuído</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -391,6 +496,32 @@ export const Dashboard: React.FC<DashboardProps> = ({ initialView = 'list' }) =>
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<style>{`
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.animate-slideUp {
|
||||
animation: slideUp 0.5s ease-out;
|
||||
}
|
||||
|
||||
.animate-fadeIn {
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -156,20 +156,20 @@ export const Home: React.FC<HomeProps> = ({ onEnter }) => {
|
|||
<p className="text-sm sm:text-base text-white/90">Envie sua mensagem, ligue ou faça uma visita em nossa empresa!</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 sm:gap-8 md:gap-12 items-start max-w-6xl mx-auto">
|
||||
{/* Left side - Contact Info */}
|
||||
<div className={`space-y-3 sm:space-y-4 md:space-y-5 text-white transition-all duration-700 delay-200 ${contactVisible ? 'opacity-100 translate-x-0' : 'opacity-0 -translate-x-20'}`}>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6 items-start max-w-7xl mx-auto">
|
||||
{/* Left side - Location Info */}
|
||||
<div className={`space-y-2.5 sm:space-y-3 text-white transition-all duration-700 delay-200 ${contactVisible ? 'opacity-100 translate-x-0' : 'opacity-0 -translate-x-20'}`}>
|
||||
<div className="group">
|
||||
<div className="flex items-start gap-2 sm:gap-3 p-2.5 sm:p-3 rounded-xl transition-all duration-300 hover:bg-white/10 hover:shadow-lg hover:-translate-y-1 cursor-pointer border border-white/5 hover:border-white/20">
|
||||
<div className="flex-shrink-0 w-10 h-10 rounded-full bg-white/20 flex items-center justify-center transition-transform duration-300 group-hover:scale-110">
|
||||
<svg className="w-6 h-6 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<div className="flex items-start gap-2 p-2 sm:p-2.5 rounded-lg transition-all duration-300 hover:bg-white/10 hover:shadow-lg cursor-pointer border border-white/5 hover:border-white/20">
|
||||
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-white/20 flex items-center justify-center transition-transform duration-300 group-hover:scale-110">
|
||||
<svg className="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-bold mb-0.5 sm:mb-1 text-sm sm:text-base">Rua Bom Recreio, 305</p>
|
||||
<p className="text-white/80 text-xs sm:text-sm">Jd. Boer II • Americana SP</p>
|
||||
<p className="text-white/80 text-xs sm:text-sm mb-1 sm:mb-2">CEP 13477-720</p>
|
||||
<p className="font-bold mb-0.5 text-sm">Rua Bom Recreio, 305</p>
|
||||
<p className="text-white/80 text-xs">Jd. Boer II • Americana SP</p>
|
||||
<p className="text-white/80 text-xs mb-1">CEP 13477-720</p>
|
||||
<a
|
||||
href="https://www.google.com/maps/place/Photum+Formaturas/@-22.7442887,-47.290221,21z/data=!4m12!1m5!3m4!2zMjLCsDQ0JzM5LjMiUyA0N8KwMTcnMjQuOCJX!8m2!3d-22.744247!4d-47.290221!3m5!1s0x94c89755cd9e70a9:0x15496eb4ec405483!8m2!3d-22.7442757!4d-47.2902662!16s%2Fg%2F11g6my_mm1?hl=pt&entry=ttu&g_ep=EgoyMDI1MTEyMy4xIKXMDSoASAFQAw%3D%3D"
|
||||
target="_blank"
|
||||
|
|
@ -183,7 +183,7 @@ export const Home: React.FC<HomeProps> = ({ onEnter }) => {
|
|||
</div>
|
||||
|
||||
{/* Mini Map */}
|
||||
<div className="rounded-xl overflow-hidden shadow-lg border border-white/10 h-40 sm:h-48">
|
||||
<div className="rounded-lg overflow-hidden shadow-lg border border-white/10 h-32 sm:h-36">
|
||||
<iframe
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d235.28736842715582!2d-47.29022099999999!3d-22.7442887!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x94c89755cd9e70a9%3A0x15496eb4ec405483!2sPhotum%20Formaturas!5e0!3m2!1spt-BR!2sbr!4v1701234567890!5m2!1spt-BR!2sbr"
|
||||
width="100%"
|
||||
|
|
@ -196,28 +196,28 @@ export const Home: React.FC<HomeProps> = ({ onEnter }) => {
|
|||
</div>
|
||||
|
||||
<div className="group">
|
||||
<div className="flex items-start gap-3 p-3 rounded-xl transition-all duration-300 hover:bg-white/10 hover:shadow-lg hover:-translate-y-1 cursor-pointer border border-white/5 hover:border-white/20">
|
||||
<div className="flex-shrink-0 w-10 h-10 rounded-full bg-white/20 flex items-center justify-center transition-transform duration-300 group-hover:scale-110">
|
||||
<svg className="w-6 h-6 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<div className="flex items-center gap-2 p-2 sm:p-2.5 rounded-lg transition-all duration-300 hover:bg-white/10 hover:shadow-lg cursor-pointer border border-white/5 hover:border-white/20">
|
||||
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-white/20 flex items-center justify-center transition-transform duration-300 group-hover:scale-110">
|
||||
<svg className="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M20 15.5c-1.25 0-2.45-.2-3.57-.57-.35-.11-.74-.03-1.02.24l-2.2 2.2c-2.83-1.44-5.15-3.75-6.59-6.59l2.2-2.21c.28-.26.36-.65.25-1C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM19 12h2c0-4.97-4.03-9-9-9v2c3.87 0 7 3.13 7 7zm-4 0h2c0-2.76-2.24-5-5-5v2c1.66 0 3 1.34 3 3z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-semibold">(19) 3405 5024</p>
|
||||
<p className="font-semibold">(19) 3621 4621</p>
|
||||
<p className="font-semibold text-sm">(19) 3405 5024</p>
|
||||
<p className="font-semibold text-sm">(19) 3621 4621</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="group">
|
||||
<div className="flex items-start gap-2 sm:gap-3 p-2.5 sm:p-3 rounded-xl transition-all duration-300 hover:bg-white/10 hover:shadow-lg hover:-translate-y-1 cursor-pointer border border-white/5 hover:border-white/20">
|
||||
<div className="flex-shrink-0 w-8 h-8 sm:w-10 sm:h-10 rounded-full bg-white/20 flex items-center justify-center transition-transform duration-300 group-hover:scale-110">
|
||||
<svg className="w-5 h-5 sm:w-6 sm:h-6 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<div className="flex items-center gap-2 p-2 sm:p-2.5 rounded-lg transition-all duration-300 hover:bg-white/10 hover:shadow-lg cursor-pointer border border-white/5 hover:border-white/20">
|
||||
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-white/20 flex items-center justify-center transition-transform duration-300 group-hover:scale-110">
|
||||
<svg className="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<a href="mailto:contato@photum.com.br" className="font-semibold hover:text-white transition-colors text-sm sm:text-base break-all">
|
||||
<a href="mailto:contato@photum.com.br" className="font-semibold hover:text-white transition-colors text-sm break-all">
|
||||
contato@photum.com.br
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -225,39 +225,80 @@ export const Home: React.FC<HomeProps> = ({ onEnter }) => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right side - Contact Form */}
|
||||
<div className={`space-y-3 sm:space-y-4 md:space-y-5 bg-gradient-to-br from-white/10 to-white/5 p-4 sm:p-5 md:p-6 rounded-2xl backdrop-blur-md border border-white/10 shadow-2xl transition-all duration-700 delay-300 ${contactVisible ? 'opacity-100 translate-x-0' : 'opacity-0 translate-x-20'}`}>
|
||||
{/* Center - Contact Form */}
|
||||
<div className={`space-y-2.5 bg-gradient-to-br from-white/10 to-white/5 p-4 sm:p-5 rounded-xl backdrop-blur-md border border-white/10 shadow-2xl transition-all duration-700 delay-250 ${contactVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-10'}`}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Nome"
|
||||
className="w-full px-3 sm:px-4 py-2.5 sm:py-3 text-sm sm:text-base bg-white/5 border-b-2 border-white/20 text-white placeholder-white/50 focus:border-white focus:bg-white/10 focus:outline-none transition-all duration-300 rounded-t-lg"
|
||||
className="w-full px-3 py-2 text-sm bg-white/5 border-b-2 border-white/20 text-white placeholder-white/50 focus:border-white focus:bg-white/10 focus:outline-none transition-all duration-300 rounded-t-lg"
|
||||
/>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="E-mail"
|
||||
className="w-full px-3 sm:px-4 py-2.5 sm:py-3 text-sm sm:text-base bg-white/5 border-b-2 border-white/20 text-white placeholder-white/50 focus:border-white focus:bg-white/10 focus:outline-none transition-all duration-300 rounded-t-lg"
|
||||
className="w-full px-3 py-2 text-sm bg-white/5 border-b-2 border-white/20 text-white placeholder-white/50 focus:border-white focus:bg-white/10 focus:outline-none transition-all duration-300 rounded-t-lg"
|
||||
/>
|
||||
<input
|
||||
type="tel"
|
||||
placeholder="Telefone"
|
||||
className="w-full px-3 sm:px-4 py-2.5 sm:py-3 text-sm sm:text-base bg-white/5 border-b-2 border-white/20 text-white placeholder-white/50 focus:border-white focus:bg-white/10 focus:outline-none transition-all duration-300 rounded-t-lg"
|
||||
className="w-full px-3 py-2 text-sm bg-white/5 border-b-2 border-white/20 text-white placeholder-white/50 focus:border-white focus:bg-white/10 focus:outline-none transition-all duration-300 rounded-t-lg"
|
||||
/>
|
||||
<textarea
|
||||
placeholder="Mensagem"
|
||||
rows={4}
|
||||
className="w-full px-3 sm:px-4 py-2.5 sm:py-3 text-sm sm:text-base bg-white/5 border-b-2 border-white/20 text-white placeholder-white/50 focus:border-white focus:bg-white/10 focus:outline-none transition-all duration-300 resize-none rounded-t-lg"
|
||||
rows={3}
|
||||
className="w-full px-3 py-2 text-sm bg-white/5 border-b-2 border-white/20 text-white placeholder-white/50 focus:border-white focus:bg-white/10 focus:outline-none transition-all duration-300 resize-none rounded-t-lg"
|
||||
></textarea>
|
||||
<button
|
||||
className="w-full px-6 sm:px-8 md:px-10 py-2.5 sm:py-3 text-sm sm:text-base text-white font-bold rounded-xl hover:opacity-90 transition-all duration-300 transform hover:scale-105 hover:shadow-2xl active:scale-95 relative overflow-hidden group flex items-center justify-center gap-2"
|
||||
className="w-full px-6 py-2.5 text-sm text-white font-bold rounded-lg hover:opacity-90 transition-all duration-300 transform hover:scale-105 hover:shadow-xl active:scale-95 relative overflow-hidden group flex items-center justify-center gap-2"
|
||||
style={{ backgroundColor: '#B9CF32' }}
|
||||
>
|
||||
<span className="relative z-10">Enviar</span>
|
||||
<svg className="w-4 h-4 sm:w-5 sm:h-5 relative z-10 rotate-45" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg className="w-4 h-4 relative z-10 rotate-45" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
|
||||
</svg>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-700"></div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Right side - WhatsApp Card */}
|
||||
<div className={`transition-all duration-700 delay-300 ${contactVisible ? 'opacity-100 translate-x-0' : 'opacity-0 translate-x-20'}`}>
|
||||
<div className="bg-gradient-to-br from-[#25D366]/20 to-[#128C7E]/20 p-5 sm:p-6 rounded-xl backdrop-blur-md border border-[#25D366]/30 shadow-2xl hover:shadow-[#25D366]/20 hover:-translate-y-1 transition-all duration-300">
|
||||
<div className="flex flex-col items-center text-center space-y-3">
|
||||
{/* WhatsApp Icon */}
|
||||
<div className="w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-[#25D366] flex items-center justify-center shadow-lg transform hover:scale-110 transition-transform duration-300">
|
||||
<svg className="w-10 h-10 sm:w-12 sm:h-12 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Text */}
|
||||
<div className="space-y-1">
|
||||
<h3 className="text-lg sm:text-xl font-bold text-white">Tire suas dúvidas</h3>
|
||||
<p className="text-xs sm:text-sm text-white/90 leading-snug">
|
||||
Faça orçamento direto no nosso WhatsApp
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* WhatsApp Button */}
|
||||
<a
|
||||
href="https://wa.me/5519999999999"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-full bg-[#25D366] hover:bg-[#128C7E] text-white font-bold py-2.5 px-4 rounded-lg transition-all duration-300 transform hover:scale-105 hover:shadow-xl active:scale-95 flex items-center justify-center gap-2 group text-sm"
|
||||
>
|
||||
<svg className="w-5 h-5 transform group-hover:rotate-12 transition-transform duration-300" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>
|
||||
</svg>
|
||||
<span>Falar no WhatsApp</span>
|
||||
</a>
|
||||
|
||||
{/* Additional Info */}
|
||||
<div className="flex items-center gap-1.5 text-xs text-white/80">
|
||||
<div className="w-2 h-2 bg-[#25D366] rounded-full animate-pulse"></div>
|
||||
<span>Resposta rápida garantida</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ export const Login: React.FC<LoginProps> = ({ onNavigate }) => {
|
|||
type="submit"
|
||||
disabled={isLoading}
|
||||
className="w-full px-6 sm:px-10 py-3 sm:py-4 text-white font-bold text-base sm:text-lg rounded-lg transition-all duration-300 transform hover:scale-[1.02] hover:shadow-xl active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
style={{backgroundColor: '#1a1a1a'}}
|
||||
style={{backgroundColor: '#4E345F'}}
|
||||
>
|
||||
{isLoading ? 'Entrando...' : 'Entrar no Sistema'}
|
||||
</button>
|
||||
|
|
|
|||
179
pages/Team.tsx
179
pages/Team.tsx
|
|
@ -36,7 +36,7 @@ const MOCK_PHOTOGRAPHERS: Photographer[] = [
|
|||
email: 'ana.mendes@photum.com',
|
||||
phone: '(41) 99999-2222',
|
||||
location: 'Curitiba, PR',
|
||||
specialties: ['Casamentos', 'Formaturas'],
|
||||
specialties: ['Formaturas', 'Ensaios'],
|
||||
rating: 4.9,
|
||||
eventsCompleted: 62,
|
||||
status: 'busy',
|
||||
|
|
@ -49,7 +49,7 @@ const MOCK_PHOTOGRAPHERS: Photographer[] = [
|
|||
email: 'roberto.costa@photum.com',
|
||||
phone: '(41) 99999-3333',
|
||||
location: 'São José dos Pinhais, PR',
|
||||
specialties: ['Formaturas', 'Eventos Sociais'],
|
||||
specialties: ['Formaturas', 'Eventos Universitários'],
|
||||
rating: 4.7,
|
||||
eventsCompleted: 38,
|
||||
status: 'active',
|
||||
|
|
@ -62,7 +62,7 @@ const MOCK_PHOTOGRAPHERS: Photographer[] = [
|
|||
email: 'juliana.santos@photum.com',
|
||||
phone: '(41) 99999-4444',
|
||||
location: 'Curitiba, PR',
|
||||
specialties: ['Casamentos', 'Ensaios'],
|
||||
specialties: ['Formaturas', 'Ensaios', 'Eventos Acadêmicos'],
|
||||
rating: 5.0,
|
||||
eventsCompleted: 71,
|
||||
status: 'active',
|
||||
|
|
@ -75,7 +75,7 @@ const MOCK_PHOTOGRAPHERS: Photographer[] = [
|
|||
email: 'fernando.oliveira@photum.com',
|
||||
phone: '(41) 99999-5555',
|
||||
location: 'Pinhais, PR',
|
||||
specialties: ['Eventos Corporativos', 'Formaturas'],
|
||||
specialties: ['Formaturas', 'Eventos Corporativos'],
|
||||
rating: 4.6,
|
||||
eventsCompleted: 29,
|
||||
status: 'inactive',
|
||||
|
|
@ -88,7 +88,7 @@ const MOCK_PHOTOGRAPHERS: Photographer[] = [
|
|||
email: 'mariana.rodrigues@photum.com',
|
||||
phone: '(41) 99999-6666',
|
||||
location: 'Curitiba, PR',
|
||||
specialties: ['Formaturas', 'Eventos Sociais', 'Casamentos'],
|
||||
specialties: ['Formaturas', 'Eventos Universitários', 'Colações de Grau'],
|
||||
rating: 4.9,
|
||||
eventsCompleted: 54,
|
||||
status: 'busy',
|
||||
|
|
@ -167,54 +167,69 @@ export const TeamPage: React.FC = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 pt-32 pb-12">
|
||||
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-white pt-32 pb-12">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-serif font-bold text-brand-black mb-2">
|
||||
Equipe & Fotógrafos
|
||||
</h1>
|
||||
<p className="text-gray-600">
|
||||
Gerencie sua equipe de fotógrafos profissionais
|
||||
</p>
|
||||
<div className="mb-8 relative">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-[#B9CF33]/10 to-[#C2388B]/10 rounded-2xl blur-3xl -z-10" />
|
||||
<div className="text-center sm:text-left">
|
||||
<h1 className="text-4xl font-serif font-bold text-brand-black mb-3 bg-gradient-to-r from-[#492E61] to-[#C2388B] bg-clip-text text-transparent">
|
||||
Fotógrafos Designados
|
||||
</h1>
|
||||
<p className="text-lg text-gray-600">
|
||||
Gerencie sua equipe especializada em formaturas
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="group bg-white rounded-2xl shadow-sm border border-gray-200 p-6 hover:shadow-xl hover:scale-105 transition-all duration-300 cursor-pointer relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-[#B9CF33]/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
<div className="relative flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-gray-600 mb-1">Total de Fotógrafos</p>
|
||||
<p className="text-3xl font-bold text-brand-black">{stats.total}</p>
|
||||
<p className="text-sm text-gray-600 mb-1 font-medium">Total de Fotógrafos</p>
|
||||
<p className="text-4xl font-bold text-brand-black">{stats.total}</p>
|
||||
</div>
|
||||
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-[#B9CF33] to-[#a5bd2e] flex items-center justify-center shadow-lg group-hover:rotate-12 transition-transform">
|
||||
<Users className="text-white" size={32} />
|
||||
</div>
|
||||
<Users className="text-brand-gold" size={32} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="group bg-white rounded-2xl shadow-sm border border-gray-200 p-6 hover:shadow-xl hover:scale-105 transition-all duration-300 cursor-pointer relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-green-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
<div className="relative flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-gray-600 mb-1">Disponíveis</p>
|
||||
<p className="text-3xl font-bold text-green-600">{stats.active}</p>
|
||||
<p className="text-sm text-gray-600 mb-1 font-medium">Disponíveis</p>
|
||||
<p className="text-4xl font-bold text-green-600">{stats.active}</p>
|
||||
</div>
|
||||
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-green-500 to-green-600 flex items-center justify-center shadow-lg group-hover:rotate-12 transition-transform">
|
||||
<Camera className="text-white" size={32} />
|
||||
</div>
|
||||
<Camera className="text-green-600" size={32} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="group bg-white rounded-2xl shadow-sm border border-gray-200 p-6 hover:shadow-xl hover:scale-105 transition-all duration-300 cursor-pointer relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-yellow-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
<div className="relative flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-gray-600 mb-1">Em Evento</p>
|
||||
<p className="text-3xl font-bold text-yellow-600">{stats.busy}</p>
|
||||
<p className="text-sm text-gray-600 mb-1 font-medium">Em Formatura</p>
|
||||
<p className="text-4xl font-bold text-yellow-600">{stats.busy}</p>
|
||||
</div>
|
||||
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-yellow-500 to-yellow-600 flex items-center justify-center shadow-lg group-hover:rotate-12 transition-transform">
|
||||
<Camera className="text-white" size={32} />
|
||||
</div>
|
||||
<Camera className="text-yellow-600" size={32} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="group bg-white rounded-2xl shadow-sm border border-gray-200 p-6 hover:shadow-xl hover:scale-105 transition-all duration-300 cursor-pointer relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-[#C2388B]/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
<div className="relative flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-gray-600 mb-1">Avaliação Média</p>
|
||||
<p className="text-3xl font-bold text-brand-gold">{stats.avgRating}</p>
|
||||
<p className="text-sm text-gray-600 mb-1 font-medium">Avaliação Média</p>
|
||||
<p className="text-4xl font-bold bg-gradient-to-r from-[#B9CF33] to-[#C2388B] bg-clip-text text-transparent">{stats.avgRating}</p>
|
||||
</div>
|
||||
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-[#B9CF33] to-[#C2388B] flex items-center justify-center shadow-lg group-hover:rotate-12 transition-transform">
|
||||
<Star className="text-white" size={32} fill="white" />
|
||||
</div>
|
||||
<Star className="text-brand-gold" size={32} fill="#B9CF33" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -270,58 +285,86 @@ export const TeamPage: React.FC = () => {
|
|||
|
||||
{/* Photographers Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{filteredPhotographers.map((photographer) => (
|
||||
{filteredPhotographers.map((photographer, index) => (
|
||||
<div
|
||||
key={photographer.id}
|
||||
className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden hover:shadow-md transition-shadow cursor-pointer"
|
||||
className="group bg-white rounded-2xl shadow-sm border border-gray-200 overflow-hidden hover:shadow-2xl hover:-translate-y-2 transition-all duration-300 cursor-pointer relative"
|
||||
onClick={() => setSelectedPhotographer(photographer)}
|
||||
style={{ animationDelay: `${index * 100}ms` }}
|
||||
>
|
||||
<div className="p-6">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
{/* Gradient Overlay */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-[#B9CF33]/0 to-[#C2388B]/0 group-hover:from-[#B9CF33]/5 group-hover:to-[#C2388B]/5 transition-all duration-300" />
|
||||
|
||||
{/* Status Badge - Floating */}
|
||||
<div className="absolute top-4 right-4 z-10">
|
||||
<span className={`px-3 py-1.5 rounded-full text-xs font-semibold shadow-lg backdrop-blur-sm ${getStatusColor(photographer.status)}`}>
|
||||
{getStatusLabel(photographer.status)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="p-6 relative z-10">
|
||||
<div className="flex items-center gap-4 mb-5">
|
||||
<div className="relative">
|
||||
<img
|
||||
src={photographer.avatar}
|
||||
alt={photographer.name}
|
||||
className="w-16 h-16 rounded-full object-cover"
|
||||
className="w-20 h-20 rounded-2xl object-cover ring-4 ring-white group-hover:ring-[#B9CF33]/30 transition-all duration-300 shadow-md"
|
||||
/>
|
||||
<div>
|
||||
<h3 className="font-semibold text-lg text-brand-black">{photographer.name}</h3>
|
||||
<div className="flex items-center gap-1 mt-1">
|
||||
<Star size={14} fill="#B9CF33" className="text-brand-gold" />
|
||||
<span className="text-sm font-medium">{photographer.rating}</span>
|
||||
<span className="text-xs text-gray-500">({photographer.eventsCompleted} eventos)</span>
|
||||
{/* Online indicator */}
|
||||
{photographer.status === 'active' && (
|
||||
<div className="absolute -bottom-1 -right-1 w-6 h-6 bg-green-500 rounded-full border-4 border-white shadow-lg animate-pulse" />
|
||||
)}
|
||||
{photographer.status === 'busy' && (
|
||||
<div className="absolute -bottom-1 -right-1 w-6 h-6 bg-yellow-500 rounded-full border-4 border-white shadow-lg" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="font-bold text-lg text-brand-black mb-1 group-hover:text-[#492E61] transition-colors">{photographer.name}</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-1 px-2 py-1 bg-gradient-to-r from-[#B9CF33] to-[#a5bd2e] rounded-lg shadow-sm">
|
||||
<Star size={14} fill="white" className="text-white" />
|
||||
<span className="text-sm font-bold text-white">{photographer.rating}</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500 font-medium">
|
||||
{photographer.eventsCompleted} formaturas
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${getStatusColor(photographer.status)}`}>
|
||||
{getStatusLabel(photographer.status)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 mb-4">
|
||||
<div className="flex items-center text-sm text-gray-600">
|
||||
<Mail size={16} className="mr-2 text-brand-gold" />
|
||||
{photographer.email}
|
||||
<div className="space-y-3 mb-5">
|
||||
<div className="flex items-center text-sm text-gray-600 group/item hover:text-[#B9CF33] transition-colors">
|
||||
<div className="w-8 h-8 rounded-lg bg-gray-50 flex items-center justify-center mr-3 group-hover/item:bg-[#B9CF33]/10 transition-colors">
|
||||
<Mail size={16} className="text-[#B9CF33]" />
|
||||
</div>
|
||||
<span className="truncate font-medium">{photographer.email}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-gray-600">
|
||||
<Phone size={16} className="mr-2 text-brand-gold" />
|
||||
{photographer.phone}
|
||||
<div className="flex items-center text-sm text-gray-600 group/item hover:text-[#B9CF33] transition-colors">
|
||||
<div className="w-8 h-8 rounded-lg bg-gray-50 flex items-center justify-center mr-3 group-hover/item:bg-[#B9CF33]/10 transition-colors">
|
||||
<Phone size={16} className="text-[#B9CF33]" />
|
||||
</div>
|
||||
<span className="font-medium">{photographer.phone}</span>
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-gray-600">
|
||||
<MapPin size={16} className="mr-2 text-brand-gold" />
|
||||
{photographer.location}
|
||||
<div className="flex items-center text-sm text-gray-600 group/item hover:text-[#B9CF33] transition-colors">
|
||||
<div className="w-8 h-8 rounded-lg bg-gray-50 flex items-center justify-center mr-3 group-hover/item:bg-[#B9CF33]/10 transition-colors">
|
||||
<MapPin size={16} className="text-[#B9CF33]" />
|
||||
</div>
|
||||
<span className="font-medium">{photographer.location}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{photographer.specialties.map((specialty, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="px-2 py-1 bg-gray-100 text-gray-700 rounded-full text-xs font-medium"
|
||||
>
|
||||
{specialty}
|
||||
</span>
|
||||
))}
|
||||
<div className="pt-4 border-t border-gray-100">
|
||||
<p className="text-xs text-gray-500 mb-2 font-medium">ESPECIALIDADES</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{photographer.specialties.map((specialty, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="px-3 py-1.5 bg-gradient-to-r from-gray-50 to-gray-100 text-gray-700 rounded-lg text-xs font-semibold border border-gray-200 hover:border-[#B9CF33] hover:bg-[#B9CF33]/5 transition-all"
|
||||
>
|
||||
{specialty}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -494,7 +537,7 @@ export const TeamPage: React.FC = () => {
|
|||
Especialidades
|
||||
</label>
|
||||
<div className="space-y-2">
|
||||
{['Formaturas', 'Casamentos', 'Eventos Corporativos', 'Eventos Sociais', 'Ensaios'].map((specialty) => (
|
||||
{['Formaturas', 'Eventos Universitários', 'Eventos Corporativos', 'Colações de Grau', 'Ensaios', 'Eventos Acadêmicos'].map((specialty) => (
|
||||
<label key={specialty} className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
|
|
|
|||
Loading…
Reference in a new issue