feat: Melhorias na tabela de eventos e formulários
- Adiciona coluna de Cidade com ordenação independente na tabela de eventos - Implementa ordenação clicável em todas as colunas (nome, tipo, data, horário, cidade, local, status) - Remove dependência de estado para filtro de cidade - Adiciona exibição de curso/turma no modal de detalhes do evento - Torna campo curso/turma obrigatório no formulário de solicitação de evento - Remove campo de imagem de capa do formulário - Corrige importação de getActiveCoursesByInstitutionId no Dashboard
This commit is contained in:
parent
de5ceea1f3
commit
8016a0298e
5 changed files with 362 additions and 157 deletions
|
|
@ -84,7 +84,7 @@ export const EventFiltersBar: React.FC<EventFiltersBarProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={filters.state}
|
value={filters.state}
|
||||||
onChange={(e) => onFilterChange({ ...filters, state: e.target.value, city: '' })}
|
onChange={(e) => onFilterChange({ ...filters, state: e.target.value })}
|
||||||
className="px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:border-brand-gold transition-colors bg-white"
|
className="px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:border-brand-gold transition-colors bg-white"
|
||||||
>
|
>
|
||||||
<option value="">Todos os estados</option>
|
<option value="">Todos os estados</option>
|
||||||
|
|
@ -105,8 +105,7 @@ export const EventFiltersBar: React.FC<EventFiltersBarProps> = ({
|
||||||
<select
|
<select
|
||||||
value={filters.city}
|
value={filters.city}
|
||||||
onChange={(e) => onFilterChange({ ...filters, city: e.target.value })}
|
onChange={(e) => onFilterChange({ ...filters, city: e.target.value })}
|
||||||
disabled={!filters.state}
|
className="px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:border-brand-gold transition-colors bg-white"
|
||||||
className="px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:border-brand-gold transition-colors bg-white disabled:bg-gray-100 disabled:cursor-not-allowed"
|
|
||||||
>
|
>
|
||||||
<option value="">Todas as cidades</option>
|
<option value="">Todas as cidades</option>
|
||||||
{availableCities.map((city) => (
|
{availableCities.map((city) => (
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,6 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
briefing: "",
|
briefing: "",
|
||||||
contacts: [{ name: "", role: "", phone: "" }],
|
contacts: [{ name: "", role: "", phone: "" }],
|
||||||
files: [] as File[],
|
files: [] as File[],
|
||||||
coverImage:
|
|
||||||
"https://images.unsplash.com/photo-1511795409834-ef04bbd61622?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80", // Default
|
|
||||||
institutionId: "",
|
institutionId: "",
|
||||||
attendees: "",
|
attendees: "",
|
||||||
courseId: "",
|
courseId: "",
|
||||||
|
|
@ -248,6 +246,12 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate course selection
|
||||||
|
if (!formData.courseId) {
|
||||||
|
alert("Por favor, selecione um curso/turma antes de continuar.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Show toast
|
// Show toast
|
||||||
setShowToast(true);
|
setShowToast(true);
|
||||||
// Call original submit after small delay for visual effect or immediately
|
// Call original submit after small delay for visual effect or immediately
|
||||||
|
|
@ -543,7 +547,7 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
{formData.institutionId && (
|
{formData.institutionId && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1 tracking-wide uppercase text-xs">
|
<label className="block text-sm font-medium text-gray-700 mb-1 tracking-wide uppercase text-xs">
|
||||||
Curso/Turma
|
Curso/Turma <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{availableCourses.length === 0 ? (
|
{availableCourses.length === 0 ? (
|
||||||
|
|
@ -575,8 +579,9 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
courseId: e.target.value,
|
courseId: e.target.value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
required
|
||||||
>
|
>
|
||||||
<option value="">Selecione um curso (opcional)</option>
|
<option value="">Selecione um curso *</option>
|
||||||
{availableCourses.map((course) => (
|
{availableCourses.map((course) => (
|
||||||
<option key={course.id} value={course.id}>
|
<option key={course.id} value={course.id}>
|
||||||
{course.name} - {course.graduationType} (
|
{course.name} - {course.graduationType} (
|
||||||
|
|
@ -587,52 +592,6 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Cover Image Upload */}
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1 tracking-wide uppercase text-xs">
|
|
||||||
Imagem de Capa
|
|
||||||
</label>
|
|
||||||
<div className="relative border border-gray-300 rounded-sm p-2 flex items-center bg-white">
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
accept="image/*"
|
|
||||||
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
|
||||||
onChange={(e) => {
|
|
||||||
if (e.target.files && e.target.files[0]) {
|
|
||||||
const file = e.target.files[0];
|
|
||||||
const imageUrl = URL.createObjectURL(file);
|
|
||||||
setFormData({ ...formData, coverImage: imageUrl });
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="flex items-center justify-between w-full px-2">
|
|
||||||
<span className="text-sm text-gray-500 truncate max-w-[200px]">
|
|
||||||
{formData.coverImage &&
|
|
||||||
!formData.coverImage.startsWith("http")
|
|
||||||
? "Imagem selecionada"
|
|
||||||
: formData.coverImage
|
|
||||||
? "Imagem atual (URL)"
|
|
||||||
: "Clique para selecionar..."}
|
|
||||||
</span>
|
|
||||||
<div className="bg-gray-100 p-1.5 rounded hover:bg-gray-200">
|
|
||||||
<Upload size={16} className="text-gray-600" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{formData.coverImage && (
|
|
||||||
<div className="mt-2 h-32 w-full rounded-sm overflow-hidden border border-gray-200 relative group">
|
|
||||||
<img
|
|
||||||
src={formData.coverImage}
|
|
||||||
alt="Preview"
|
|
||||||
className="w-full h-full object-cover"
|
|
||||||
/>
|
|
||||||
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center text-white text-xs">
|
|
||||||
Visualização da Capa
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row justify-end gap-3 sm:gap-0 mt-8">
|
<div className="flex flex-col sm:flex-row justify-end gap-3 sm:gap-0 mt-8">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React, { useState, useMemo } from 'react';
|
||||||
import { EventData, EventStatus, UserRole } from '../types';
|
import { EventData, EventStatus, UserRole } from '../types';
|
||||||
import { Calendar, MapPin, Users, CheckCircle, Clock } from 'lucide-react';
|
import { Calendar, MapPin, Users, CheckCircle, Clock, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react';
|
||||||
import { STATUS_COLORS } from '../constants';
|
import { STATUS_COLORS } from '../constants';
|
||||||
|
|
||||||
interface EventTableProps {
|
interface EventTableProps {
|
||||||
|
|
@ -10,6 +10,9 @@ interface EventTableProps {
|
||||||
userRole: UserRole;
|
userRole: UserRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SortField = 'name' | 'type' | 'date' | 'time' | 'city' | 'location' | 'status';
|
||||||
|
type SortOrder = 'asc' | 'desc' | null;
|
||||||
|
|
||||||
export const EventTable: React.FC<EventTableProps> = ({
|
export const EventTable: React.FC<EventTableProps> = ({
|
||||||
events,
|
events,
|
||||||
onEventClick,
|
onEventClick,
|
||||||
|
|
@ -17,6 +20,84 @@ export const EventTable: React.FC<EventTableProps> = ({
|
||||||
userRole
|
userRole
|
||||||
}) => {
|
}) => {
|
||||||
const canApprove = (userRole === UserRole.BUSINESS_OWNER || userRole === UserRole.SUPERADMIN);
|
const canApprove = (userRole === UserRole.BUSINESS_OWNER || userRole === UserRole.SUPERADMIN);
|
||||||
|
const [sortField, setSortField] = useState<SortField | null>(null);
|
||||||
|
const [sortOrder, setSortOrder] = useState<SortOrder>(null);
|
||||||
|
|
||||||
|
const handleSort = (field: SortField) => {
|
||||||
|
if (sortField === field) {
|
||||||
|
// Se já está ordenando por este campo, alterna a ordem
|
||||||
|
if (sortOrder === 'asc') {
|
||||||
|
setSortOrder('desc');
|
||||||
|
} else if (sortOrder === 'desc') {
|
||||||
|
setSortOrder(null);
|
||||||
|
setSortField(null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Novo campo, começa com ordem ascendente
|
||||||
|
setSortField(field);
|
||||||
|
setSortOrder('asc');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortedEvents = useMemo(() => {
|
||||||
|
if (!sortField || !sortOrder) {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorted = [...events].sort((a, b) => {
|
||||||
|
let aValue: any;
|
||||||
|
let bValue: any;
|
||||||
|
|
||||||
|
switch (sortField) {
|
||||||
|
case 'name':
|
||||||
|
aValue = a.name.toLowerCase();
|
||||||
|
bValue = b.name.toLowerCase();
|
||||||
|
break;
|
||||||
|
case 'type':
|
||||||
|
aValue = a.type.toLowerCase();
|
||||||
|
bValue = b.type.toLowerCase();
|
||||||
|
break;
|
||||||
|
case 'date':
|
||||||
|
aValue = new Date(a.date + 'T00:00:00').getTime();
|
||||||
|
bValue = new Date(b.date + 'T00:00:00').getTime();
|
||||||
|
break;
|
||||||
|
case 'time':
|
||||||
|
aValue = a.time;
|
||||||
|
bValue = b.time;
|
||||||
|
break;
|
||||||
|
case 'city':
|
||||||
|
aValue = a.address.city.toLowerCase();
|
||||||
|
bValue = b.address.city.toLowerCase();
|
||||||
|
break;
|
||||||
|
case 'location':
|
||||||
|
aValue = `${a.address.city}, ${a.address.state}`.toLowerCase();
|
||||||
|
bValue = `${b.address.city}, ${b.address.state}`.toLowerCase();
|
||||||
|
break;
|
||||||
|
case 'status':
|
||||||
|
aValue = a.status.toLowerCase();
|
||||||
|
bValue = b.status.toLowerCase();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aValue < bValue) return sortOrder === 'asc' ? -1 : 1;
|
||||||
|
if (aValue > bValue) return sortOrder === 'asc' ? 1 : -1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return sorted;
|
||||||
|
}, [events, sortField, sortOrder]);
|
||||||
|
|
||||||
|
const getSortIcon = (field: SortField) => {
|
||||||
|
if (sortField !== field) {
|
||||||
|
return <ArrowUpDown size={14} className="opacity-0 group-hover:opacity-50 transition-opacity" />;
|
||||||
|
}
|
||||||
|
if (sortOrder === 'asc') {
|
||||||
|
return <ArrowUp size={14} className="text-brand-gold" />;
|
||||||
|
}
|
||||||
|
return <ArrowDown size={14} className="text-brand-gold" />;
|
||||||
|
};
|
||||||
|
|
||||||
const formatDate = (date: string) => {
|
const formatDate = (date: string) => {
|
||||||
const eventDate = new Date(date + 'T00:00:00');
|
const eventDate = new Date(date + 'T00:00:00');
|
||||||
|
|
@ -50,20 +131,59 @@ export const EventTable: React.FC<EventTableProps> = ({
|
||||||
Ações
|
Ações
|
||||||
</th>
|
</th>
|
||||||
)}
|
)}
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
<th
|
||||||
Nome do Evento
|
className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider cursor-pointer hover:bg-gray-100 transition-colors group"
|
||||||
|
onClick={() => handleSort('name')}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
Nome do Evento
|
||||||
|
{getSortIcon('name')}
|
||||||
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
<th
|
||||||
Tipo
|
className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider cursor-pointer hover:bg-gray-100 transition-colors group"
|
||||||
|
onClick={() => handleSort('type')}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
Tipo
|
||||||
|
{getSortIcon('type')}
|
||||||
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
<th
|
||||||
Data
|
className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider cursor-pointer hover:bg-gray-100 transition-colors group"
|
||||||
|
onClick={() => handleSort('date')}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
Data
|
||||||
|
{getSortIcon('date')}
|
||||||
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
<th
|
||||||
Horário
|
className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider cursor-pointer hover:bg-gray-100 transition-colors group"
|
||||||
|
onClick={() => handleSort('time')}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
Horário
|
||||||
|
{getSortIcon('time')}
|
||||||
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
<th
|
||||||
Local
|
className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider cursor-pointer hover:bg-gray-100 transition-colors group"
|
||||||
|
onClick={() => handleSort('city')}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
Cidade
|
||||||
|
{getSortIcon('city')}
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider cursor-pointer hover:bg-gray-100 transition-colors group"
|
||||||
|
onClick={() => handleSort('location')}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
Local
|
||||||
|
{getSortIcon('location')}
|
||||||
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||||||
Contatos
|
Contatos
|
||||||
|
|
@ -71,13 +191,19 @@ export const EventTable: React.FC<EventTableProps> = ({
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||||||
Equipe
|
Equipe
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
<th
|
||||||
Status
|
className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider cursor-pointer hover:bg-gray-100 transition-colors group"
|
||||||
|
onClick={() => handleSort('status')}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
Status
|
||||||
|
{getSortIcon('status')}
|
||||||
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-gray-100">
|
<tbody className="divide-y divide-gray-100">
|
||||||
{events.map((event) => (
|
{sortedEvents.map((event) => (
|
||||||
<tr
|
<tr
|
||||||
key={event.id}
|
key={event.id}
|
||||||
onClick={() => onEventClick(event)}
|
onClick={() => onEventClick(event)}
|
||||||
|
|
@ -115,6 +241,11 @@ export const EventTable: React.FC<EventTableProps> = ({
|
||||||
{event.time}
|
{event.time}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<div className="text-sm text-gray-700">
|
||||||
|
{event.address.city}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3">
|
||||||
<div className="flex items-center text-sm text-gray-700">
|
<div className="flex items-center text-sm text-gray-700">
|
||||||
<MapPin size={14} className="mr-1.5 text-brand-gold flex-shrink-0" />
|
<MapPin size={14} className="mr-1.5 text-brand-gold flex-shrink-0" />
|
||||||
|
|
@ -164,7 +295,7 @@ export const EventTable: React.FC<EventTableProps> = ({
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{events.length === 0 && (
|
{sortedEvents.length === 0 && (
|
||||||
<div className="text-center py-12 text-gray-500">
|
<div className="text-center py-12 text-gray-500">
|
||||||
<p>Nenhum evento encontrado.</p>
|
<p>Nenhum evento encontrado.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
updateEventStatus,
|
updateEventStatus,
|
||||||
assignPhotographer,
|
assignPhotographer,
|
||||||
getInstitutionById,
|
getInstitutionById,
|
||||||
|
getActiveCoursesByInstitutionId,
|
||||||
} = useData();
|
} = useData();
|
||||||
const [view, setView] = useState<"list" | "create" | "edit" | "details">(
|
const [view, setView] = useState<"list" | "create" | "edit" | "details">(
|
||||||
initialView
|
initialView
|
||||||
|
|
@ -67,14 +68,10 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
// Extract unique values for filters
|
// Extract unique values for filters
|
||||||
const { availableStates, availableCities, availableTypes } = useMemo(() => {
|
const { availableStates, availableCities, availableTypes } = useMemo(() => {
|
||||||
const states = [...new Set(myEvents.map(e => e.address.state))].sort();
|
const states = [...new Set(myEvents.map(e => e.address.state))].sort();
|
||||||
const cities = advancedFilters.state
|
const cities = [...new Set(myEvents.map(e => e.address.city))].sort();
|
||||||
? [...new Set(myEvents
|
|
||||||
.filter(e => e.address.state === advancedFilters.state)
|
|
||||||
.map(e => e.address.city))].sort()
|
|
||||||
: [];
|
|
||||||
const types = [...new Set(myEvents.map(e => e.type))].sort();
|
const types = [...new Set(myEvents.map(e => e.type))].sort();
|
||||||
return { availableStates: states, availableCities: cities, availableTypes: types };
|
return { availableStates: states, availableCities: cities, availableTypes: types };
|
||||||
}, [myEvents, advancedFilters.state]);
|
}, [myEvents]);
|
||||||
|
|
||||||
// Helper function to check time range
|
// Helper function to check time range
|
||||||
const isInTimeRange = (time: string, range: string): boolean => {
|
const isInTimeRange = (time: string, range: string): boolean => {
|
||||||
|
|
@ -451,6 +448,27 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
{institution.type}
|
{institution.type}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{/* Course Information */}
|
||||||
|
{selectedEvent.courseId &&
|
||||||
|
(() => {
|
||||||
|
const course = getActiveCoursesByInstitutionId(
|
||||||
|
selectedEvent.institutionId
|
||||||
|
).find((c) => c.id === selectedEvent.courseId);
|
||||||
|
if (course) {
|
||||||
|
return (
|
||||||
|
<div className="bg-brand-gold/10 border border-brand-gold/30 rounded px-3 py-2 mb-3">
|
||||||
|
<p className="text-xs text-gray-500 uppercase tracking-wide mb-0.5">
|
||||||
|
Curso/Turma
|
||||||
|
</p>
|
||||||
|
<p className="text-sm font-semibold text-brand-black">
|
||||||
|
{course.name} - {course.graduationType} ({course.year}/{course.semester}º sem)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})()}
|
||||||
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 text-sm">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-gray-500 text-xs uppercase tracking-wide">
|
<p className="text-gray-500 text-xs uppercase tracking-wide">
|
||||||
|
|
|
||||||
|
|
@ -202,47 +202,49 @@ export const TeamPage: React.FC = () => {
|
||||||
const [selectedProfessional, setSelectedProfessional] =
|
const [selectedProfessional, setSelectedProfessional] =
|
||||||
useState<Professional | null>(null);
|
useState<Professional | null>(null);
|
||||||
const [showAddModal, setShowAddModal] = useState(false);
|
const [showAddModal, setShowAddModal] = useState(false);
|
||||||
|
|
||||||
const [newProfessional, setNewProfessional] = useState<Partial<Professional>>({
|
const [newProfessional, setNewProfessional] = useState<Partial<Professional>>(
|
||||||
name: "",
|
{
|
||||||
role: "Fotógrafo",
|
name: "",
|
||||||
address: {
|
role: "Fotógrafo",
|
||||||
street: "",
|
address: {
|
||||||
number: "",
|
street: "",
|
||||||
complement: "",
|
number: "",
|
||||||
neighborhood: "",
|
complement: "",
|
||||||
city: "",
|
neighborhood: "",
|
||||||
state: "",
|
city: "",
|
||||||
},
|
state: "",
|
||||||
whatsapp: "",
|
},
|
||||||
cpfCnpj: "",
|
whatsapp: "",
|
||||||
bankInfo: {
|
cpfCnpj: "",
|
||||||
bank: "",
|
bankInfo: {
|
||||||
agency: "",
|
bank: "",
|
||||||
accountPix: "",
|
agency: "",
|
||||||
},
|
accountPix: "",
|
||||||
hasCar: false,
|
},
|
||||||
hasStudio: false,
|
hasCar: false,
|
||||||
studioQuantity: 0,
|
hasStudio: false,
|
||||||
cardType: "",
|
studioQuantity: 0,
|
||||||
accountHolder: "",
|
cardType: "",
|
||||||
observations: "",
|
accountHolder: "",
|
||||||
ratings: {
|
observations: "",
|
||||||
technicalQuality: 0,
|
ratings: {
|
||||||
appearance: 0,
|
technicalQuality: 0,
|
||||||
education: 0,
|
appearance: 0,
|
||||||
sympathy: 0,
|
education: 0,
|
||||||
eventPerformance: 0,
|
sympathy: 0,
|
||||||
scheduleAvailability: 0,
|
eventPerformance: 0,
|
||||||
average: 0,
|
scheduleAvailability: 0,
|
||||||
},
|
average: 0,
|
||||||
freeTable: "",
|
},
|
||||||
extraFee: "",
|
freeTable: "",
|
||||||
email: "",
|
extraFee: "",
|
||||||
specialties: [],
|
email: "",
|
||||||
avatar: "",
|
specialties: [],
|
||||||
});
|
avatar: "",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const [avatarFile, setAvatarFile] = useState<File | null>(null);
|
const [avatarFile, setAvatarFile] = useState<File | null>(null);
|
||||||
const [avatarPreview, setAvatarPreview] = useState<string>("");
|
const [avatarPreview, setAvatarPreview] = useState<string>("");
|
||||||
|
|
||||||
|
|
@ -300,16 +302,21 @@ export const TeamPage: React.FC = () => {
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
professional.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
professional.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
professional.email.toLowerCase().includes(searchTerm.toLowerCase());
|
professional.email.toLowerCase().includes(searchTerm.toLowerCase());
|
||||||
const matchesRole = roleFilter === "all" || professional.role === roleFilter;
|
const matchesRole =
|
||||||
|
roleFilter === "all" || professional.role === roleFilter;
|
||||||
const matchesStatus =
|
const matchesStatus =
|
||||||
statusFilter === "all" || professional.status === statusFilter;
|
statusFilter === "all" || professional.status === statusFilter;
|
||||||
return matchesSearch && matchesRole && matchesStatus;
|
return matchesSearch && matchesRole && matchesStatus;
|
||||||
});
|
});
|
||||||
|
|
||||||
const stats = {
|
const stats = {
|
||||||
photographers: MOCK_PROFESSIONALS.filter((p) => p.role === "Fotógrafo").length,
|
photographers: MOCK_PROFESSIONALS.filter((p) => p.role === "Fotógrafo")
|
||||||
cinematographers: MOCK_PROFESSIONALS.filter((p) => p.role === "Cinegrafista").length,
|
.length,
|
||||||
receptionists: MOCK_PROFESSIONALS.filter((p) => p.role === "Recepcionista").length,
|
cinematographers: MOCK_PROFESSIONALS.filter(
|
||||||
|
(p) => p.role === "Cinegrafista"
|
||||||
|
).length,
|
||||||
|
receptionists: MOCK_PROFESSIONALS.filter((p) => p.role === "Recepcionista")
|
||||||
|
.length,
|
||||||
total: MOCK_PROFESSIONALS.length,
|
total: MOCK_PROFESSIONALS.length,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -344,7 +351,9 @@ export const TeamPage: React.FC = () => {
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600 mb-1">Total de Cinegrafistas</p>
|
<p className="text-sm text-gray-600 mb-1">
|
||||||
|
Total de Cinegrafistas
|
||||||
|
</p>
|
||||||
<p className="text-3xl font-bold text-brand-black">
|
<p className="text-3xl font-bold text-brand-black">
|
||||||
{stats.cinematographers}
|
{stats.cinematographers}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -355,7 +364,9 @@ export const TeamPage: React.FC = () => {
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600 mb-1">Total de Recepcionistas</p>
|
<p className="text-sm text-gray-600 mb-1">
|
||||||
|
Total de Recepcionistas
|
||||||
|
</p>
|
||||||
<p className="text-3xl font-bold text-brand-black">
|
<p className="text-3xl font-bold text-brand-black">
|
||||||
{stats.receptionists}
|
{stats.receptionists}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -606,7 +617,10 @@ export const TeamPage: React.FC = () => {
|
||||||
required
|
required
|
||||||
value={newProfessional.name}
|
value={newProfessional.name}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({ ...newProfessional, name: e.target.value })
|
setNewProfessional({
|
||||||
|
...newProfessional,
|
||||||
|
name: e.target.value,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
placeholder="Nome completo"
|
placeholder="Nome completo"
|
||||||
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
||||||
|
|
@ -645,7 +659,10 @@ export const TeamPage: React.FC = () => {
|
||||||
required
|
required
|
||||||
value={newProfessional.email}
|
value={newProfessional.email}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({ ...newProfessional, email: e.target.value })
|
setNewProfessional({
|
||||||
|
...newProfessional,
|
||||||
|
email: e.target.value,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
placeholder="email@exemplo.com"
|
placeholder="email@exemplo.com"
|
||||||
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
||||||
|
|
@ -661,7 +678,10 @@ export const TeamPage: React.FC = () => {
|
||||||
required
|
required
|
||||||
value={newProfessional.whatsapp}
|
value={newProfessional.whatsapp}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({ ...newProfessional, whatsapp: e.target.value })
|
setNewProfessional({
|
||||||
|
...newProfessional,
|
||||||
|
whatsapp: e.target.value,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
placeholder="(00) 00000-0000"
|
placeholder="(00) 00000-0000"
|
||||||
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
||||||
|
|
@ -678,7 +698,10 @@ export const TeamPage: React.FC = () => {
|
||||||
required
|
required
|
||||||
value={newProfessional.cpfCnpj}
|
value={newProfessional.cpfCnpj}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({ ...newProfessional, cpfCnpj: e.target.value })
|
setNewProfessional({
|
||||||
|
...newProfessional,
|
||||||
|
cpfCnpj: e.target.value,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
placeholder="000.000.000-00 ou 00.000.000/0000-00"
|
placeholder="000.000.000-00 ou 00.000.000/0000-00"
|
||||||
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
||||||
|
|
@ -704,7 +727,10 @@ export const TeamPage: React.FC = () => {
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({
|
setNewProfessional({
|
||||||
...newProfessional,
|
...newProfessional,
|
||||||
address: { ...newProfessional.address!, street: e.target.value },
|
address: {
|
||||||
|
...newProfessional.address!,
|
||||||
|
street: e.target.value,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
placeholder="Nome da rua"
|
placeholder="Nome da rua"
|
||||||
|
|
@ -723,7 +749,10 @@ export const TeamPage: React.FC = () => {
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({
|
setNewProfessional({
|
||||||
...newProfessional,
|
...newProfessional,
|
||||||
address: { ...newProfessional.address!, number: e.target.value },
|
address: {
|
||||||
|
...newProfessional.address!,
|
||||||
|
number: e.target.value,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
placeholder="123"
|
placeholder="123"
|
||||||
|
|
@ -743,7 +772,10 @@ export const TeamPage: React.FC = () => {
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({
|
setNewProfessional({
|
||||||
...newProfessional,
|
...newProfessional,
|
||||||
address: { ...newProfessional.address!, complement: e.target.value },
|
address: {
|
||||||
|
...newProfessional.address!,
|
||||||
|
complement: e.target.value,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
placeholder="Apto, Sala, etc"
|
placeholder="Apto, Sala, etc"
|
||||||
|
|
@ -762,7 +794,10 @@ export const TeamPage: React.FC = () => {
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({
|
setNewProfessional({
|
||||||
...newProfessional,
|
...newProfessional,
|
||||||
address: { ...newProfessional.address!, neighborhood: e.target.value },
|
address: {
|
||||||
|
...newProfessional.address!,
|
||||||
|
neighborhood: e.target.value,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
placeholder="Nome do bairro"
|
placeholder="Nome do bairro"
|
||||||
|
|
@ -783,7 +818,10 @@ export const TeamPage: React.FC = () => {
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({
|
setNewProfessional({
|
||||||
...newProfessional,
|
...newProfessional,
|
||||||
address: { ...newProfessional.address!, city: e.target.value },
|
address: {
|
||||||
|
...newProfessional.address!,
|
||||||
|
city: e.target.value,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
placeholder="Nome da cidade"
|
placeholder="Nome da cidade"
|
||||||
|
|
@ -803,7 +841,10 @@ export const TeamPage: React.FC = () => {
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({
|
setNewProfessional({
|
||||||
...newProfessional,
|
...newProfessional,
|
||||||
address: { ...newProfessional.address!, state: e.target.value.toUpperCase() },
|
address: {
|
||||||
|
...newProfessional.address!,
|
||||||
|
state: e.target.value.toUpperCase(),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
placeholder="SP"
|
placeholder="SP"
|
||||||
|
|
@ -831,7 +872,10 @@ export const TeamPage: React.FC = () => {
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({
|
setNewProfessional({
|
||||||
...newProfessional,
|
...newProfessional,
|
||||||
bankInfo: { ...newProfessional.bankInfo!, bank: e.target.value },
|
bankInfo: {
|
||||||
|
...newProfessional.bankInfo!,
|
||||||
|
bank: e.target.value,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
placeholder="Nome do banco"
|
placeholder="Nome do banco"
|
||||||
|
|
@ -850,7 +894,10 @@ export const TeamPage: React.FC = () => {
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({
|
setNewProfessional({
|
||||||
...newProfessional,
|
...newProfessional,
|
||||||
bankInfo: { ...newProfessional.bankInfo!, agency: e.target.value },
|
bankInfo: {
|
||||||
|
...newProfessional.bankInfo!,
|
||||||
|
agency: e.target.value,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
placeholder="0000-0"
|
placeholder="0000-0"
|
||||||
|
|
@ -869,7 +916,10 @@ export const TeamPage: React.FC = () => {
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({
|
setNewProfessional({
|
||||||
...newProfessional,
|
...newProfessional,
|
||||||
bankInfo: { ...newProfessional.bankInfo!, accountPix: e.target.value },
|
bankInfo: {
|
||||||
|
...newProfessional.bankInfo!,
|
||||||
|
accountPix: e.target.value,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
placeholder="Conta ou chave Pix"
|
placeholder="Conta ou chave Pix"
|
||||||
|
|
@ -888,7 +938,10 @@ export const TeamPage: React.FC = () => {
|
||||||
required
|
required
|
||||||
value={newProfessional.cardType}
|
value={newProfessional.cardType}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({ ...newProfessional, cardType: e.target.value })
|
setNewProfessional({
|
||||||
|
...newProfessional,
|
||||||
|
cardType: e.target.value,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
placeholder="Débito, Crédito, Pix, etc"
|
placeholder="Débito, Crédito, Pix, etc"
|
||||||
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
||||||
|
|
@ -904,7 +957,10 @@ export const TeamPage: React.FC = () => {
|
||||||
required
|
required
|
||||||
value={newProfessional.accountHolder}
|
value={newProfessional.accountHolder}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({ ...newProfessional, accountHolder: e.target.value })
|
setNewProfessional({
|
||||||
|
...newProfessional,
|
||||||
|
accountHolder: e.target.value,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
placeholder="Nome do titular"
|
placeholder="Nome do titular"
|
||||||
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
||||||
|
|
@ -926,7 +982,10 @@ export const TeamPage: React.FC = () => {
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={newProfessional.hasCar}
|
checked={newProfessional.hasCar}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({ ...newProfessional, hasCar: e.target.checked })
|
setNewProfessional({
|
||||||
|
...newProfessional,
|
||||||
|
hasCar: e.target.checked,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
className="w-4 h-4 text-brand-gold focus:ring-brand-gold border-gray-300 rounded"
|
className="w-4 h-4 text-brand-gold focus:ring-brand-gold border-gray-300 rounded"
|
||||||
/>
|
/>
|
||||||
|
|
@ -942,7 +1001,10 @@ export const TeamPage: React.FC = () => {
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={newProfessional.hasStudio}
|
checked={newProfessional.hasStudio}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({ ...newProfessional, hasStudio: e.target.checked })
|
setNewProfessional({
|
||||||
|
...newProfessional,
|
||||||
|
hasStudio: e.target.checked,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
className="w-4 h-4 text-brand-gold focus:ring-brand-gold border-gray-300 rounded"
|
className="w-4 h-4 text-brand-gold focus:ring-brand-gold border-gray-300 rounded"
|
||||||
/>
|
/>
|
||||||
|
|
@ -1096,7 +1158,10 @@ export const TeamPage: React.FC = () => {
|
||||||
required
|
required
|
||||||
value={newProfessional.freeTable}
|
value={newProfessional.freeTable}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({ ...newProfessional, freeTable: e.target.value })
|
setNewProfessional({
|
||||||
|
...newProfessional,
|
||||||
|
freeTable: e.target.value,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
placeholder="R$ 800,00"
|
placeholder="R$ 800,00"
|
||||||
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
||||||
|
|
@ -1111,7 +1176,10 @@ export const TeamPage: React.FC = () => {
|
||||||
type="text"
|
type="text"
|
||||||
value={newProfessional.extraFee}
|
value={newProfessional.extraFee}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({ ...newProfessional, extraFee: e.target.value })
|
setNewProfessional({
|
||||||
|
...newProfessional,
|
||||||
|
extraFee: e.target.value,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
placeholder="R$ 150,00"
|
placeholder="R$ 150,00"
|
||||||
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-brand-gold"
|
||||||
|
|
@ -1128,7 +1196,10 @@ export const TeamPage: React.FC = () => {
|
||||||
<textarea
|
<textarea
|
||||||
value={newProfessional.observations}
|
value={newProfessional.observations}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setNewProfessional({ ...newProfessional, observations: e.target.value })
|
setNewProfessional({
|
||||||
|
...newProfessional,
|
||||||
|
observations: e.target.value,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
placeholder="Qualquer tipo de observação relevante"
|
placeholder="Qualquer tipo de observação relevante"
|
||||||
rows={4}
|
rows={4}
|
||||||
|
|
@ -1178,7 +1249,9 @@ export const TeamPage: React.FC = () => {
|
||||||
<h2 className="text-2xl font-serif font-bold text-brand-black mb-1">
|
<h2 className="text-2xl font-serif font-bold text-brand-black mb-1">
|
||||||
{selectedProfessional.name}
|
{selectedProfessional.name}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600 mb-2">{selectedProfessional.role}</p>
|
<p className="text-gray-600 mb-2">
|
||||||
|
{selectedProfessional.role}
|
||||||
|
</p>
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<Star
|
<Star
|
||||||
size={18}
|
size={18}
|
||||||
|
|
@ -1189,7 +1262,8 @@ export const TeamPage: React.FC = () => {
|
||||||
{selectedProfessional.ratings.average.toFixed(1)}
|
{selectedProfessional.ratings.average.toFixed(1)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-sm text-gray-500">
|
<span className="text-sm text-gray-500">
|
||||||
({selectedProfessional.eventsCompleted} eventos concluídos)
|
({selectedProfessional.eventsCompleted} eventos
|
||||||
|
concluídos)
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
|
|
@ -1214,7 +1288,9 @@ export const TeamPage: React.FC = () => {
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Contato */}
|
{/* Contato */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-lg mb-3 text-brand-black">Contato</h3>
|
<h3 className="font-semibold text-lg mb-3 text-brand-black">
|
||||||
|
Contato
|
||||||
|
</h3>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center text-gray-700">
|
<div className="flex items-center text-gray-700">
|
||||||
<Mail size={18} className="mr-3 text-brand-gold" />
|
<Mail size={18} className="mr-3 text-brand-gold" />
|
||||||
|
|
@ -1227,10 +1303,14 @@ export const TeamPage: React.FC = () => {
|
||||||
<div className="flex items-start text-gray-700">
|
<div className="flex items-start text-gray-700">
|
||||||
<MapPin size={18} className="mr-3 text-brand-gold mt-1" />
|
<MapPin size={18} className="mr-3 text-brand-gold mt-1" />
|
||||||
<span>
|
<span>
|
||||||
{selectedProfessional.address.street}, {selectedProfessional.address.number}
|
{selectedProfessional.address.street},{" "}
|
||||||
{selectedProfessional.address.complement && ` - ${selectedProfessional.address.complement}`}
|
{selectedProfessional.address.number}
|
||||||
|
{selectedProfessional.address.complement &&
|
||||||
|
` - ${selectedProfessional.address.complement}`}
|
||||||
<br />
|
<br />
|
||||||
{selectedProfessional.address.neighborhood}, {selectedProfessional.address.city} - {selectedProfessional.address.state}
|
{selectedProfessional.address.neighborhood},{" "}
|
||||||
|
{selectedProfessional.address.city} -{" "}
|
||||||
|
{selectedProfessional.address.state}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1238,7 +1318,9 @@ export const TeamPage: React.FC = () => {
|
||||||
|
|
||||||
{/* Recursos */}
|
{/* Recursos */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-lg mb-3 text-brand-black">Recursos</h3>
|
<h3 className="font-semibold text-lg mb-3 text-brand-black">
|
||||||
|
Recursos
|
||||||
|
</h3>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{selectedProfessional.hasCar && (
|
{selectedProfessional.hasCar && (
|
||||||
<span className="flex items-center gap-2 px-3 py-1 bg-green-100 text-green-700 rounded-full text-sm">
|
<span className="flex items-center gap-2 px-3 py-1 bg-green-100 text-green-700 rounded-full text-sm">
|
||||||
|
|
@ -1257,13 +1339,19 @@ export const TeamPage: React.FC = () => {
|
||||||
|
|
||||||
{/* Avaliações */}
|
{/* Avaliações */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-lg mb-3 text-brand-black">Avaliações</h3>
|
<h3 className="font-semibold text-lg mb-3 text-brand-black">
|
||||||
|
Avaliações
|
||||||
|
</h3>
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||||
{selectedProfessional.role !== "Recepcionista" && (
|
{selectedProfessional.role !== "Recepcionista" && (
|
||||||
<div className="bg-gray-50 p-3 rounded-lg">
|
<div className="bg-gray-50 p-3 rounded-lg">
|
||||||
<p className="text-xs text-gray-600 mb-1">Qual. Técnica</p>
|
<p className="text-xs text-gray-600 mb-1">
|
||||||
|
Qual. Técnica
|
||||||
|
</p>
|
||||||
<p className="text-lg font-semibold text-brand-gold">
|
<p className="text-lg font-semibold text-brand-gold">
|
||||||
{selectedProfessional.ratings.technicalQuality.toFixed(1)}
|
{selectedProfessional.ratings.technicalQuality.toFixed(
|
||||||
|
1
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -1286,9 +1374,13 @@ export const TeamPage: React.FC = () => {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-50 p-3 rounded-lg">
|
<div className="bg-gray-50 p-3 rounded-lg">
|
||||||
<p className="text-xs text-gray-600 mb-1">Disponibilidade</p>
|
<p className="text-xs text-gray-600 mb-1">
|
||||||
|
Disponibilidade
|
||||||
|
</p>
|
||||||
<p className="text-lg font-semibold text-brand-gold">
|
<p className="text-lg font-semibold text-brand-gold">
|
||||||
{selectedProfessional.ratings.scheduleAvailability.toFixed(1)}
|
{selectedProfessional.ratings.scheduleAvailability.toFixed(
|
||||||
|
1
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1296,7 +1388,9 @@ export const TeamPage: React.FC = () => {
|
||||||
|
|
||||||
{/* Valores */}
|
{/* Valores */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-lg mb-3 text-brand-black">Valores</h3>
|
<h3 className="font-semibold text-lg mb-3 text-brand-black">
|
||||||
|
Valores
|
||||||
|
</h3>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
<div className="bg-gray-50 p-3 rounded-lg">
|
<div className="bg-gray-50 p-3 rounded-lg">
|
||||||
<p className="text-xs text-gray-600 mb-1">Tabela Free</p>
|
<p className="text-xs text-gray-600 mb-1">Tabela Free</p>
|
||||||
|
|
@ -1305,7 +1399,9 @@ export const TeamPage: React.FC = () => {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-50 p-3 rounded-lg">
|
<div className="bg-gray-50 p-3 rounded-lg">
|
||||||
<p className="text-xs text-gray-600 mb-1">Extra/Equipamento</p>
|
<p className="text-xs text-gray-600 mb-1">
|
||||||
|
Extra/Equipamento
|
||||||
|
</p>
|
||||||
<p className="text-lg font-semibold text-brand-black">
|
<p className="text-lg font-semibold text-brand-black">
|
||||||
{selectedProfessional.extraFee}
|
{selectedProfessional.extraFee}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -1316,7 +1412,9 @@ export const TeamPage: React.FC = () => {
|
||||||
{/* Observações */}
|
{/* Observações */}
|
||||||
{selectedProfessional.observations && (
|
{selectedProfessional.observations && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-lg mb-2 text-brand-black">Observações</h3>
|
<h3 className="font-semibold text-lg mb-2 text-brand-black">
|
||||||
|
Observações
|
||||||
|
</h3>
|
||||||
<p className="text-gray-700 bg-gray-50 p-3 rounded-lg">
|
<p className="text-gray-700 bg-gray-50 p-3 rounded-lg">
|
||||||
{selectedProfessional.observations}
|
{selectedProfessional.observations}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue