USER
FOI SO UM EXEMPLO, de como quero no atendimento e novoatendimento
import React, { useEffect, useState } from "react";
import styles from "./Atendimentos.module.css";
import { DatePickerDemo } from "../shadcdn-ui/DatePicker/DatePicker";
import CardAtendimento from "./CardAtendimento";
import NovoAtendimento from "./NovoAtendimento";
import { AppointmentRequestDTO } from "./types";
import { useApi } from "../../lib/doctorApi";
const Atendimentos: React.FC = () => {
const [appointments, setAppointments] = useState<AppointmentRequestDTO[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const api = useApi(); // `useApi` é tratado como estável.
useEffect(() => {
const fetchAppointments = async () => {
try {
const response = await api.get("/appointments/find?skip=0&limit=40");
console.log("dados resgatados", response);
// Verifique se a resposta é um array
if (Array.isArray(response.data.result.list)) {
setAppointments(response.data.result.list); // Armazenando os dados no estado
} else {
throw new Error("A resposta não contém uma lista de atendimentos.");
}
} catch (error) {
console.error("Erro ao carregar atendimentos:", error);
setError(error instanceof Error ? error.message : "Erro desconhecido");
} finally {
setLoading(false);
}
};
fetchAppointments();
}, []);
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error}</div>;
}
return (
<div className={styles.mainBody}>
<h1>Atendimentos</h1>
<h3>Home / Local</h3>
<div className={styles.formulario}>
<h2>Filtrar</h2>
<div className={styles.situacao}>
<h2>Situação:</h2>
<form className={styles.radioList}>
<label className={styles.reto}>
<input type="radio" name="situacao" value="opcao1" />
Rascunho
</label>
<label className={styles.reto}>
<input type="radio" name="situacao" value="opcao2" />
Aprovadas
</label>
<label className={styles.reto}>
<input type="radio" name="situacao" value="opcao3" />À faturar
</label>
<label className={styles.reto}>
<input type="radio" name="situacao" value="opcao4" />
Entregues
</label>
<label className={styles.reto}>
<input type="radio" name="situacao" value="opcao5" />
Glosados
</label>
<label className={styles.reto}>
<input type="radio" name="situacao" value="opcao6" />
Perdidos
</label>
<label className={styles.reto}>
<input type="radio" name="situacao" value="opcao7" />
Recebidos
</label>
</form>
</div>
<div className={styles.modalidade}>
<div className={styles.modalidadeOptions}>
<h2>Modalidade:</h2>
<form className={styles.radioListModalidade}>
<label className={styles.reto}>
<input type="radio" name="modalidade" value="opcao1" />
Todas
</label>
<label className={styles.reto}>
<input type="radio" name="modalidade" value="opcao2" />
Convenio
</label>
<label className={styles.reto}>
<input type="radio" name="modalidade" value="opcao3" />
Particular
</label>
<label className={styles.reto}>
<input type="radio" name="modalidade" value="opcao4" />
Plantão
</label>
</form>
</div>
<div className={styles.select}>
<label htmlFor="localSelect">
<h2>Local:</h2>
</label>
<select id="localSelect">
<option value="">Buscar</option>
{/* Adicione as opções aqui */}
</select>
</div>
<div className={styles.select}>
<label htmlFor="payerSelect">
<h2>Fonte Pagadora:</h2>
</label>
<select id="payerSelect">
<option value="">Buscar</option>
{/* Adicione as opções aqui */}
</select>
</div>
</div>
<div className={styles.dateSection}>
<div>
<h2>Data Inicial:</h2>
<DatePickerDemo />
</div>
<div>
<h2>Data Final:</h2>
<DatePickerDemo />
</div>
</div>
</div>
<div className={styles.pesquisaAtendimentos}>
<h2>Busque por Atendimentos</h2>
{/* Adicione os campos de pesquisa aqui */}
</div>
<div className={styles.campoPesquisa}>
<input
className={styles.inputBusca}
type="text"
placeholder="Atendimentos"
/>
</div>
<div className={styles.cardsAtendimentos}>
{appointments.map((appointment) => (
<CardAtendimento
key={appointment.id} // Assegure que id está definido em AppointmentRequestDTO
appointment={appointment}
alternarCard={() => {}}
/>
))}
</div>
</div>
);
};
export default Atendimentos;
import EstiloNovoAtendimento from "./NovoAtendimento.module.css";
import React, { useState, useEffect } from "react";
import { useApi } from "../../lib/doctorApi";
import { AppointmentRequestDTO, ProcedureDTO } from "./types";
import DropdownSelect from "./DropdownSelect";
import CardProcedimento from "./CardProcedimento";
import { v4 as uuidv4 } from "uuid"; // Importa a função para gerar UUIDs
import { toMoney } from "../utils/FormatedData";
import Anexo from "../ui/Anexo";
const CoresInput = [
"#25AAE1", // Cor 1
"#8696BB", // Cor 2
"#39465F", // Cor 3
"#D56D98", // Cor 4
];
const NovoAtendimento: React.FC = () => {
const updateProcedimento = (index: number, novosDados: ProcedureDTO) => {
console.log(index, novosDados);
const cloneAtendimento = { ...atendimento };
cloneAtendimento.procedures[index] = novosDados;
setAtendimento(cloneAtendimento);
};
const [botaoSalvar, setBotaoSalvar] = useState("");
const enviarDadosAtendimento = async () => {
loading;
try {
const result = await api.post("appointments/create", atendimento);
if (result?.data?.success) {
if (
result.data.result?.id &&
result.data.result.id !== atendimento.id
) {
const cloneAtendimento = { ...atendimento };
cloneAtendimento.id = result.data.result.id;
setAtendimento(cloneAtendimento);
}
console.log("dados enviados", result);
}
result?.data?.messages;
console.log("atendimento criado comm sucesso", result);
setBotaoSalvar("Salvo");
} catch (e: any) {
e?.response?.data?.messages;
console.log("erro ao criar atendimento", e);
setBotaoSalvar("Salvar");
}
setLoading;
};
useEffect(() => {
setBotaoSalvar("Salvar");
const queryParams = new URLSearchParams(location.search);
const appointmentId = queryParams.get("id");
if (appointmentId) {
const fetchAtendimento = async () => {
try {
const response = await api.get(`appointments/get/${appointmentId}`);
const atendimento = response.data.result;
setAtendimento(atendimento);
} catch (error) {
console.error("Erro ao buscar paciente:", error);
}
};
fetchAtendimento();
}
}, []);
const handleAdicionarProcedimento = () => {
const novoProcedimento: ProcedureDTO = {
id: uuidv4(),
createdAt: new Date(),
updatedAt: new Date(),
cbhpmCode: "",
cbhpmName: "",
expectedValue: 0,
glossedValue: 0,
lostValue: 0,
paidValue: 0,
placeId: atendimento.related.place?.id || "",
payerId: atendimento.related.payer?.id || "",
patientId: atendimento.related.patient?.id || "",
statusOfActivity: "draft",
attachments: [],
};
const cloneAtendimento = { ...atendimento };
cloneAtendimento.procedures.push(novoProcedimento);
setAtendimento(cloneAtendimento);
setMostrarCard(true);
};
const handleExcluirProcedimento = (id: string) => {
// Filtra o array de procedimentos, mantendo apenas aqueles cujo id não coincide.
const novosProcedimentos = atendimento.procedures.filter(
(proc) => proc.id !== id
);
// Cria uma cópia do estado de atendimento e atualiza a lista de procedimentos
setAtendimento((prev) => ({
...prev,
procedures: novosProcedimentos, // Atualiza a lista de procedimentos
}));
// Opcional: Se existir alguma lógica que dependa da atualização da barra gráfica, você pode chamar:
};
// Atualiza o localStorage sempre que procedimentos mudar
const [mostrarCard, setMostrarCard] = useState(false); // Estado para expandir/recolher o Card
const [anexos, setAnexos] = useState<
{ id: string; fileName: string; extension: string }[]
>([]); // Estado para os arquivos anexados
const [loading, setLoading] = useState(false);
const api = useApi(); // `useApi` é tratado como estável
const [atendimento, setAtendimento] = useState<AppointmentRequestDTO>({
status: {
glossed: false,
lost: false,
received: false,
draft: false,
validated: false,
derived: false,
finished: false,
},
related: {
place: undefined,
payer: undefined,
patient: undefined,
},
paymentInfo: {
method: "healthInsurance",
info: {
totalSum: 0,
healthInsurance: {
cardNumber: "",
},
totalValues: {
expected: 0,
glossed: 0,
lost: 0,
paid: 0,
},
totalProcedures: {
expected: 0,
glossed: 0,
lost: 0,
paid: 0,
},
},
},
serviceInfo: {
guideCode: "",
date: "",
totalValue: 0,
},
procedures: [],
});
const atualizarValoresBarra = () => {
const totalProcedures = {
expected: 0,
glossed: 0,
lost: 0,
paid: 0,
};
const totalValue = {
expected: 0,
glossed: 0,
lost: 0,
paid: 0,
};
atendimento.procedures.forEach((proc) => {
if (proc.expectedValue > 0) {
totalProcedures.expected += 1; // Incrementa para expectedValue
totalValue.expected += proc.expectedValue;
}
if (proc.glossedValue > 0) {
totalProcedures.glossed += 1; // Incrementa para glossedValue
totalValue.glossed += proc.glossedValue;
}
if (proc.lostValue > 0) {
totalProcedures.lost += 1; // Incrementa para lostValue
totalValue.lost += proc.lostValue;
}
if (proc.paidValue > 0) {
totalProcedures.paid += 1; // Incrementa para paidValue
totalValue.paid += proc.paidValue;
}
});
const cloneAtendimento = { ...atendimento };
cloneAtendimento.paymentInfo.info.totalProcedures = totalProcedures;
cloneAtendimento.paymentInfo.info.totalValues = totalValue;
cloneAtendimento.paymentInfo.info.totalSum =
cloneAtendimento.paymentInfo.info.totalValues.expected +
cloneAtendimento.paymentInfo.info.totalValues.glossed +
cloneAtendimento.paymentInfo.info.totalValues.lost +
cloneAtendimento.paymentInfo.info.totalValues.paid;
setAtendimento(cloneAtendimento); // Atualiza o estado dos valores da barra
};
useEffect(() => {
atualizarValoresBarra();
}, [JSON.stringify(atendimento.procedures)]);
// Função que alterna entre expandir e recolher
const alternarCard = () => {
setMostrarCard(!mostrarCard);
};
return (
<div>
<label className={EstiloNovoAtendimento.labeltitle}>
Cadastro de Atendimento
</label>
<form>
<div className={EstiloNovoAtendimento.divValores}>
<div className={EstiloNovoAtendimento.inputValores}>
<label className={EstiloNovoAtendimento.total}>Total:</label>
{[
atendimento.paymentInfo.info.totalValues.expected,
atendimento.paymentInfo.info.totalValues.glossed,
atendimento.paymentInfo.info.totalValues.lost,
atendimento.paymentInfo.info.totalValues.paid,
].map((valor, index) => (
<div key={index}>
<span
style={{
color: CoresInput[index],
fontSize: "14px",
marginRight: "2px",
}}
>
R$
</span>
<input
key={index}
type="number"
value={valor.toFixed(2)}
placeholder="R$ 0,00"
style={{ color: CoresInput[index] }}
readOnly
/>
</div>
))}
</div>
<div className={EstiloNovoAtendimento.divBarraGrafico}>
<label className={EstiloNovoAtendimento.labelTotal}>
R$ {toMoney(atendimento.paymentInfo.info.totalSum)}
{/* Aqui já usará o total atualizado */}
</label>
<div className={EstiloNovoAtendimento.graficoRepresentativo}>
<div className={EstiloNovoAtendimento.barraFixa}>
{[
atendimento.paymentInfo.info.totalValues.expected,
atendimento.paymentInfo.info.totalValues.glossed,
atendimento.paymentInfo.info.totalValues.lost,
atendimento.paymentInfo.info.totalValues.paid,
].map((valor, index) => (
<div
key={`grafico-${index}`}
style={{
backgroundColor: CoresInput[index],
width:
atendimento.paymentInfo.info.totalSum > 0
? `${
(valor / atendimento.paymentInfo.info.totalSum) *
100
}%`
: `0%`,
height: "100%",
}}
/>
))}
</div>
</div>
</div>
</div>
<div className={EstiloNovoAtendimento.container}>
<div className={EstiloNovoAtendimento.modalidade}>
<label>Modalidade:</label>
<div className={EstiloNovoAtendimento.divMargin}>
<input
className={EstiloNovoAtendimento.radio}
type="radio"
id="convenio"
name="modalidade"
value="convenio"
onChange={() => {
setAtendimento((prev) => ({
...prev,
paymentInfo: {
...prev.paymentInfo,
method: "healthInsurance", // Ou qualquer método que você queira usar
},
}));
}}
/>
<label
htmlFor="convenio"
className={EstiloNovoAtendimento.labelModalidade}
>
Convênio
</label>
<input
className={EstiloNovoAtendimento.radio}
type="radio"
id="particular"
name="modalidade"
value="particular"
onChange={() => {
setAtendimento((prev) => ({
...prev,
paymentInfo: {
...prev.paymentInfo,
method: "private", // Atualize para "particular"
},
}));
}}
/>
<label
htmlFor="particular"
className={EstiloNovoAtendimento.labelModalidade}
>
Particular
</label>
<input
className={EstiloNovoAtendimento.radio}
type="radio"
id="plantao"
name="modalidade"
value="plantao"
onChange={() => {
setAtendimento((prev) => ({
...prev,
paymentInfo: {
...prev.paymentInfo,
method: "onDuty", // Atualize para "plantao"
},
}));
}}
/>
<label
htmlFor="plantao"
className={EstiloNovoAtendimento.labelModalidade}
>
Plantão
</label>
</div>
</div>
<DropdownSelect
endpoint="places/find?skip=0&limit=2000&payer=true" // URL do endpoint
parseData={(data) =>
data.result.list.map((local: any) => ({
id: local.id,
name: local.name,
}))
} // Função que transforma os dados da API no formato { id, name }
onSelect={(selectedItem) => {
// Atualizar o estado de atendimento para incluir o payer
setAtendimento((prev) => ({
...prev,
related: {
...prev.related,
payer: { id: selectedItem.id, name: selectedItem.name }, // Assumindo que você quer armazenar um objeto { id, name }
},
}));
}} // Define o item selecionado no estado
value={atendimento.related.payer?.id || ""} // Valor atual do dropdown
label="Fonte Pagadora:" // Rótulo a ser exibido
/>
</div>
<div className={EstiloNovoAtendimento.container}>
{/* TODO: O Componente paciente precisa ter um campo de busca e buscar na api do backend com limite de 10 pacientes */}
<DropdownSelect
endpoint="patients/find?skip=0&limit=2000" // URL do endpoint
parseData={(data) =>
data.result.list.map((item: any) => ({
id: item.id,
name: item.name,
}))
} // Função que transforma os dados da API no formato { id, name }
onSelect={(selectedItem) => {
// Atualizar o estado de atendimento para incluir o payer
setAtendimento((prev) => ({
...prev,
related: {
...prev.related,
patient: { id: selectedItem.id, name: selectedItem.name }, // Assumindo que você quer armazenar um objeto { id, name }
},
}));
}} // Define o item selecionado no estado
value={atendimento.related.patient?.id || ""} // Valor atual do dropdown
label="Paciente:" // Rótulo a ser exibido
/>
<DropdownSelect
endpoint="places/find?skip=0&limit=2000&place=true" // URL do endpoint para buscar locais de atendimento
parseData={(data) =>
data.result.list.map((local: any) => ({
id: local.id,
name: local.name,
}))
} // Função para transformar os dados da API no formato { id, name }
onSelect={(selectedItem) => {
// Atualizar o estado de atendimento para incluir o payer
setAtendimento((prev) => ({
...prev,
related: {
...prev.related,
place: { id: selectedItem.id, name: selectedItem.name }, // Assumindo que você quer armazenar um objeto { id, name }
},
}));
}} // Callback para atualizar o formulário com o local selecionado
value={atendimento.related.place?.id || ""} // ID do item selecionado
label="Local de Atendimento:" // Rótulo exibido
/>
</div>
<div className={EstiloNovoAtendimento.container}>
<div className={EstiloNovoAtendimento.organizador}>
<span>Código da guia(opcional)</span>
<input
className={EstiloNovoAtendimento.input300}
type="text"
name="codigo"
placeholder="Digite"
value={atendimento.serviceInfo.guideCode}
onChange={(e) => {
const codigo = e.target.value;
// Atualiza o estado de atendimento com o código da guia
setAtendimento((prev) => ({
...prev,
serviceInfo: {
...prev.serviceInfo,
guideCode: codigo, // Atualiza o guideCode com o valor do input
},
}));
}}
/>
</div>
<div className={EstiloNovoAtendimento.organizador}>
<span>Data</span>
<input
className={EstiloNovoAtendimento.inputData}
type="date"
name="data"
placeholder="dd/mm/yyyy"
value={atendimento.createdAt?.toISOString()}
onChange={(e) => {
const data = e.target.value;
setAtendimento((prev) => ({
...prev,
serviceInfo: {
...prev.serviceInfo,
date: data,
},
}));
}}
/>
</div>
</div>
</form>
{/* deveria ficar aqui */}
<div className={EstiloNovoAtendimento.divbutton}>
<button
type="button" // tipo "button" para evitar o envio do formulário
onClick={handleAdicionarProcedimento} // Chama a função ao clicar
className={EstiloNovoAtendimento.buttonProcedimento}
>
Adicionar Procedimento
</button>
</div>
{/* Segunda barra gráfica com funcionalidade ajustada */}
<div className={EstiloNovoAtendimento.divValores2}>
<div className={EstiloNovoAtendimento.inputProcedimentos}>
{[
atendimento.paymentInfo.info.totalProcedures.expected,
atendimento.paymentInfo.info.totalProcedures.glossed,
atendimento.paymentInfo.info.totalProcedures.lost,
atendimento.paymentInfo.info.totalProcedures.paid,
].map((valor, index) => (
<input
key={index}
type="number"
value={valor.toFixed(0)} // Mostra a contagem
placeholder="0"
readOnly // Barra apenas visual, não editável
style={{ color: CoresInput[index] }} // Cor correspondente à respectiva
/>
))}
</div>
<div className={EstiloNovoAtendimento.divBarraGrafico}>
<div className={EstiloNovoAtendimento.divTotal}>
<img
src="/procedimento.png"
alt=""
className={EstiloNovoAtendimento.icone}
/>
<label className={EstiloNovoAtendimento.labelProcedimento}>
<label>{atendimento.procedures.length}</label> Procedimento
</label>
</div>
<div className={EstiloNovoAtendimento.graficoRepresentativo}>
<div className={EstiloNovoAtendimento.barraFixa}>
{[
atendimento.paymentInfo.info.totalProcedures.expected,
atendimento.paymentInfo.info.totalProcedures.glossed,
atendimento.paymentInfo.info.totalProcedures.lost,
atendimento.paymentInfo.info.totalProcedures.paid,
].map((valor, index) => (
<div
key={`grafico-segunda-${index}`}
style={{
backgroundColor: CoresInput[index],
width: `${(valor / atendimento.procedures.length) * 100}%`, // Ajuste a largura da barra conforme a contagem
height: "100%",
}}
/>
))}
</div>
</div>
{/* Botão de expandir/recolher */}
<div
className={EstiloNovoAtendimento.expandirProcedimentos}
onClick={alternarCard}
>
<img
src="/setaBaixo.png"
alt="Expandir/recolher"
className={EstiloNovoAtendimento.iconSeta}
/>
<label className={EstiloNovoAtendimento.labelExpandir}>
{mostrarCard ? "Recolher" : "Expandir"}
</label>
</div>
</div>
</div>
{/* Renderiza o Card apenas se mostrarCard for true */}
{mostrarCard && (
<>
{/* Renderiza os procedimentos adicionados */}
{atendimento.procedures.map((proc, index) => (
<CardProcedimento
key={"procedure_" + proc.id}
index={index} // Usando o índice corretamente
procedure={proc}
updateProcedimento={updateProcedimento}
handleExcluir={handleExcluirProcedimento}
/>
))}
</>
)}
<Anexo
attachments={atendimento.attachments || []}
setAttachments={(novosAnexos) => {
setAtendimento((prev) => ({
...prev,
attachments: novosAnexos,
}));
}}
/>
<div className={EstiloNovoAtendimento.divbutton}>
<button
className={EstiloNovoAtendimento.buttonSalvar}
disabled={loading}
onClick={enviarDadosAtendimento}
>
{loading ? botaoSalvar : "Salvar"}
</button>
</div>
</div>
);
};
export default NovoAtendimento;