From 636ad739932c72f4da01f59fbcf44f199bd068a0 Mon Sep 17 00:00:00 2001 From: NANDO9322 Date: Thu, 25 Dec 2025 12:22:53 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20aprimora=20responsividade=20mobile,=20f?= =?UTF-8?q?orm=20de=20eventos=20e=20persist=C3=AAncia=20de=20dados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Frontend: - Implementa visualização em cards mobile para lista de Eventos (/painel), Gestão de Cursos (/cursos) e modal de Equipe. - Corrige rolagem e layout do modal de detalhes do profissional em telas pequenas. - Unifica seleção de turma (Curso/Inst/Ano) no formulário de eventos para simplificar UX. - Adiciona botão "Voltar" no formulário de eventos. - Adiciona integração de busca de CEP e validação de "Qtd Estúdios". - Ajusta inputs de avaliação (estrelas) e exibição de disponibilidade de horário. - Atualiza interfaces (types.ts) para incluir campos novos (cep, email no profissional). - Backend: - Adiciona persistência do campo "email" na tabela de profissionais. - Corrige bug de persistência nula no campo "media" (avaliação). - Atualiza queries SQL e gera novos modelos (sqlc) para refletir mudanças no schema. - Atualiza documentação Swagger. --- frontend/components/EventForm.tsx | 78 ++-- frontend/components/EventTable.tsx | 92 ++++- .../components/ProfessionalDetailsModal.tsx | 184 +++++++--- frontend/contexts/DataContext.tsx | 36 +- frontend/pages/CourseManagement.tsx | 339 +++++++++++------- frontend/pages/Dashboard.tsx | 84 ++++- frontend/pages/Team.tsx | 4 +- frontend/types.ts | 1 + 8 files changed, 579 insertions(+), 239 deletions(-) diff --git a/frontend/components/EventForm.tsx b/frontend/components/EventForm.tsx index 294ed10..5f5c1dc 100644 --- a/frontend/components/EventForm.tsx +++ b/frontend/components/EventForm.tsx @@ -661,8 +661,6 @@ export const EventForm: React.FC = ({ value={selectedCompanyId} onChange={e => { setSelectedCompanyId(e.target.value); - setSelectedCourseName(""); - setSelectedInstitutionName(""); setFormData({ ...formData, fotId: "" }); }} > @@ -688,65 +686,53 @@ export const EventForm: React.FC = ({ )} - {/* 1. Curso */} -
- - -
- - {/* 2. Instituição */} -
- - -
- - {/* 3. Ano/Turma (Final FOT Selection) */} + {/* Consolidated Turma Selection */}
- + + {loadingFots &&

Carregando turmas...

}
)} -
+
+ + )} + + {isPhotographer && photographerAssignment && ( + <> + {photographerAssignment.status === "PENDENTE" && ( +
+ + +
+ )} + {photographerAssignment.status === "ACEITO" && ( + Aceito + )} + {photographerAssignment.status === "REJEITADO" && ( + Rejeitado + )} + + )} +
+ )} +
+ + ); + })} + + +
diff --git a/frontend/components/ProfessionalDetailsModal.tsx b/frontend/components/ProfessionalDetailsModal.tsx index 22c73db..0ebb170 100644 --- a/frontend/components/ProfessionalDetailsModal.tsx +++ b/frontend/components/ProfessionalDetailsModal.tsx @@ -1,7 +1,10 @@ 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'; +import { + X, Mail, Phone, MapPin, Building, Star, Camera, DollarSign, Award, + User, Car, CreditCard, AlertTriangle +} from 'lucide-react'; interface ProfessionalDetailsModalProps { professional: Professional; @@ -18,10 +21,10 @@ export const ProfessionalDetailsModal: React.FC = return (
-
+
{/* Header com Capa/Avatar Style */} -
+
- - - - - - - - - - - - - - - - - {filteredList.length === 0 ? ( - - - - ) : ( - filteredList.map((item) => ( - - - - - - - - - - - - + + + + + + + + + + + + + )) + )} + +
- FOT - - Empresa - - Curso - - Instituição - - Ano Formatura - - Cidade - - Estado - - Observações - - Gastos Captação - - Pré Venda - - Ações -
-
- -

- Nenhuma turma FOT encontrada -

+ <> +
+ {filteredList.length === 0 ? ( +
+ +

Nenhuma turma FOT encontrada

+
+ ) : ( + filteredList.map((item) => ( +
+
+
+ FOT {item.fot} +
{item.empresa_nome}
-
-
- {item.fot || "-"} -
-
-
- {item.empresa_nome || "-"} -
-
-
- {item.curso_nome || "-"} -
-
-
- {item.instituicao || "-"} -
-
-
- {item.ano_formatura_label || "-"} -
-
-
- {item.cidade || "-"} -
-
-
- {item.estado || "-"} -
-
-
- {item.observacoes || "-"} -
-
-
- {item.gastos_captacao - ? item.gastos_captacao.toLocaleString("pt-BR", { - style: "currency", - currency: "BRL", - }) - : "R$ 0,00"} -
-
- + {item.pre_venda ? "Pré-venda" : "Regular"} + + + +
+

Curso: {item.curso_nome}

+

Inst: {item.instituicao}

+

+ {item.cidade} - {item.estado} + + {item.ano_formatura_label} +

+
+ + {item.observacoes && ( +

+ {item.observacoes} +

+ )} + +
+
+ {item.gastos_captacao ? item.gastos_captacao.toLocaleString("pt-BR", { style: "currency", currency: "BRL" }) : "R$ 0,00"} +
+
+
-
- - + Editar + + +
+ + + )) + )} + + +
+ + + + + + + + + + + + + + + + + + {filteredList.length === 0 ? ( + + - )) - )} - -
+ FOT + + Empresa + + Curso + + Instituição + + Ano Formatura + + Cidade + + Estado + + Observações + + Gastos Captação + + Pré Venda + + Ações +
+
+ +

+ Nenhuma turma FOT encontrada +

-
+ ) : ( + filteredList.map((item) => ( +
+
+ {item.fot || "-"} +
+
+
+ {item.empresa_nome || "-"} +
+
+
+ {item.curso_nome || "-"} +
+
+
+ {item.instituicao || "-"} +
+
+
+ {item.ano_formatura_label || "-"} +
+
+
+ {item.cidade || "-"} +
+
+
+ {item.estado || "-"} +
+
+
+ {item.observacoes || "-"} +
+
+
+ {item.gastos_captacao + ? item.gastos_captacao.toLocaleString("pt-BR", { + style: "currency", + currency: "BRL", + }) + : "R$ 0,00"} +
+
+ + {item.pre_venda ? "Sim" : "Não"} + + +
+ + +
+
+
+ )} diff --git a/frontend/pages/Dashboard.tsx b/frontend/pages/Dashboard.tsx index dea6e35..e0845ae 100644 --- a/frontend/pages/Dashboard.tsx +++ b/frontend/pages/Dashboard.tsx @@ -849,8 +849,8 @@ export const Dashboard: React.FC = ({

- {/* Tabela de Profissionais */} -
+ {/* Tabela de Profissionais (Desktop) */} +
@@ -984,6 +984,86 @@ export const Dashboard: React.FC = ({
+ + {/* Lista de Cards (Mobile) */} +
+ {professionals.map((photographer) => { + const assignment = (selectedEvent.assignments || []).find( + (a) => a.professionalId === photographer.id + ); + const status = assignment ? assignment.status : null; + + return ( +
+
handleViewProfessional(photographer)}> +
+
+

{photographer.name || photographer.nome}

+

ID: {photographer.id.substring(0, 8)}...

+
+
+ +
+ + {photographer.role} + + + {status === "ACEITO" && ( + + Confirmado + + )} + {status === "PENDENTE" && ( + + Pendente + + )} + {status === "REJEITADO" && ( + + Recusado + + )} + {!status && ( + + Disponível + + )} +
+ +
+ + +
+
+ ); + })} + {professionals.length === 0 && ( +
+ +

Nenhum profissional disponível.

+
+ )} +
+
{/* Footer */} diff --git a/frontend/pages/Team.tsx b/frontend/pages/Team.tsx index 1f72a69..313f03e 100644 --- a/frontend/pages/Team.tsx +++ b/frontend/pages/Team.tsx @@ -716,8 +716,8 @@ export const TeamPage: React.FC = () => { {/* View Modal */} {viewProfessional && ( -
-
+
+
{/* Header / Avatar Section */}