- Adiciona tabela `disponibilidade_profissionais` no schema - Adiciona coluna `posicao` para mapa de profissionais no evento - Implementa Service e Handler para gerenciar Disponibilidade (Set/Get) - Implementa lógica de Escalonamento: listar disponíveis e atribuir posição - Atualiza rotas da API e serviço do frontend feat(frontend): implementa modal de detalhes e updates otimistas na equipe - Adiciona componente ProfessionalDetailsModal para exibir dados completos do profissional - Implementa update otimista em DataContext para adição/removação instantânea da equipe - Corrige bug crítico de sintaxe e estrutura na Dashboard.tsx - Adiciona badges de status (Pendente/Confirmado/Rejeitado) na listagem de equipe - Corrige erro de referência de variável (professionalId) no fluxo de atribuição
123 lines
6.3 KiB
TypeScript
123 lines
6.3 KiB
TypeScript
import React from 'react';
|
|
import { Professional } from '../types';
|
|
import { Button } from './Button';
|
|
import { X, Mail, Phone, MapPin, Building, Star, Camera, DollarSign, Award } from 'lucide-react';
|
|
|
|
interface ProfessionalDetailsModalProps {
|
|
professional: Professional;
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export const ProfessionalDetailsModal: React.FC<ProfessionalDetailsModalProps> = ({
|
|
professional,
|
|
isOpen,
|
|
onClose,
|
|
}) => {
|
|
if (!isOpen) return null;
|
|
|
|
return (
|
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4 animate-fadeIn">
|
|
<div className="bg-white rounded-2xl shadow-2xl max-w-2xl w-full overflow-hidden flex flex-col relative animate-slideIn">
|
|
|
|
{/* Header com Capa/Avatar Style */}
|
|
<div className="h-32 bg-gradient-to-r from-brand-purple to-brand-purple/80 relative">
|
|
<button
|
|
onClick={onClose}
|
|
className="absolute top-4 right-4 text-white hover:bg-white/20 p-2 rounded-full transition-colors"
|
|
>
|
|
<X size={24} />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Conteúdo Principal */}
|
|
<div className="px-8 pb-8 -mt-16 flex flex-col items-center sm:items-start relative z-10">
|
|
|
|
{/* Avatar Grande */}
|
|
<div
|
|
className="w-32 h-32 rounded-full border-4 border-white bg-white shadow-lg mb-4"
|
|
style={{
|
|
backgroundImage: `url(${professional.avatar || `https://ui-avatars.com/api/?name=${encodeURIComponent(professional.name)}&background=random`})`,
|
|
backgroundSize: 'cover',
|
|
backgroundPosition: 'center'
|
|
}}
|
|
/>
|
|
|
|
<div className="text-center sm:text-left w-full">
|
|
<h2 className="text-2xl font-serif font-bold text-brand-black">{professional.name}</h2>
|
|
<div className="flex flex-wrap justify-center sm:justify-start gap-2 mt-2">
|
|
<span className="px-3 py-1 bg-brand-gold/10 text-brand-black rounded-full text-sm font-medium border border-brand-gold/20">
|
|
{professional.role}
|
|
</span>
|
|
{/* Mock de Avaliação */}
|
|
<span className="px-3 py-1 bg-yellow-50 text-yellow-700 rounded-full text-sm font-medium border border-yellow-200 flex items-center gap-1">
|
|
<Star size={14} className="fill-yellow-500 text-yellow-500" /> 4.9
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="w-full grid grid-cols-1 md:grid-cols-2 gap-6 mt-8">
|
|
|
|
<div className="space-y-4">
|
|
<h3 className="font-bold text-gray-900 border-b pb-2 flex items-center gap-2">
|
|
<Building size={18} className="text-brand-gold" />
|
|
Dados Pessoais
|
|
</h3>
|
|
|
|
<div className="space-y-3 text-sm">
|
|
<div className="flex items-center gap-3 text-gray-700">
|
|
<Mail size={16} className="text-gray-400" />
|
|
<span>{professional.email}</span>
|
|
</div>
|
|
<div className="flex items-center gap-3 text-gray-700">
|
|
<Phone size={16} className="text-gray-400" />
|
|
<span>{professional.phone || "Não informado"}</span>
|
|
</div>
|
|
{/* Endereço Mockado se não tiver no tipo, ou usar campos extras do backend se mapeados */}
|
|
<div className="flex items-center gap-3 text-gray-700">
|
|
<MapPin size={16} className="text-gray-400" />
|
|
<span>São Paulo, SP</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<h3 className="font-bold text-gray-900 border-b pb-2 flex items-center gap-2">
|
|
<Camera size={18} className="text-brand-gold" />
|
|
Equipamentos & Habilidades
|
|
</h3>
|
|
|
|
{/* Mock de Habilidades / Equipamentos (pois não está no type Professional simples ainda) */}
|
|
<div className="text-sm text-gray-600 space-y-2">
|
|
<p>Equipamento Profissional: <span className="text-gray-900">Canon R6, Lentes série L</span></p>
|
|
<div className="flex flex-wrap gap-2 mt-2">
|
|
{["Formatura", "Casamento", "Estúdio"].map(tag => (
|
|
<span key={tag} className="text-xs bg-gray-100 px-2 py-1 rounded text-gray-600">{tag}</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="w-full mt-8 bg-gray-50 p-4 rounded-lg border border-gray-100">
|
|
<div className="flex items-start gap-3">
|
|
<Award className="text-brand-gold mt-1" size={20} />
|
|
<div>
|
|
<h4 className="font-bold text-gray-900 text-sm">Performance</h4>
|
|
<p className="text-sm text-gray-600 mt-1">Este profissional tem mantido uma taxa de 100% de presença e alta satisfação nos últimos eventos.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="w-full mt-8 flex justify-end">
|
|
<Button variant="outline" onClick={onClose}>
|
|
Fechar
|
|
</Button>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|