fix atendimentos.ts
Signed-off-by: Rodrigo Martins <rodrigo.sousa.577@gmail.com>
This commit is contained in:
1 parent
ea0481e09e
commit
8deada3efa
2 files changed
+167
-111
No files matched your search
@@ -0,0 +1,74 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Calendar } from '@fullcalendar/react';
|
||||
import dayGridPlugin from '@fullcalendar/daygrid';
|
||||
import timeGridPlugin from '@fullcalendar/timegrid';
|
||||
import interactionPlugin from '@fullcalendar/interaction';
|
||||
import ptBrLocale from '@fullcalendar/core/locales/pt-br';
|
||||
import { startOfMonth, endOfMonth, format } from 'date-fns';
|
||||
import { api } from '../services/api';
|
||||
|
||||
interface AgendamentoCalendarioProps {
|
||||
onEventClick?: (event: any) => void;
|
||||
onDateSelect?: (selectInfo: any) => void;
|
||||
}
|
||||
|
||||
export const AgendamentoCalendario: React.FC<AgendamentoCalendarioProps> = ({
|
||||
onEventClick,
|
||||
onDateSelect
|
||||
}) => {
|
||||
const [eventos, setEventos] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const loadAgendamentos = async (currentDate: Date) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const inicio = format(startOfMonth(currentDate), 'yyyy-MM-dd');
|
||||
const fim = format(endOfMonth(currentDate), 'yyyy-MM-dd');
|
||||
|
||||
const response = await api.get('/atendimentos/agendamentos', {
|
||||
params: { inicio, fim }
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
setEventos(response.data.data);
|
||||
} else {
|
||||
throw new Error(response.data.error || 'Erro ao carregar agendamentos');
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('Erro ao carregar agendamentos:', err);
|
||||
setError(err.message || 'Erro ao carregar agendamentos');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full">
|
||||
{loading && <div className="text-center">Carregando...</div>}
|
||||
{error && <div className="text-red-500 text-center">{error}</div>}
|
||||
|
||||
<Calendar
|
||||
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
|
||||
initialView="dayGridMonth"
|
||||
locale={ptBrLocale}
|
||||
events={eventos}
|
||||
editable={false}
|
||||
selectable={true}
|
||||
selectMirror={true}
|
||||
dayMaxEvents={true}
|
||||
weekends={true}
|
||||
datesSet={({ start }) => loadAgendamentos(start)}
|
||||
eventClick={({ event }) => onEventClick?.(event)}
|
||||
select={(selectInfo) => onDateSelect?.(selectInfo)}
|
||||
headerToolbar={{
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'dayGridMonth,timeGridWeek,timeGridDay'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -12,81 +12,86 @@ const upload = multer({ storage: multer.memoryStorage() });
|
||||
|
||||
router.use(authMiddleware);
|
||||
|
||||
// Middleware para logging com informações mais detalhadas
|
||||
router.use((req, res, next) => {
|
||||
const requestId = Math.random().toString(36).substring(7);
|
||||
console.log(`[${new Date().toISOString()}][${requestId}] Request:`, {
|
||||
method: req.method,
|
||||
path: req.path,
|
||||
originalUrl: req.originalUrl,
|
||||
params: req.params,
|
||||
query: req.query,
|
||||
body: req.body
|
||||
});
|
||||
|
||||
// Interceptar resposta para logging
|
||||
const oldJson = res.json;
|
||||
res.json = function(data) {
|
||||
console.log(`[${new Date().toISOString()}][${requestId}] Response:`, {
|
||||
statusCode: res.statusCode,
|
||||
data: data
|
||||
});
|
||||
return oldJson.apply(res, arguments);
|
||||
};
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
// Função auxiliar para validar se uma string é um UUID válido
|
||||
function isValidUUID(uuid: string): boolean {
|
||||
function isValidUUID(uuid: string, isRoute: boolean = false): boolean {
|
||||
// Lista de rotas que não precisam de validação UUID
|
||||
const routeExceptions = ['agendamentos', 'upload'];
|
||||
|
||||
// Se for uma rota e estiver na lista de exceções, retorna true
|
||||
if (isRoute && (routeExceptions.includes(uuid) || routeExceptions.some(route => uuid.startsWith(route)))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!uuid) return false;
|
||||
|
||||
try {
|
||||
// Validação principal com regex para UUID padrão
|
||||
const regexUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
|
||||
// Se passar na validação principal, retorna verdadeiro
|
||||
if (regexUUID.test(uuid)) return true;
|
||||
|
||||
// Caso contrário, verifica se é um ID válido de alguma outra forma
|
||||
// (como um ID numérico ou string que possa ser convertida em UUID)
|
||||
console.log(`ID recebido em formato não-UUID padrão: ${uuid}`);
|
||||
|
||||
// Se for "agendamentos", é uma rota especial que não precisa de validação UUID
|
||||
if (uuid === "agendamentos") return true;
|
||||
|
||||
// Se chegou aqui e não é um UUID válido, retorna falso
|
||||
return false;
|
||||
return regexUUID.test(uuid);
|
||||
} catch (error) {
|
||||
console.error("Erro ao validar UUID:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware para validação de UUID em rotas específicas
|
||||
router.param('id', (req, res, next, id) => {
|
||||
const path = req.path.split('/')[1]; // Pega o primeiro segmento da rota após a /
|
||||
|
||||
if (!isValidUUID(id, true)) {
|
||||
console.error(`ID inválido: ${id} para rota ${path}`);
|
||||
return res.status(400).json({
|
||||
error: "ID inválido",
|
||||
details: "O formato do ID fornecido não é válido para esta operação"
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
// Rota para listar agendamentos
|
||||
router.get("/agendamentos", async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { data } = req.query;
|
||||
console.log("Requisição de agendamentos recebida, data:", data);
|
||||
const { inicio, fim } = req.query;
|
||||
console.log("[Agendamentos] Buscando agendamentos:", { inicio, fim });
|
||||
|
||||
let dateFilter = {};
|
||||
let whereClause = {};
|
||||
|
||||
if (data) {
|
||||
try {
|
||||
// Aceitar e validar o formato da data de forma mais flexível
|
||||
const parsedDate = new Date(data as string);
|
||||
|
||||
// Verificar se a data é válida
|
||||
if (isNaN(parsedDate.getTime())) {
|
||||
console.error("Data inválida recebida:", data);
|
||||
// Em vez de retornar erro, continue sem filtro
|
||||
console.log("Continuando sem filtro de data devido a data inválida");
|
||||
} else {
|
||||
console.log("Data parseada:", parsedDate);
|
||||
|
||||
const weekStart = startOfWeek(parsedDate, { locale: ptBR });
|
||||
const weekEnd = endOfWeek(parsedDate, { locale: ptBR });
|
||||
|
||||
console.log("Período de busca:", {
|
||||
weekStart: weekStart.toISOString(),
|
||||
weekEnd: weekEnd.toISOString()
|
||||
});
|
||||
|
||||
dateFilter = {
|
||||
if (inicio && fim) {
|
||||
whereClause = {
|
||||
data: {
|
||||
gte: weekStart,
|
||||
lte: weekEnd,
|
||||
},
|
||||
gte: new Date(inicio as string),
|
||||
lte: new Date(fim as string)
|
||||
}
|
||||
};
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.error("Erro ao analisar data:", parseError);
|
||||
// Continuar sem filtro de data em caso de erro, em vez de falhar
|
||||
console.log("Continuando sem filtro de data devido ao erro");
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Filtro de data:", JSON.stringify(dateFilter));
|
||||
|
||||
// Usando o modelo correto e com tratamento de erro adequado
|
||||
try {
|
||||
const agendamentos = await prisma.agendamentoAtendimento.findMany({
|
||||
where: dateFilter,
|
||||
where: whereClause,
|
||||
include: {
|
||||
cliente: {
|
||||
select: {
|
||||
@@ -106,20 +111,27 @@ router.get("/agendamentos", async (req: Request, res: Response) => {
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`Encontrados ${agendamentos.length} agendamentos`);
|
||||
|
||||
return res.json(agendamentos);
|
||||
} catch (dbError) {
|
||||
console.error("Erro na consulta do banco de dados:", dbError);
|
||||
return res.status(500).json({
|
||||
error: "Erro ao buscar agendamentos no banco de dados",
|
||||
details: dbError.message
|
||||
return res.json({
|
||||
success: true,
|
||||
data: agendamentos.map(ag => ({
|
||||
id: ag.id,
|
||||
title: `${ag.cliente.nome} - ${ag.observacoes || 'Sem observações'}`,
|
||||
start: ag.data,
|
||||
end: ag.data,
|
||||
clienteId: ag.clienteId,
|
||||
observacoes: ag.observacoes,
|
||||
status: ag.status,
|
||||
usuarioId: ag.usuarioId,
|
||||
usuario: ag.usuario,
|
||||
cliente: ag.cliente
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Erro geral ao listar agendamentos:", error);
|
||||
console.error("[Agendamentos] Erro:", error);
|
||||
return res.status(500).json({
|
||||
error: "Erro ao listar agendamentos",
|
||||
success: false,
|
||||
error: "Erro ao buscar agendamentos",
|
||||
details: error.message
|
||||
});
|
||||
}
|
||||
@@ -128,11 +140,14 @@ router.get("/agendamentos", async (req: Request, res: Response) => {
|
||||
// Rota para criar um novo agendamento
|
||||
router.post("/agendamentos", async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { clienteId, data, observacoes, tipo } = req.body;
|
||||
console.log("Criando agendamento:", { clienteId, data, observacoes, tipo });
|
||||
const { clienteId, data, observacoes } = req.body; // Removido tipo temporariamente
|
||||
console.log("[Agendamentos] Criando agendamento:", { clienteId, data, observacoes });
|
||||
|
||||
if (!clienteId || !data) {
|
||||
return res.status(400).json({ error: "Cliente e data são obrigatórios" });
|
||||
return res.status(400).json({
|
||||
error: "Dados inválidos",
|
||||
details: "Cliente e data são obrigatórios"
|
||||
});
|
||||
}
|
||||
|
||||
// Verificar se o cliente existe
|
||||
@@ -144,15 +159,14 @@ router.post("/agendamentos", async (req: Request, res: Response) => {
|
||||
return res.status(404).json({ error: "Cliente não encontrado" });
|
||||
}
|
||||
|
||||
// Criar o agendamento - usando agendamentoAtendimento
|
||||
// Criar o agendamento sem o campo tipo
|
||||
const agendamento = await prisma.agendamentoAtendimento.create({
|
||||
data: {
|
||||
clienteId,
|
||||
data: new Date(data),
|
||||
observacoes,
|
||||
tipo,
|
||||
status: "AGENDADO",
|
||||
usuarioId: req.user?.id // Adicionando usuarioId do middleware auth
|
||||
usuarioId: req.user?.id
|
||||
},
|
||||
include: {
|
||||
cliente: {
|
||||
@@ -164,16 +178,20 @@ router.post("/agendamentos", async (req: Request, res: Response) => {
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Agendamento criado:", {
|
||||
console.log("[Agendamentos] Agendamento criado com sucesso:", {
|
||||
id: agendamento.id,
|
||||
data: agendamento.data,
|
||||
clienteNome: agendamento.cliente.nome
|
||||
});
|
||||
|
||||
res.status(201).json(agendamento);
|
||||
return res.status(201).json(agendamento);
|
||||
} catch (error) {
|
||||
console.error("Erro ao criar agendamento:", error);
|
||||
res.status(500).json({ error: "Erro ao criar agendamento", details: error.message });
|
||||
console.error("[Agendamentos] Erro ao criar agendamento:", error);
|
||||
return res.status(500).json({
|
||||
error: "Erro ao criar agendamento",
|
||||
details: error.message,
|
||||
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -183,15 +201,6 @@ router.put("/agendamentos/:id", async (req: Request, res: Response) => {
|
||||
const { id } = req.params;
|
||||
const { clienteId, data, observacoes, tipo } = req.body;
|
||||
|
||||
// Validação do UUID antes de consultar o banco de dados
|
||||
if (!isValidUUID(id)) {
|
||||
console.error(`ID de agendamento inválido: ${id}. Não é um UUID válido.`);
|
||||
return res.status(400).json({
|
||||
error: "ID de agendamento inválido",
|
||||
details: "O formato do ID fornecido não é um UUID válido."
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Atualizando agendamento:", { id, clienteId, data, observacoes, tipo });
|
||||
|
||||
// Verificar se o agendamento existe usando a tabela correta
|
||||
@@ -241,15 +250,6 @@ router.delete("/agendamentos/:id", async (req: Request, res: Response) => {
|
||||
const { id } = req.params;
|
||||
console.log("Excluindo agendamento:", id);
|
||||
|
||||
// Validação do UUID antes de consultar o banco de dados
|
||||
if (!isValidUUID(id)) {
|
||||
console.error(`ID de agendamento inválido: ${id}. Não é um UUID válido.`);
|
||||
return res.status(400).json({
|
||||
error: "ID de agendamento inválido",
|
||||
details: "O formato do ID fornecido não é um UUID válido."
|
||||
});
|
||||
}
|
||||
|
||||
// Verificar se o agendamento existe usando a tabela correta
|
||||
const agendamento = await prisma.agendamentoAtendimento.findUnique({
|
||||
where: { id },
|
||||
@@ -286,15 +286,6 @@ router.patch("/:id", async (req: Request, res: Response) => {
|
||||
const { id } = req.params;
|
||||
const { anexos, ...data } = req.body;
|
||||
|
||||
// Validação do UUID antes de consultar o banco de dados
|
||||
if (!isValidUUID(id)) {
|
||||
console.error(`ID de atendimento inválido: ${id}. Não é um UUID válido.`);
|
||||
return res.status(400).json({
|
||||
error: "ID de atendimento inválido",
|
||||
details: "O formato do ID fornecido não é um UUID válido."
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Atualizando atendimento:", id, { anexos, data }); // Debug log
|
||||
|
||||
const atendimento = await prisma.atendimento.update({
|
||||
@@ -363,15 +354,6 @@ router.get("/:id", async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
// Validação do UUID antes de consultar o banco de dados
|
||||
if (!isValidUUID(id)) {
|
||||
console.error(`ID de atendimento inválido: ${id}. Não é um UUID válido.`);
|
||||
return res.status(400).json({
|
||||
error: "ID de atendimento inválido",
|
||||
details: "O formato do ID fornecido não é um UUID válido."
|
||||
});
|
||||
}
|
||||
|
||||
const atendimento = await prisma.atendimento.findUnique({
|
||||
where: {
|
||||
id: id,
|
||||
|
||||
Reference in new issue
Block a user