feat: destaca eventos de pré-venda e adiciona filtros de status da turma

- Destaque em azul para pré-venda na grid.
- Filtros por status (Pré-venda/Finalizada) no Dashboard.
- Badges de status nos detalhes do evento.
- Ajustes no backend para expor campo `pre_venda` da FOT.
This commit is contained in:
NANDO9322 2026-02-10 16:36:57 -03:00
parent b49b0f31a6
commit 9906db8bc6
7 changed files with 56 additions and 9 deletions

View file

@ -78,6 +78,7 @@ type AgendaResponse struct {
ParsedAssignments []Assignment `json:"assignments"`
ParsedContacts []ContactInfo `json:"contacts"`
ShadowFotFinalizada bool `json:"fot_finalizada"`
ShadowFotPreVenda bool `json:"fot_pre_venda"`
}
func (s *Service) CalculateStatus(fotoFaltante, recepFaltante, cineFaltante int32) string {
@ -205,6 +206,7 @@ func (s *Service) List(ctx context.Context, userID uuid.UUID, role string, regia
EmpresaNome: r.EmpresaNome,
EmpresaID: r.EmpresaID,
FotFinalizada: r.FotFinalizada,
FotPreVenda: r.FotPreVenda,
AnoSemestre: r.AnoSemestre,
ObservacoesFot: r.ObservacoesFot,
TipoEventoNome: r.TipoEventoNome,
@ -220,7 +222,7 @@ func (s *Service) List(ctx context.Context, userID uuid.UUID, role string, regia
// DEBUG: Check first few rows
for i, r := range rows {
if i < 5 {
fmt.Printf("DEBUG SERVICE ADMIN PATH: Row %d ID=%s FotFinalizada=%v\n", i, r.ID.Bytes, r.FotFinalizada.Bool)
fmt.Printf("DEBUG SERVICE ADMIN PATH: Row %d ID=%s FotFinalizada=%v FotPreVenda=%v\n", i, r.ID.Bytes, r.FotFinalizada.Bool, r.FotPreVenda.Bool)
}
}
}
@ -251,6 +253,7 @@ func (s *Service) List(ctx context.Context, userID uuid.UUID, role string, regia
ParsedAssignments: assignments,
ParsedContacts: contacts,
ShadowFotFinalizada: row.FotFinalizada.Bool,
ShadowFotPreVenda: row.FotPreVenda.Bool,
})
}

View file

@ -439,6 +439,7 @@ SELECT
te.nome as tipo_evento_nome,
cf.empresa_id,
cf.finalizada as fot_finalizada,
cf.pre_venda as fot_pre_venda,
COALESCE(
(SELECT json_agg(json_build_object(
'professional_id', ap.profissional_id,
@ -502,6 +503,7 @@ type ListAgendasRow struct {
TipoEventoNome string `json:"tipo_evento_nome"`
EmpresaID pgtype.UUID `json:"empresa_id"`
FotFinalizada pgtype.Bool `json:"fot_finalizada"`
FotPreVenda pgtype.Bool `json:"fot_pre_venda"`
AssignedProfessionals interface{} `json:"assigned_professionals"`
}
@ -555,6 +557,7 @@ func (q *Queries) ListAgendas(ctx context.Context, regiao pgtype.Text) ([]ListAg
&i.TipoEventoNome,
&i.EmpresaID,
&i.FotFinalizada,
&i.FotPreVenda,
&i.AssignedProfessionals,
); err != nil {
return nil, err
@ -579,6 +582,7 @@ SELECT
te.nome as tipo_evento_nome,
cf.empresa_id,
cf.finalizada as fot_finalizada,
cf.pre_venda as fot_pre_venda,
COALESCE(
(SELECT json_agg(json_build_object(
'professional_id', ap.profissional_id,
@ -647,6 +651,7 @@ type ListAgendasByCompanyRow struct {
TipoEventoNome string `json:"tipo_evento_nome"`
EmpresaID pgtype.UUID `json:"empresa_id"`
FotFinalizada pgtype.Bool `json:"fot_finalizada"`
FotPreVenda pgtype.Bool `json:"fot_pre_venda"`
AssignedProfessionals interface{} `json:"assigned_professionals"`
}
@ -700,6 +705,7 @@ func (q *Queries) ListAgendasByCompany(ctx context.Context, arg ListAgendasByCom
&i.TipoEventoNome,
&i.EmpresaID,
&i.FotFinalizada,
&i.FotPreVenda,
&i.AssignedProfessionals,
); err != nil {
return nil, err
@ -827,6 +833,7 @@ SELECT
te.nome as tipo_evento_nome,
cf.empresa_id,
cf.finalizada as fot_finalizada,
cf.pre_venda as fot_pre_venda,
COALESCE(
(SELECT json_agg(json_build_object(
'professional_id', ap.profissional_id,
@ -895,6 +902,7 @@ type ListAgendasByUserRow struct {
TipoEventoNome string `json:"tipo_evento_nome"`
EmpresaID pgtype.UUID `json:"empresa_id"`
FotFinalizada pgtype.Bool `json:"fot_finalizada"`
FotPreVenda pgtype.Bool `json:"fot_pre_venda"`
AssignedProfessionals interface{} `json:"assigned_professionals"`
}
@ -948,6 +956,7 @@ func (q *Queries) ListAgendasByUser(ctx context.Context, arg ListAgendasByUserPa
&i.TipoEventoNome,
&i.EmpresaID,
&i.FotFinalizada,
&i.FotPreVenda,
&i.AssignedProfessionals,
); err != nil {
return nil, err

View file

@ -46,6 +46,7 @@ SELECT
te.nome as tipo_evento_nome,
cf.empresa_id,
cf.finalizada as fot_finalizada,
cf.pre_venda as fot_pre_venda,
COALESCE(
(SELECT json_agg(json_build_object(
'professional_id', ap.profissional_id,
@ -79,6 +80,7 @@ SELECT
te.nome as tipo_evento_nome,
cf.empresa_id,
cf.finalizada as fot_finalizada,
cf.pre_venda as fot_pre_venda,
COALESCE(
(SELECT json_agg(json_build_object(
'professional_id', ap.profissional_id,
@ -228,6 +230,7 @@ SELECT
te.nome as tipo_evento_nome,
cf.empresa_id,
cf.finalizada as fot_finalizada,
cf.pre_venda as fot_pre_venda,
COALESCE(
(SELECT json_agg(json_build_object(
'professional_id', ap.profissional_id,

View file

@ -541,7 +541,13 @@ export const EventTable: React.FC<EventTableProps> = ({
<tr
key={event.id}
onClick={() => onEventClick(event)}
className={`cursor-pointer transition-colors ${event.fot_finalizada ? "bg-red-50 hover:bg-red-100 border-l-4 border-l-red-500" : "hover:bg-gray-50"}`}
className={`cursor-pointer transition-colors border-l-4 ${
event.fot_finalizada
? "bg-red-50 hover:bg-red-100 border-l-red-500"
: ((event.fot_pre_venda || event.pre_venda) && (userRole === UserRole.SUPERADMIN || userRole === UserRole.BUSINESS_OWNER || userRole === UserRole.RESEARCHER))
? "bg-blue-50 hover:bg-blue-100 border-l-blue-500"
: "border-l-transparent hover:bg-gray-50"
}`}
>
<td className="px-4 py-3">
<span className="text-sm font-medium text-gray-900">

View file

@ -724,7 +724,9 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
typeId: e.tipo_evento_id,
local_evento: e.local_evento, // Added local_evento mapping
fot_finalizada: e.fot_finalizada, // Mapped from backend
assignments: Array.isArray(e.assigned_professionals)
fot_pre_venda: e.fot_pre_venda, // Mapped from backend
pre_venda: e.fot_pre_venda, // Fallback/Alias
assignments: Array.isArray(e.assigned_professionals)
? e.assigned_professionals.map((a: any) => ({
professionalId: a.professional_id,
status: a.status,

View file

@ -439,8 +439,15 @@ export const Dashboard: React.FC<DashboardProps> = ({
const matchesInstitution =
!advancedFilters.institution || e.instituicao === advancedFilters.institution;
// New FOT Status Filter
const matchesFotStatus =
!advancedFilters.fotStatus ||
(advancedFilters.fotStatus === 'finalizada' && e.fot_finalizada) ||
(advancedFilters.fotStatus === 'pre_venda' && (e.fot_pre_venda || e.pre_venda)) ||
(advancedFilters.fotStatus === 'normal' && !e.fot_finalizada && !e.fot_pre_venda && !e.pre_venda);
return (
matchesSearch && matchesStatus && matchesDate && matchesFot && matchesType && matchesCompany && matchesInstitution
matchesSearch && matchesStatus && matchesDate && matchesFot && matchesType && matchesCompany && matchesInstitution && matchesFotStatus
);
});
@ -883,11 +890,26 @@ export const Dashboard: React.FC<DashboardProps> = ({
</span>
</div>
</div>
<div
className={`px-4 py-2 rounded text-sm font-semibold ${STATUS_COLORS[selectedEvent.status]
}`}
>
{selectedEvent.status}
<div className="flex items-center gap-2">
{/* Badge de Finalizada */}
{(selectedEvent.fot_finalizada) && (
<div className="px-4 py-2 rounded text-sm font-semibold bg-red-100 text-red-800 border border-red-200">
Turma Finalizada
</div>
)}
{/* Badge de Pré-Venda */}
{((selectedEvent.fot_pre_venda || selectedEvent.pre_venda) && !selectedEvent.fot_finalizada) && (
<div className="px-4 py-2 rounded text-sm font-semibold bg-blue-100 text-blue-800 border border-blue-200">
Pré-Venda
</div>
)}
<div
className={`px-4 py-2 rounded text-sm font-semibold ${STATUS_COLORS[selectedEvent.status]
}`}
>
{selectedEvent.status}
</div>
</div>
</div>
</div>

View file

@ -151,6 +151,8 @@ export interface EventData {
courseId?: string; // ID do curso/turma relacionado ao evento
fotId?: string; // ID da Turma (FOT)
fot_finalizada?: boolean; // Status da Turma (Visualizacao no Dashboard)
fot_pre_venda?: boolean; // Status de Pré-Venda (vindo do FOT)
pre_venda?: boolean; // Status de Pré-Venda
typeId?: string; // ID do Tipo de Evento (UUID)
// Campos de gestão de equipe e recursos