From ec2d96333f80341cf883fa169c89ee3c6144d528 Mon Sep 17 00:00:00 2001 From: NANDO9322 Date: Tue, 3 Feb 2026 12:47:43 -0300 Subject: [PATCH] =?UTF-8?q?Fix:=20Visibilidade=20da=20agenda=20para=20clie?= =?UTF-8?q?ntes=20e=20corre=C3=A7=C3=B5es=20no=20filtro=20de=20c=C3=B3digo?= =?UTF-8?q?s=20de=20acesso?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Backend: Implementada query `ListAgendasByCompany` e ajustada lógica do serviço de agenda para filtrar eventos pela empresa do usuário. - Backend: Adicionada migração segura (idempotente) para incluir coluna `empresa_id` em produção. - Frontend: Corrigido filtro [getEventsByRole] para exibir eventos importados (da empresa) para o cliente. - Frontend: Renomeada aba de aprovação para 'Cadastros Clientes'. --- backend/internal/agenda/service.go | 17 ++- backend/internal/db/generated/agenda.sql.go | 132 ++++++++++++++++++++ backend/internal/db/queries/agenda.sql | 33 ++++- frontend/contexts/DataContext.tsx | 4 + frontend/pages/UserApproval.tsx | 4 +- 5 files changed, 184 insertions(+), 6 deletions(-) diff --git a/backend/internal/agenda/service.go b/backend/internal/agenda/service.go index 1ea4541..438553d 100644 --- a/backend/internal/agenda/service.go +++ b/backend/internal/agenda/service.go @@ -122,13 +122,24 @@ func (s *Service) List(ctx context.Context, userID uuid.UUID, role string) ([]Ag var rows []generated.ListAgendasRow var err error - // If role is CLIENT (cliente), filter by userID + // If role is CLIENT (cliente) or EVENT_OWNER if role == "cliente" || role == "EVENT_OWNER" { - listRows, err := s.queries.ListAgendasByUser(ctx, pgtype.UUID{Bytes: userID, Valid: true}) + // New Logic: Fetch User's Company + user, err := s.queries.GetUsuarioByID(ctx, pgtype.UUID{Bytes: userID, Valid: true}) + if err != nil { + return nil, fmt.Errorf("erro ao buscar usuário para filtro de empresa: %v", err) + } + + if !user.EmpresaID.Valid { + // If no company linked, return empty or error? Empty seems safer. + return []AgendaResponse{}, nil + } + + listRows, err := s.queries.ListAgendasByCompany(ctx, user.EmpresaID) if err != nil { return nil, err } - // Convert ListAgendasByUserRow to ListAgendasRow manually + // Convert ListAgendasByCompanyRow to ListAgendasRow manually for _, r := range listRows { rows = append(rows, generated.ListAgendasRow{ ID: r.ID, diff --git a/backend/internal/db/generated/agenda.sql.go b/backend/internal/db/generated/agenda.sql.go index eec533d..92e8481 100644 --- a/backend/internal/db/generated/agenda.sql.go +++ b/backend/internal/db/generated/agenda.sql.go @@ -518,6 +518,138 @@ func (q *Queries) ListAgendas(ctx context.Context) ([]ListAgendasRow, error) { return items, nil } +const listAgendasByCompany = `-- name: ListAgendasByCompany :many +SELECT + a.id, a.user_id, a.fot_id, a.data_evento, a.tipo_evento_id, a.observacoes_evento, a.local_evento, a.endereco, a.horario, a.qtd_formandos, a.qtd_fotografos, a.qtd_recepcionistas, a.qtd_cinegrafistas, a.qtd_estudios, a.qtd_ponto_foto, a.qtd_ponto_id, a.qtd_ponto_decorado, a.qtd_pontos_led, a.qtd_plataforma_360, a.status_profissionais, a.foto_faltante, a.recep_faltante, a.cine_faltante, a.logistica_observacoes, a.pre_venda, a.criado_em, a.atualizado_em, a.status, a.logistica_notificacao_enviada_em, + cf.fot as fot_numero, + cf.instituicao, + c.nome as curso_nome, + e.nome as empresa_nome, + af.ano_semestre, + cf.observacoes as observacoes_fot, + te.nome as tipo_evento_nome, + cf.empresa_id, + COALESCE( + (SELECT json_agg(json_build_object( + 'professional_id', ap.profissional_id, + 'status', ap.status, + 'motivo_rejeicao', ap.motivo_rejeicao, + 'funcao_id', ap.funcao_id + )) + FROM agenda_profissionais ap + WHERE ap.agenda_id = a.id), + '[]'::json + ) as assigned_professionals +FROM agenda a +JOIN cadastro_fot cf ON a.fot_id = cf.id +JOIN cursos c ON cf.curso_id = c.id +JOIN empresas e ON cf.empresa_id = e.id +JOIN anos_formaturas af ON cf.ano_formatura_id = af.id +JOIN tipos_eventos te ON a.tipo_evento_id = te.id +WHERE cf.empresa_id = $1 +ORDER BY a.data_evento +` + +type ListAgendasByCompanyRow struct { + ID pgtype.UUID `json:"id"` + UserID pgtype.UUID `json:"user_id"` + FotID pgtype.UUID `json:"fot_id"` + DataEvento pgtype.Date `json:"data_evento"` + TipoEventoID pgtype.UUID `json:"tipo_evento_id"` + ObservacoesEvento pgtype.Text `json:"observacoes_evento"` + LocalEvento pgtype.Text `json:"local_evento"` + Endereco pgtype.Text `json:"endereco"` + Horario pgtype.Text `json:"horario"` + QtdFormandos pgtype.Int4 `json:"qtd_formandos"` + QtdFotografos pgtype.Int4 `json:"qtd_fotografos"` + QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"` + QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"` + QtdEstudios pgtype.Int4 `json:"qtd_estudios"` + QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"` + QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"` + QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"` + QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"` + QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"` + StatusProfissionais pgtype.Text `json:"status_profissionais"` + FotoFaltante pgtype.Int4 `json:"foto_faltante"` + RecepFaltante pgtype.Int4 `json:"recep_faltante"` + CineFaltante pgtype.Int4 `json:"cine_faltante"` + LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"` + PreVenda pgtype.Bool `json:"pre_venda"` + CriadoEm pgtype.Timestamptz `json:"criado_em"` + AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"` + Status pgtype.Text `json:"status"` + LogisticaNotificacaoEnviadaEm pgtype.Timestamp `json:"logistica_notificacao_enviada_em"` + FotNumero string `json:"fot_numero"` + Instituicao pgtype.Text `json:"instituicao"` + CursoNome string `json:"curso_nome"` + EmpresaNome string `json:"empresa_nome"` + AnoSemestre string `json:"ano_semestre"` + ObservacoesFot pgtype.Text `json:"observacoes_fot"` + TipoEventoNome string `json:"tipo_evento_nome"` + EmpresaID pgtype.UUID `json:"empresa_id"` + AssignedProfessionals interface{} `json:"assigned_professionals"` +} + +func (q *Queries) ListAgendasByCompany(ctx context.Context, empresaID pgtype.UUID) ([]ListAgendasByCompanyRow, error) { + rows, err := q.db.Query(ctx, listAgendasByCompany, empresaID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ListAgendasByCompanyRow + for rows.Next() { + var i ListAgendasByCompanyRow + if err := rows.Scan( + &i.ID, + &i.UserID, + &i.FotID, + &i.DataEvento, + &i.TipoEventoID, + &i.ObservacoesEvento, + &i.LocalEvento, + &i.Endereco, + &i.Horario, + &i.QtdFormandos, + &i.QtdFotografos, + &i.QtdRecepcionistas, + &i.QtdCinegrafistas, + &i.QtdEstudios, + &i.QtdPontoFoto, + &i.QtdPontoID, + &i.QtdPontoDecorado, + &i.QtdPontosLed, + &i.QtdPlataforma360, + &i.StatusProfissionais, + &i.FotoFaltante, + &i.RecepFaltante, + &i.CineFaltante, + &i.LogisticaObservacoes, + &i.PreVenda, + &i.CriadoEm, + &i.AtualizadoEm, + &i.Status, + &i.LogisticaNotificacaoEnviadaEm, + &i.FotNumero, + &i.Instituicao, + &i.CursoNome, + &i.EmpresaNome, + &i.AnoSemestre, + &i.ObservacoesFot, + &i.TipoEventoNome, + &i.EmpresaID, + &i.AssignedProfessionals, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listAgendasByFot = `-- name: ListAgendasByFot :many SELECT a.id, a.user_id, a.fot_id, a.data_evento, a.tipo_evento_id, a.observacoes_evento, a.local_evento, a.endereco, a.horario, a.qtd_formandos, a.qtd_fotografos, a.qtd_recepcionistas, a.qtd_cinegrafistas, a.qtd_estudios, a.qtd_ponto_foto, a.qtd_ponto_id, a.qtd_ponto_decorado, a.qtd_pontos_led, a.qtd_plataforma_360, a.status_profissionais, a.foto_faltante, a.recep_faltante, a.cine_faltante, a.logistica_observacoes, a.pre_venda, a.criado_em, a.atualizado_em, a.status, a.logistica_notificacao_enviada_em, diff --git a/backend/internal/db/queries/agenda.sql b/backend/internal/db/queries/agenda.sql index 0a70d2e..8932b6d 100644 --- a/backend/internal/db/queries/agenda.sql +++ b/backend/internal/db/queries/agenda.sql @@ -205,4 +205,35 @@ ORDER BY a.data_evento; -- name: GetAgendaByFotDataTipo :one SELECT * FROM agenda WHERE fot_id = $1 AND data_evento = $2 AND tipo_evento_id = $3 -LIMIT 1; \ No newline at end of file +LIMIT 1; + +-- name: ListAgendasByCompany :many +SELECT + a.*, + cf.fot as fot_numero, + cf.instituicao, + c.nome as curso_nome, + e.nome as empresa_nome, + af.ano_semestre, + cf.observacoes as observacoes_fot, + te.nome as tipo_evento_nome, + cf.empresa_id, + COALESCE( + (SELECT json_agg(json_build_object( + 'professional_id', ap.profissional_id, + 'status', ap.status, + 'motivo_rejeicao', ap.motivo_rejeicao, + 'funcao_id', ap.funcao_id + )) + FROM agenda_profissionais ap + WHERE ap.agenda_id = a.id), + '[]'::json + ) as assigned_professionals +FROM agenda a +JOIN cadastro_fot cf ON a.fot_id = cf.id +JOIN cursos c ON cf.curso_id = c.id +JOIN empresas e ON cf.empresa_id = e.id +JOIN anos_formaturas af ON cf.ano_formatura_id = af.id +JOIN tipos_eventos te ON a.tipo_evento_id = te.id +WHERE cf.empresa_id = $1 +ORDER BY a.data_evento; \ No newline at end of file diff --git a/frontend/contexts/DataContext.tsx b/frontend/contexts/DataContext.tsx index e5581c5..3ee0862 100644 --- a/frontend/contexts/DataContext.tsx +++ b/frontend/contexts/DataContext.tsx @@ -976,6 +976,10 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({ return events; } if (role === "EVENT_OWNER") { + // Check if logged user has company linked and matches requested user + if (user && user.id === userId && user.empresaId) { + return events.filter(e => e.empresaId === user.empresaId); + } return events.filter((e) => e.ownerId === userId); } if (role === "PHOTOGRAPHER") { diff --git a/frontend/pages/UserApproval.tsx b/frontend/pages/UserApproval.tsx index 01eafbc..3fef23c 100644 --- a/frontend/pages/UserApproval.tsx +++ b/frontend/pages/UserApproval.tsx @@ -168,7 +168,7 @@ export const UserApproval: React.FC = ({ onNavigate }) => { }`} > - Cadastros Empresas + Cadastros Clientes = ({ onNavigate }) => { )}

{activeTab === "cliente" - ? "Nenhum cadastro de empresa encontrado" + ? "Nenhum cadastro de cliente encontrado" : "Nenhum cadastro profissional encontrado"}