feat: Ajusta a edição de eventos no gerenciamento de agenda, incluindo atribuição de profissionais e status

This commit is contained in:
NANDO9322 2025-12-29 19:55:50 -03:00
parent cd00fb53f9
commit 002dee832d
8 changed files with 248 additions and 102 deletions

View file

@ -1,6 +1,7 @@
package agenda package agenda
import ( import (
"fmt"
"net/http" "net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -126,8 +127,11 @@ func (h *Handler) Update(c *gin.Context) {
return return
} }
fmt.Printf("Update Payload: %+v\n", req)
agenda, err := h.service.Update(c.Request.Context(), id, req) agenda, err := h.service.Update(c.Request.Context(), id, req)
if err != nil { if err != nil {
fmt.Printf("Update Error for ID %s: %v\n", id, err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erro ao atualizar agenda: " + err.Error()}) c.JSON(http.StatusInternalServerError, gin.H{"error": "Erro ao atualizar agenda: " + err.Error()})
return return
} }

View file

@ -3,6 +3,7 @@ package agenda
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"time" "time"
"photum-backend/internal/db/generated" "photum-backend/internal/db/generated"
@ -149,6 +150,7 @@ func (s *Service) List(ctx context.Context, userID uuid.UUID, role string) ([]Ag
Instituicao: r.Instituicao, Instituicao: r.Instituicao,
CursoNome: r.CursoNome, CursoNome: r.CursoNome,
EmpresaNome: r.EmpresaNome, EmpresaNome: r.EmpresaNome,
EmpresaID: r.EmpresaID,
AnoSemestre: r.AnoSemestre, AnoSemestre: r.AnoSemestre,
ObservacoesFot: r.ObservacoesFot, ObservacoesFot: r.ObservacoesFot,
TipoEventoNome: r.TipoEventoNome, TipoEventoNome: r.TipoEventoNome,
@ -191,6 +193,13 @@ func (s *Service) Get(ctx context.Context, id uuid.UUID) (generated.Agenda, erro
} }
func (s *Service) Update(ctx context.Context, id uuid.UUID, req CreateAgendaRequest) (generated.Agenda, error) { func (s *Service) Update(ctx context.Context, id uuid.UUID, req CreateAgendaRequest) (generated.Agenda, error) {
if req.FotID == uuid.Nil {
return generated.Agenda{}, fmt.Errorf("FOT ID inválido ou não informado")
}
if req.TipoEventoID == uuid.Nil {
return generated.Agenda{}, fmt.Errorf("Tipo de Evento ID inválido ou não informado")
}
status := s.CalculateStatus(req.FotoFaltante, req.RecepFaltante, req.CineFaltante) status := s.CalculateStatus(req.FotoFaltante, req.RecepFaltante, req.CineFaltante)
params := generated.UpdateAgendaParams{ params := generated.UpdateAgendaParams{

View file

@ -302,6 +302,7 @@ SELECT
af.ano_semestre, af.ano_semestre,
cf.observacoes as observacoes_fot, cf.observacoes as observacoes_fot,
te.nome as tipo_evento_nome, te.nome as tipo_evento_nome,
cf.empresa_id,
COALESCE( COALESCE(
(SELECT json_agg(json_build_object( (SELECT json_agg(json_build_object(
'professional_id', ap.profissional_id, 'professional_id', ap.profissional_id,
@ -357,6 +358,7 @@ type ListAgendasRow struct {
AnoSemestre string `json:"ano_semestre"` AnoSemestre string `json:"ano_semestre"`
ObservacoesFot pgtype.Text `json:"observacoes_fot"` ObservacoesFot pgtype.Text `json:"observacoes_fot"`
TipoEventoNome string `json:"tipo_evento_nome"` TipoEventoNome string `json:"tipo_evento_nome"`
EmpresaID pgtype.UUID `json:"empresa_id"`
AssignedProfessionals interface{} `json:"assigned_professionals"` AssignedProfessionals interface{} `json:"assigned_professionals"`
} }
@ -405,6 +407,7 @@ func (q *Queries) ListAgendas(ctx context.Context) ([]ListAgendasRow, error) {
&i.AnoSemestre, &i.AnoSemestre,
&i.ObservacoesFot, &i.ObservacoesFot,
&i.TipoEventoNome, &i.TipoEventoNome,
&i.EmpresaID,
&i.AssignedProfessionals, &i.AssignedProfessionals,
); err != nil { ); err != nil {
return nil, err return nil, err
@ -427,6 +430,7 @@ SELECT
af.ano_semestre, af.ano_semestre,
cf.observacoes as observacoes_fot, cf.observacoes as observacoes_fot,
te.nome as tipo_evento_nome, te.nome as tipo_evento_nome,
cf.empresa_id,
COALESCE( COALESCE(
(SELECT json_agg(json_build_object( (SELECT json_agg(json_build_object(
'professional_id', ap.profissional_id, 'professional_id', ap.profissional_id,
@ -483,6 +487,7 @@ type ListAgendasByUserRow struct {
AnoSemestre string `json:"ano_semestre"` AnoSemestre string `json:"ano_semestre"`
ObservacoesFot pgtype.Text `json:"observacoes_fot"` ObservacoesFot pgtype.Text `json:"observacoes_fot"`
TipoEventoNome string `json:"tipo_evento_nome"` TipoEventoNome string `json:"tipo_evento_nome"`
EmpresaID pgtype.UUID `json:"empresa_id"`
AssignedProfessionals interface{} `json:"assigned_professionals"` AssignedProfessionals interface{} `json:"assigned_professionals"`
} }
@ -531,6 +536,7 @@ func (q *Queries) ListAgendasByUser(ctx context.Context, userID pgtype.UUID) ([]
&i.AnoSemestre, &i.AnoSemestre,
&i.ObservacoesFot, &i.ObservacoesFot,
&i.TipoEventoNome, &i.TipoEventoNome,
&i.EmpresaID,
&i.AssignedProfessionals, &i.AssignedProfessionals,
); err != nil { ); err != nil {
return nil, err return nil, err

View file

@ -42,6 +42,7 @@ SELECT
af.ano_semestre, af.ano_semestre,
cf.observacoes as observacoes_fot, cf.observacoes as observacoes_fot,
te.nome as tipo_evento_nome, te.nome as tipo_evento_nome,
cf.empresa_id,
COALESCE( COALESCE(
(SELECT json_agg(json_build_object( (SELECT json_agg(json_build_object(
'professional_id', ap.profissional_id, 'professional_id', ap.profissional_id,
@ -70,6 +71,7 @@ SELECT
af.ano_semestre, af.ano_semestre,
cf.observacoes as observacoes_fot, cf.observacoes as observacoes_fot,
te.nome as tipo_evento_nome, te.nome as tipo_evento_nome,
cf.empresa_id,
COALESCE( COALESCE(
(SELECT json_agg(json_build_object( (SELECT json_agg(json_build_object(
'professional_id', ap.profissional_id, 'professional_id', ap.profissional_id,

View file

@ -148,39 +148,64 @@ export const EventForm: React.FC<EventFormProps> = ({
// Populate form with initialData // Populate form with initialData
useEffect(() => { useEffect(() => {
if (initialData) { if (initialData) {
console.log("EventForm received initialData:", initialData);
// Robust mapping for API snake_case fields
const mapLink = (initialData as any).local_evento || (initialData.address && initialData.address.mapLink) || "";
const mappedFotId = (initialData as any).fot_id || initialData.fotId || "";
const mappedEmpresaId = (initialData as any).empresa_id || (initialData as any).empresaId || "";
const mappedObservacoes = (initialData as any).observacoes_evento || initialData.name || "";
console.log("Mapped Values:", { mappedFotId, mappedEmpresaId, mappedObservacoes, mapLink });
// Parse Endereco String if Object is missing but String exists
// Format expected: "Rua, Numero - Cidade/UF" or similar
let addressData = initialData.address || {
street: "", number: "", city: "", state: "", zip: "", lat: -22.7394, lng: -47.3314, mapLink: ""
};
if (!initialData.address && (initialData as any).endereco) {
// Simple heuristic parser or just default. User might need to refine.
// Doing a best-effort copy to street if unstructured
addressData = { ...addressData, street: (initialData as any).endereco || "" };
}
// Safety: ensure all strings
addressData = {
street: addressData.street || "",
number: addressData.number || "",
city: addressData.city || "",
state: addressData.state || "",
zip: addressData.zip || "",
lat: addressData.lat || -22.7394,
lng: addressData.lng || -47.3314,
mapLink: addressData.mapLink || ""
};
// 1. Populate standard form fields // 1. Populate standard form fields
setFormData(prev => ({ setFormData(prev => ({
...prev, ...prev,
...initialData, ...initialData,
startTime: initialData.time || "00:00", startTime: initialData.time || (initialData as any).horario || "00:00",
locationName: (initialData as any).local_evento || (initialData as any).address?.mapLink?.includes('http') ? "" : (initialData as any).address?.mapLink || "", endTime: (initialData as any).horario_termino || prev.endTime || "",
fotId: initialData.fotId || "", locationName: mapLink.includes('http') ? "" : mapLink, // Avoid putting URL in name
fotId: mappedFotId,
name: mappedObservacoes, // Map Observacoes to Name field (displayed as "Observacoes do Evento")
briefing: mappedObservacoes, // Sync briefing
address: addressData,
})); }));
// 2. Populate derived dropdowns if data exists // 2. Populate derived dropdowns if data exists
// Check for empresa_id or empresaId in initialData if (mappedEmpresaId && (user?.role === UserRole.BUSINESS_OWNER || user?.role === UserRole.SUPERADMIN)) {
const initEmpresaId = (initialData as any).empresa_id || (initialData as any).empresaId; console.log("Setting Selected Company:", mappedEmpresaId);
if (initEmpresaId && (user?.role === UserRole.BUSINESS_OWNER || user?.role === UserRole.SUPERADMIN)) { setSelectedCompanyId(mappedEmpresaId);
setSelectedCompanyId(initEmpresaId);
} }
if (initialData.curso) { if (initialData.curso || (initialData as any).curso_nome) {
setSelectedCourseName(initialData.curso); setSelectedCourseName(initialData.curso || (initialData as any).curso_nome || "");
} }
if (initialData.instituicao) { if (initialData.instituicao) {
setSelectedInstitutionName(initialData.instituicao); setSelectedInstitutionName(initialData.instituicao || "");
}
// 3. Populate address if available
if (initialData.address) {
setFormData(prev => ({
...prev,
address: {
...prev.address,
...initialData.address,
mapLink: initialData.address.mapLink || ""
}
}));
} }
} }
}, [initialData, user?.role]); }, [initialData, user?.role]);
@ -204,16 +229,18 @@ export const EventForm: React.FC<EventFormProps> = ({
} }
// If we have a target company (or user is linked), fetch FOTs // If we have a target company (or user is linked), fetch FOTs
if (targetEmpresaId || user?.role === UserRole.EVENT_OWNER) { // EventOwner might be linked differently or via getCadastroFot logic if (targetEmpresaId || user?.role === UserRole.EVENT_OWNER) {
// Verify logic: EventOwner (client) usually has strict link.
// If targetEmpresaId is still empty and user is NOT BusinessOwner/Superadmin, we might skip or let backend decide (if user has implicit link).
// But let's assume valid flow.
setLoadingFots(true); setLoadingFots(true);
// Clear previous FOTs to force UI update and avoid stale data
setAvailableFots([]);
console.log("Fetching FOTs for company:", targetEmpresaId);
const token = localStorage.getItem("token") || ""; const token = localStorage.getItem("token") || "";
const response = await getCadastroFot(token, targetEmpresaId); const response = await getCadastroFot(token, targetEmpresaId);
if (response.data) { if (response.data) {
console.log("FOTs loaded:", response.data.length);
setAvailableFots(response.data); setAvailableFots(response.data);
} }
setLoadingFots(false); setLoadingFots(false);
@ -354,23 +381,36 @@ export const EventForm: React.FC<EventFormProps> = ({
// Validation // Validation
if (!formData.name) return alert("Preencha o tipo de evento"); if (!formData.name) return alert("Preencha o tipo de evento");
if (!formData.date) return alert("Preencha a data"); if (!formData.date) return alert("Preencha a data");
if (user?.role === UserRole.BUSINESS_OWNER || user?.role === UserRole.EVENT_OWNER) {
if (!formData.fotId) { // Ensure typeId is valid
alert("Por favor, selecione a Turma (Cadastro FOT) antes de continuar."); let finalTypeId = formData.typeId;
return; if (!finalTypeId || finalTypeId === "00000000-0000-0000-0000-000000000000") {
// Try to match by name
const matchingType = eventTypes.find(t => t.nome === formData.type);
if (matchingType) {
finalTypeId = matchingType.id;
} else {
// If strictly required by DB, we must stop.
// But for legacy compatibility, maybe we prompt user
return alert("Tipo de evento inválido. Por favor, selecione novamente o tipo do evento.");
} }
} }
if (!formData.fotId) {
alert("ERRO CRÍTICO: Turma (FOT) não identificada. Por favor, selecione a Turma ou Empresa novamente.");
return;
}
try { try {
setShowToast(true); setShowToast(true);
// Prepare Payload for Agenda API // Prepare Payload for Agenda API
const payload = { const payload = {
fot_id: formData.fotId, fot_id: formData.fotId, // Must be valid UUID
tipo_evento_id: formData.typeId || "00000000-0000-0000-0000-000000000000", tipo_evento_id: finalTypeId,
data_evento: new Date(formData.date).toISOString(), data_evento: new Date(formData.date).toISOString(),
horario: formData.startTime || "", horario: formData.startTime || "",
observacoes_evento: formData.briefing || "", observacoes_evento: formData.name || formData.briefing || "",
local_evento: formData.locationName || "", local_evento: formData.locationName || "",
endereco: `${formData.address.street}, ${formData.address.number} - ${formData.address.city}/${formData.address.state}`, endereco: `${formData.address.street}, ${formData.address.number} - ${formData.address.city}/${formData.address.state}`,
qtd_formandos: parseInt(formData.attendees) || 0, qtd_formandos: parseInt(formData.attendees) || 0,
@ -395,15 +435,13 @@ export const EventForm: React.FC<EventFormProps> = ({
}; };
// Submit to parent handler // Submit to parent handler
setTimeout(() => { if (onSubmit) {
if (onSubmit) { await onSubmit(payload);
onSubmit(formData); }
} alert("Solicitação enviada com sucesso!");
alert("Solicitação enviada com sucesso!");
}, 1000);
} catch (e: any) { } catch (e: any) {
console.error(e); console.error(e);
alert("Erro inesperado: " + e.message); alert("Erro ao salvar: " + (e.message || "Erro desconhecido"));
setShowToast(false); setShowToast(false);
} }
}; };

View file

@ -1,6 +1,6 @@
import React, { createContext, useContext, useState, ReactNode, useEffect } from "react"; import React, { createContext, useContext, useState, ReactNode, useEffect } from "react";
import { useAuth } from "./AuthContext"; import { useAuth } from "./AuthContext";
import { getPendingUsers, approveUser as apiApproveUser, getProfessionals, assignProfessional as apiAssignProfessional, removeProfessional as apiRemoveProfessional, updateEventStatus as apiUpdateStatus, updateAssignmentStatus as apiUpdateAssignmentStatus } from "../services/apiService"; import { getPendingUsers, approveUser as apiApproveUser, getProfessionals, assignProfessional as apiAssignProfessional, removeProfessional as apiRemoveProfessional, updateEventStatus as apiUpdateStatus, updateAssignmentStatus as apiUpdateAssignmentStatus, updateAgenda as apiUpdateAgenda } from "../services/apiService";
import { import {
EventData, EventData,
EventStatus, EventStatus,
@ -608,6 +608,7 @@ interface DataContextType {
rejectUser: (userId: string) => void; rejectUser: (userId: string) => void;
professionals: Professional[]; professionals: Professional[];
respondToAssignment: (eventId: string, status: string, reason?: string) => Promise<void>; respondToAssignment: (eventId: string, status: string, reason?: string) => Promise<void>;
updateEventDetails: (id: string, data: any) => Promise<void>;
} }
const DataContext = createContext<DataContextType | undefined>(undefined); const DataContext = createContext<DataContextType | undefined>(undefined);
@ -693,6 +694,7 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
instituicao: e.instituicao, instituicao: e.instituicao,
anoFormatura: e.ano_semestre, anoFormatura: e.ano_semestre,
empresa: e.empresa_nome, empresa: e.empresa_nome,
empresaId: e.empresa_id, // Ensure ID is passed to frontend
observacoes: e.observacoes_fot, observacoes: e.observacoes_fot,
typeId: e.tipo_evento_id, typeId: e.tipo_evento_id,
assignments: Array.isArray(e.assigned_professionals) assignments: Array.isArray(e.assigned_professionals)
@ -815,59 +817,63 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
fetchProfs(); fetchProfs();
}, [token]); }, [token]);
const addEvent = async (event: EventData) => { const addEvent = async (event: any) => {
const token = localStorage.getItem("token"); const token = localStorage.getItem("token");
if (!token) { if (!token) {
console.error("No token found"); console.error("No token found");
// Fallback for offline/mock throw new Error("Usuário não autenticado");
setEvents((prev) => [event, ...prev]);
return;
} }
try { try {
// Map frontend fields (camelCase) to backend fields (snake_case) // Check if payload is already mapped (snake_case) or needs mapping (camelCase)
const payload = { let payload;
fot_id: event.fotId, if (event.fot_id && event.tipo_evento_id) {
data_evento: event.date + "T" + (event.time || "00:00") + ":00Z", // Backend expects full datetime with timezone // Already snake_case (from EventForm payload)
tipo_evento_id: event.typeId, payload = event;
observacoes_evento: event.name, // "Observações do Evento" maps to name in EventForm } else {
// local_evento: event.address.street + ", " + event.address.number, // Or map separate fields if needed // Legacy camelCase mapping
local_evento: event.address.mapLink || "Local a definir", // using mapLink or some string payload = {
endereco: `${event.address.street}, ${event.address.number}, ${event.address.city} - ${event.address.state}`, fot_id: event.fotId,
horario: event.startTime, data_evento: event.date + "T" + (event.time || "00:00") + ":00Z",
// Defaulting missing counts to 0 for now as they are not in the simplified form tipo_evento_id: event.typeId,
qtd_formandos: event.attendees ? parseInt(String(event.attendees)) : 0, observacoes_evento: event.name,
qtd_fotografos: 0, local_evento: event.address?.mapLink || "Local a definir",
qtd_recepcionistas: 0, endereco: event.address ? `${event.address.street}, ${event.address.number}, ${event.address.city} - ${event.address.state}` : "",
qtd_cinegrafistas: 0, horario: event.startTime,
qtd_estudios: 0, qtd_formandos: event.attendees ? parseInt(String(event.attendees)) : 0,
qtd_ponto_foto: 0, qtd_fotografos: 0,
qtd_ponto_id: 0, qtd_recepcionistas: 0,
qtd_ponto_decorado: 0, qtd_cinegrafistas: 0,
qtd_pontos_led: 0, qtd_estudios: 0,
qtd_plataforma_360: 0, qtd_ponto_foto: 0,
status_profissionais: "AGUARDANDO", // Will be calculated by backend anyway qtd_ponto_id: 0,
foto_faltante: 0, qtd_ponto_decorado: 0,
recep_faltante: 0, qtd_pontos_led: 0,
cine_faltante: 0, qtd_plataforma_360: 0,
logistica_observacoes: "", status_profissionais: "AGUARDANDO",
pre_venda: false foto_faltante: 0,
}; recep_faltante: 0,
cine_faltante: 0,
logistica_observacoes: "",
pre_venda: false
};
}
console.log("[DEBUG] addEvent payload:", payload);
const result = await import("../services/apiService").then(m => m.createAgenda(token, payload)); const result = await import("../services/apiService").then(m => m.createAgenda(token, payload));
if (result.data) { if (result.data) {
// Success
console.log("Agenda criada:", result.data); console.log("Agenda criada:", result.data);
// Force reload to ensure complete data consistency (FOT, joins, etc.) // Force reload to ensure complete data consistency
window.location.href = '/painel'; window.location.href = '/painel';
} else { } else {
console.error("Erro ao criar agenda API:", result.error); console.error("Erro ao criar agenda API:", result.error);
// Fallback or Toast? throw new Error(result.error || "Erro ao criar agenda");
// We will optimistically add it locally or throw
} }
} catch (err) { } catch (err: any) {
console.error("Exception creating agenda:", err); console.error("Exception creating agenda:", err);
throw err; // Re-throw so EventForm knows it failed
} }
}; };
@ -1077,6 +1083,43 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
}) })
); );
}, },
updateEventDetails: async (id, data) => {
const token = localStorage.getItem("token");
if (!token) return;
const result = await apiUpdateAgenda(token, id, data);
if (result.error) {
throw new Error(result.error);
}
// Re-fetch logic to ensure state consistency
// Re-implementing simplified fetch logic here or we can trigger a reload.
// Since we are in DataContext, we can call a fetch function if we extract it.
// But to be safe and quick:
try {
const result = await import("../services/apiService").then(m => m.getAgendas(token));
if (result.data) {
// Re-map events logic from useEffect...
// This duplication is painful.
// Alternative: window.location.reload() in Dashboard.
// But let's assume the user navigates away or we do a simple local merge for Key Fields used in List.
setEvents(prev => prev.map(evt => {
if (evt.id === id) {
return {
...evt,
date: data.data_evento ? data.data_evento.split("T")[0] : evt.date,
time: data.horario || evt.time,
name: data.observacoes_evento || evt.name,
briefing: data.observacoes_evento || evt.briefing,
fotId: data.fot_id || evt.fotId,
empresaId: data.empresa_id || evt.empresaId, // If provided
// Address is hard to parse back to object from payload without logic
};
}
return evt;
}));
}
} catch (e) { console.error("Refresh failed", e); }
},
addEvent, addEvent,
updateEventStatus, updateEventStatus,
assignPhotographer, assignPhotographer,

View file

@ -34,6 +34,7 @@ export const Dashboard: React.FC<DashboardProps> = ({
}) => { }) => {
const { user } = useAuth(); const { user } = useAuth();
const navigate = useNavigate(); const navigate = useNavigate();
// Extract updateEventDetails from useData
const { const {
events, events,
getEventsByRole, getEventsByRole,
@ -44,7 +45,48 @@ export const Dashboard: React.FC<DashboardProps> = ({
getInstitutionById, getInstitutionById,
getActiveCoursesByInstitutionId, getActiveCoursesByInstitutionId,
respondToAssignment, respondToAssignment,
updateEventDetails,
} = useData(); } = useData();
// ... (inside component)
const handleSaveEvent = async (data: any) => {
const isClient = user.role === UserRole.EVENT_OWNER;
if (view === "edit" && selectedEvent) {
if (updateEventDetails) {
await updateEventDetails(selectedEvent.id, data);
// Force reload of view to reflect changes (or rely on DataContext optimistic update)
// But DataContext optimistic update only touched generic fields.
// Address might still be old in 'selectedEvent' state if we don't update it.
// Updating selectedEvent manually as well to be safe:
const updatedEvent = { ...selectedEvent, ...data, date: data.date || data.data_evento?.split('T')[0] || selectedEvent.date };
setSelectedEvent(updatedEvent);
setView("details");
// Optional: Reload page safely if critical fields changed that DataContext map didn't catch?
// For now, trust DataContext + local state update.
// Actually, DataContext refetch logic was "try import...", so it might be async.
// Let's reload window to be 100% sure for the user as requested "mudei a data e não mudou".
window.location.reload();
} else {
console.error("Update function not available");
}
} else {
const initialStatus = isClient
? EventStatus.PENDING_APPROVAL
: EventStatus.PLANNING;
const newEvent: EventData = {
...data,
id: Math.random().toString(36).substr(2, 9),
status: initialStatus,
checklist: [],
ownerId: isClient ? user.id : "unknown",
photographerIds: [],
};
addEvent(newEvent);
setView("list");
}
};
const [view, setView] = useState<"list" | "create" | "edit" | "details">( const [view, setView] = useState<"list" | "create" | "edit" | "details">(
initialView initialView
); );
@ -116,30 +158,7 @@ export const Dashboard: React.FC<DashboardProps> = ({
); );
}); });
const handleSaveEvent = (data: any) => {
const isClient = user.role === UserRole.EVENT_OWNER;
if (view === "edit" && selectedEvent) {
const updatedEvent = { ...selectedEvent, ...data };
console.log("Updated", updatedEvent);
setSelectedEvent(updatedEvent);
setView("details");
} else {
const initialStatus = isClient
? EventStatus.PENDING_APPROVAL
: EventStatus.PLANNING;
const newEvent: EventData = {
...data,
id: Math.random().toString(36).substr(2, 9),
status: initialStatus,
checklist: [],
ownerId: isClient ? user.id : "unknown",
photographerIds: [],
};
addEvent(newEvent);
setView("list");
}
};
// Keep selectedEvent in sync with global events state // Keep selectedEvent in sync with global events state
useEffect(() => { useEffect(() => {

View file

@ -529,6 +529,31 @@ export const getAgendas = async (token: string): Promise<ApiResponse<any[]>> =>
} }
}; };
// Agenda - Update
export const updateAgenda = async (token: string, id: string, data: any) => {
try {
const response = await fetch(`${API_BASE_URL}/api/agenda/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify(data),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
}
const responseData = await response.json();
return { data: responseData, error: null };
} catch (error: any) {
console.error("Erro ao atualizar agenda:", error);
return { data: null, error: error.message || "Erro ao atualizar agenda" };
}
};
export const updateAssignmentStatus = async (token: string, eventId: string, professionalId: string, status: string, reason?: string) => { export const updateAssignmentStatus = async (token: string, eventId: string, professionalId: string, status: string, reason?: string) => {
try { try {
const response = await fetch(`${API_BASE_URL}/api/agenda/${eventId}/professionals/${professionalId}/status`, { const response = await fetch(`${API_BASE_URL}/api/agenda/${eventId}/professionals/${professionalId}/status`, {