forked from RiseUP/riseup_squad_03
refactor(principal): remove legenda global do AppShell
This commit is contained in:
@@ -145,7 +145,7 @@ function resolveRoute(pathname, navigate) {
|
|||||||
if (pathname === '/laudos') {
|
if (pathname === '/laudos') {
|
||||||
return {
|
return {
|
||||||
element: <ReportsPage navigate={navigate} />,
|
element: <ReportsPage navigate={navigate} />,
|
||||||
title: 'Laudos',
|
title: 'Relatorios medicos',
|
||||||
withShell: true,
|
withShell: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ import { useEffect, useMemo, useState } from 'react'
|
|||||||
|
|
||||||
import { profileRepository } from '../repositories/profileRepository.js'
|
import { profileRepository } from '../repositories/profileRepository.js'
|
||||||
import { BrandLogo } from './Brand.jsx'
|
import { BrandLogo } from './Brand.jsx'
|
||||||
import { FeatureLegend } from './FeatureState.jsx'
|
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ href: '/inicio', label: 'Painel', icon: 'pulse', activePaths: ['/inicio', '/home', '/dashboard'] },
|
{ href: '/inicio', label: 'Painel', icon: 'pulse', activePaths: ['/inicio', '/home', '/dashboard'] },
|
||||||
{ href: '/agenda', label: 'Agenda', icon: 'calendar' },
|
{ href: '/agenda', label: 'Agenda', icon: 'calendar' },
|
||||||
{ href: '/pacientes', label: 'Pacientes', icon: 'users', exact: true },
|
{ href: '/pacientes', label: 'Pacientes', icon: 'users', exact: true },
|
||||||
{ href: '/prontuario', label: 'Prontuario', icon: 'file' },
|
{ href: '/prontuario', label: 'Prontuario', icon: 'file' },
|
||||||
{ href: '/laudos', label: 'Laudos', icon: 'clipboard' },
|
{ href: '/laudos', label: 'Relatorios medicos', icon: 'clipboard' },
|
||||||
{
|
{
|
||||||
href: '/camunicacao',
|
href: '/camunicacao',
|
||||||
label: 'Comunicacao',
|
label: 'Comunicacao',
|
||||||
@@ -26,7 +25,7 @@ const titles = {
|
|||||||
'/dashboard': 'Painel',
|
'/dashboard': 'Painel',
|
||||||
'/agenda': 'Agenda',
|
'/agenda': 'Agenda',
|
||||||
'/consultas': 'Consultas',
|
'/consultas': 'Consultas',
|
||||||
'/laudos': 'Laudos',
|
'/laudos': 'Relatorios medicos',
|
||||||
'/pacientes': 'Pacientes',
|
'/pacientes': 'Pacientes',
|
||||||
'/prontuario': 'Prontuario',
|
'/prontuario': 'Prontuario',
|
||||||
'/camunicacao': 'Comunicacao',
|
'/camunicacao': 'Comunicacao',
|
||||||
@@ -198,9 +197,6 @@ export function AppShell({ children, currentPath, navigate, routeTitle }) {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className="w-full px-4 py-6 md:px-8 md:py-8" id="app-content">
|
<main className="w-full px-4 py-6 md:px-8 md:py-8" id="app-content">
|
||||||
<div className="mb-6">
|
|
||||||
<FeatureLegend />
|
|
||||||
</div>
|
|
||||||
<div className="sr-only" aria-live="polite">
|
<div className="sr-only" aria-live="polite">
|
||||||
{pageTitle}
|
{pageTitle}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,72 +2,60 @@ export const reportMapper = {
|
|||||||
toUi(apiData) {
|
toUi(apiData) {
|
||||||
if (!apiData) return null
|
if (!apiData) return null
|
||||||
|
|
||||||
const patient = apiData.patient || apiData.paciente || apiData.patients || {}
|
|
||||||
const doctor = apiData.doctor || apiData.medico || apiData.professional || apiData.doctors || {}
|
|
||||||
const createdAt = apiData.created_at || apiData.createdAt || apiData.data_criacao || apiData.date
|
|
||||||
const status = normalizeStatus(apiData.status || apiData.situacao)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: String(apiData.id || apiData.report_id || apiData.laudo_id),
|
id: String(apiData.id || ''),
|
||||||
patientId: apiData.patientId || apiData.patient_id || apiData.paciente_id || patient.id || '',
|
orderNumber: apiData.order_number || '',
|
||||||
patient: apiData.patientName || apiData.patient_name || patient.full_name || patient.nome || patient.name || 'Paciente',
|
patientId: apiData.patient_id || '',
|
||||||
date: createdAt ? new Date(createdAt).toLocaleDateString('pt-BR') : 'Sem data',
|
status: normalizeStatus(apiData.status),
|
||||||
doctor: apiData.doctorName || apiData.doctor_name || apiData.medico_nome || doctor.name || doctor.nome || 'Medico(a)',
|
exam: apiData.exam || '',
|
||||||
author: apiData.author || apiData.autor || doctor.name || doctor.nome || 'Medico(a)',
|
requestedBy: apiData.requested_by || '',
|
||||||
type: apiData.type || apiData.report_type || apiData.tipo || apiData.tipo_laudo || 'Laudo medico',
|
cidCode: apiData.cid_code || '',
|
||||||
status,
|
diagnosis: apiData.diagnosis || '',
|
||||||
content: apiData.content || apiData.conteudo || apiData.text || '',
|
conclusion: apiData.conclusion || '',
|
||||||
cid: apiData.cid || '',
|
contentHtml: apiData.content_html || '',
|
||||||
tags: apiData.tags || [],
|
contentJson: apiData.content_json ?? null,
|
||||||
verified: apiData.verified ?? apiData.verificado ?? status !== 'rascunho',
|
hideDate: Boolean(apiData.hide_date),
|
||||||
showDate: apiData.showDate ?? apiData.exibir_data ?? true,
|
hideSignature: Boolean(apiData.hide_signature),
|
||||||
signDigital: apiData.signDigital ?? apiData.assinatura_digital ?? true,
|
dueAt: apiData.due_at || '',
|
||||||
versions: normalizeVersions(apiData.versions || apiData.versoes),
|
createdBy: apiData.created_by || '',
|
||||||
|
updatedBy: apiData.updated_by || '',
|
||||||
|
createdAt: apiData.created_at || '',
|
||||||
|
updatedAt: apiData.updated_at || '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
toApi(uiData, dialect = 'api') {
|
toApi(uiData) {
|
||||||
if (dialect === 'supabase') {
|
return cleanPayload({
|
||||||
return {
|
|
||||||
patient_id: uiData.patientId,
|
|
||||||
report_type: uiData.type,
|
|
||||||
content: uiData.content,
|
|
||||||
status: uiData.status,
|
|
||||||
cid: uiData.cid || null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
patient_id: uiData.patientId,
|
patient_id: uiData.patientId,
|
||||||
paciente_id: uiData.patientId,
|
status: normalizeApiStatus(uiData.status),
|
||||||
report_type: uiData.type,
|
exam: emptyToUndefined(uiData.exam),
|
||||||
tipo: uiData.type,
|
requested_by: emptyToUndefined(uiData.requestedBy),
|
||||||
content: uiData.content,
|
cid_code: emptyToUndefined(uiData.cidCode),
|
||||||
conteudo: uiData.content,
|
diagnosis: emptyToUndefined(uiData.diagnosis),
|
||||||
status: uiData.status,
|
conclusion: emptyToUndefined(uiData.conclusion),
|
||||||
cid: uiData.cid || null,
|
content_html: emptyToUndefined(uiData.contentHtml),
|
||||||
}
|
content_json: uiData.contentJson === undefined ? undefined : uiData.contentJson,
|
||||||
|
hide_date: Boolean(uiData.hideDate),
|
||||||
|
hide_signature: Boolean(uiData.hideSignature),
|
||||||
|
due_at: emptyToUndefined(uiData.dueAt),
|
||||||
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeStatus(status) {
|
function normalizeStatus(status) {
|
||||||
if (!status) return 'rascunho'
|
return status === 'draft' ? 'draft' : 'draft'
|
||||||
|
|
||||||
const normalized = String(status).toLowerCase()
|
|
||||||
if (['finalizado', 'liberado', 'assinado'].includes(normalized)) return 'finalizado'
|
|
||||||
if (['enviado', 'entregue'].includes(normalized)) return 'enviado'
|
|
||||||
return 'rascunho'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeVersions(versions) {
|
function normalizeApiStatus(status) {
|
||||||
if (Array.isArray(versions) && versions.length) return versions
|
return status === 'draft' ? 'draft' : 'draft'
|
||||||
|
}
|
||||||
return [
|
|
||||||
{
|
function emptyToUndefined(value) {
|
||||||
version: 1,
|
return value === '' || value === null ? undefined : value
|
||||||
action: 'Criado',
|
}
|
||||||
user: 'Sistema',
|
|
||||||
summary: 'Registro importado da API',
|
function cleanPayload(payload) {
|
||||||
},
|
return Object.fromEntries(
|
||||||
]
|
Object.entries(payload).filter(([, value]) => value !== undefined),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import {
|
|||||||
} from 'date-fns'
|
} from 'date-fns'
|
||||||
import { ptBR } from 'date-fns/locale'
|
import { ptBR } from 'date-fns/locale'
|
||||||
|
|
||||||
import { FeatureBadge } from '../components/FeatureState.jsx'
|
|
||||||
import { featurePanelClass } from '../components/featureStateStyles.js'
|
|
||||||
import { AgendaDailyView } from '../components/calendar/AgendaDailyView.jsx'
|
import { AgendaDailyView } from '../components/calendar/AgendaDailyView.jsx'
|
||||||
import { AgendaWeeklyView } from '../components/calendar/AgendaWeeklyView.jsx'
|
import { AgendaWeeklyView } from '../components/calendar/AgendaWeeklyView.jsx'
|
||||||
import { AgendaMonthlyView } from '../components/calendar/AgendaMonthlyView.jsx'
|
import { AgendaMonthlyView } from '../components/calendar/AgendaMonthlyView.jsx'
|
||||||
@@ -131,7 +129,7 @@ export function AgendaPage({ navigate }) {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{error ? (
|
{error ? (
|
||||||
<section className={`rounded-2xl border bg-[#262626] p-5 shadow-[0_1px_3px_rgba(0,0,0,0.2)] ${featurePanelClass('live')}`}>
|
<section className="rounded-2xl border border-[#404040] bg-[#262626] p-5 shadow-[0_1px_3px_rgba(0,0,0,0.2)]">
|
||||||
<div className="rounded-xl border border-dashed border-[#7f1d1d] bg-[#2a1111] p-6">
|
<div className="rounded-xl border border-dashed border-[#7f1d1d] bg-[#2a1111] p-6">
|
||||||
<h2 className="text-base font-bold text-[#fecaca]">Nao foi possivel liberar a agenda</h2>
|
<h2 className="text-base font-bold text-[#fecaca]">Nao foi possivel liberar a agenda</h2>
|
||||||
<p className="mt-2 text-sm leading-6 text-[#fca5a5]">{error}</p>
|
<p className="mt-2 text-sm leading-6 text-[#fca5a5]">{error}</p>
|
||||||
@@ -142,14 +140,13 @@ export function AgendaPage({ navigate }) {
|
|||||||
</section>
|
</section>
|
||||||
) : (
|
) : (
|
||||||
<section className="grid gap-6 xl:grid-cols-1">
|
<section className="grid gap-6 xl:grid-cols-1">
|
||||||
<div className={`rounded-2xl border bg-[#262626] p-5 shadow-[0_1px_3px_rgba(0,0,0,0.2)] ${featurePanelClass('live')}`}>
|
<div className="rounded-2xl border border-[#404040] bg-[#262626] p-5 shadow-[0_1px_3px_rgba(0,0,0,0.2)]">
|
||||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
<div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<h2 className="text-base font-bold leading-6 text-[#e5e5e5]">
|
<h2 className="text-base font-bold leading-6 text-[#e5e5e5]">
|
||||||
{format(baseDate, "EEEE, dd 'de' MMMM", { locale: ptBR })}
|
{format(baseDate, "EEEE, dd 'de' MMMM", { locale: ptBR })}
|
||||||
</h2>
|
</h2>
|
||||||
<FeatureBadge status="live" />
|
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-1 text-sm leading-5 text-[#a3a3a3]">
|
<p className="mt-1 text-sm leading-5 text-[#a3a3a3]">
|
||||||
Visualização: {activeView.toLowerCase()} | {visibleAppointments.length} registros visíveis
|
Visualização: {activeView.toLowerCase()} | {visibleAppointments.length} registros visíveis
|
||||||
|
|||||||
@@ -301,43 +301,43 @@ async function deletePatient(patientId) {
|
|||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="overflow-x-auto rounded-lg border border-[#404040]">
|
<div className="overflow-x-auto rounded-lg border border-[#404040]">
|
||||||
<table className="w-full whitespace-nowrap text-left text-sm">
|
<table className="w-full min-w-full table-fixed text-left text-sm">
|
||||||
<thead className="bg-[#171717] text-xs font-semibold uppercase text-[#a3a3a3]">
|
<thead className="bg-[#171717] text-xs font-semibold uppercase text-[#a3a3a3]">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-6 py-4">Nome</th>
|
<th className="w-[24%] px-6 py-4">Nome</th>
|
||||||
<th className="px-6 py-4">Telefone</th>
|
<th className="w-[14%] px-6 py-4">Telefone</th>
|
||||||
<th className="px-6 py-4">Cidade</th>
|
<th className="w-[12%] px-6 py-4">Cidade</th>
|
||||||
<th className="px-6 py-4">Estado</th>
|
<th className="w-[8%] px-6 py-4">Estado</th>
|
||||||
<th className="px-6 py-4">Ultimo atendimento</th>
|
<th className="w-[16%] px-6 py-4">Ultimo atendimento</th>
|
||||||
<th className="px-6 py-4">Proximo atendimento</th>
|
<th className="w-[18%] px-6 py-4">Proximo atendimento</th>
|
||||||
<th className="px-6 py-4 text-right">Acoes</th>
|
<th className="sticky right-0 w-[8.5rem] bg-[#171717] px-6 py-4 text-right">Acoes</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-[#404040] bg-[#262626]">
|
<tbody className="divide-y divide-[#404040] bg-[#262626]">
|
||||||
{paginatedPatients.length ? (
|
{paginatedPatients.length ? (
|
||||||
paginatedPatients.map((patient) => (
|
paginatedPatients.map((patient) => (
|
||||||
<tr className="transition hover:bg-[#303030]" key={patient.id}>
|
<tr className="transition hover:bg-[#303030]" key={patient.id}>
|
||||||
<td className="px-6 py-4">
|
<td className="px-6 py-4 align-top">
|
||||||
<button className="flex items-center gap-3 text-left" onClick={() => openDetail(patient)} type="button">
|
<button className="flex items-center gap-3 text-left" onClick={() => openDetail(patient)} type="button">
|
||||||
<span className="grid size-8 place-items-center rounded-full bg-[#333333] text-xs font-bold text-[#3b82f6]">
|
<span className="grid size-8 shrink-0 place-items-center rounded-full bg-[#333333] text-xs font-bold text-[#3b82f6]">
|
||||||
{patient.name.charAt(0)}
|
{patient.name.charAt(0)}
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span className="min-w-0">
|
||||||
<span className="block font-medium text-[#e5e5e5] transition hover:text-[#3b82f6]">
|
<span className="block whitespace-normal break-words font-medium text-[#e5e5e5] transition hover:text-[#3b82f6]">
|
||||||
{patient.name}
|
{patient.name}
|
||||||
</span>
|
</span>
|
||||||
<span className="mt-0.5 block text-xs text-[#a3a3a3]">
|
<span className="mt-0.5 block whitespace-normal break-words text-xs text-[#a3a3a3]">
|
||||||
{patient.insurance || 'Sem convenio'} {patient.vip ? ' | VIP' : ''}
|
{patient.insurance || 'Sem convenio'} {patient.vip ? ' | VIP' : ''}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 text-[#a3a3a3]">{patient.phone}</td>
|
<td className="px-6 py-4 align-top whitespace-normal break-words text-[#a3a3a3]">{patient.phone}</td>
|
||||||
<td className="px-6 py-4 text-[#a3a3a3]">{patient.city}</td>
|
<td className="px-6 py-4 align-top whitespace-normal break-words text-[#a3a3a3]">{patient.city}</td>
|
||||||
<td className="px-6 py-4 text-[#a3a3a3]">{patient.state}</td>
|
<td className="px-6 py-4 align-top text-[#a3a3a3]">{patient.state}</td>
|
||||||
<td className="px-6 py-4 text-[#a3a3a3]">{patient.lastVisit || 'Ainda nao houve atendimento'}</td>
|
<td className="px-6 py-4 align-top whitespace-normal break-words text-[#a3a3a3]">{patient.lastVisit || 'Ainda nao houve atendimento'}</td>
|
||||||
<td className="px-6 py-4 text-[#a3a3a3]">{patient.nextVisit || 'Nenhum atendimento agendado'}</td>
|
<td className="px-6 py-4 align-top whitespace-normal break-words text-[#a3a3a3]">{patient.nextVisit || 'Nenhum atendimento agendado'}</td>
|
||||||
<td className="relative px-6 py-4 text-right">
|
<td className="relative sticky right-0 bg-[#262626] px-6 py-4 text-right shadow-[-10px_0_12px_-12px_rgba(0,0,0,0.75)]">
|
||||||
<button
|
<button
|
||||||
aria-label={`Acoes de ${patient.name}`}
|
aria-label={`Acoes de ${patient.name}`}
|
||||||
className="rounded p-1 text-[#a3a3a3] transition hover:bg-[#333333] hover:text-[#e5e5e5]"
|
className="rounded p-1 text-[#a3a3a3] transition hover:bg-[#333333] hover:text-[#e5e5e5]"
|
||||||
@@ -779,7 +779,7 @@ function PatientVisits({ navigate, patient }) {
|
|||||||
<div className="grid gap-3">
|
<div className="grid gap-3">
|
||||||
{[
|
{[
|
||||||
{ date: patient.nextVisit, status: 'Agendada', description: `Retorno para ${patient.condition}` },
|
{ date: patient.nextVisit, status: 'Agendada', description: `Retorno para ${patient.condition}` },
|
||||||
{ date: patient.lastVisit, status: 'Finalizada', description: 'Consulta registrada no historico local.' },
|
{ date: patient.lastVisit, status: 'Finalizada', description: 'Consulta registrada no historico do paciente.' },
|
||||||
].map((visit) => (
|
].map((visit) => (
|
||||||
<div className="rounded-xl border border-[#404040] bg-[#171717] p-4" key={`${visit.date}-${visit.status}`}>
|
<div className="rounded-xl border border-[#404040] bg-[#171717] p-4" key={`${visit.date}-${visit.status}`}>
|
||||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||||
@@ -814,7 +814,7 @@ function PatientDocuments({ patient }) {
|
|||||||
{patient.exams.map((exam) => (
|
{patient.exams.map((exam) => (
|
||||||
<div className="rounded-xl border border-[#404040] bg-[#171717] p-4" key={exam}>
|
<div className="rounded-xl border border-[#404040] bg-[#171717] p-4" key={exam}>
|
||||||
<p className="font-semibold text-[#f5f5f5]">{exam}</p>
|
<p className="font-semibold text-[#f5f5f5]">{exam}</p>
|
||||||
<p className="mt-2 text-sm text-[#a3a3a3]">Pendente de revisão mockada.</p>
|
<p className="mt-2 text-sm text-[#a3a3a3]">Pendente de revisão.</p>
|
||||||
<span className="mt-4 inline-flex rounded bg-amber-500/20 px-2.5 py-1 text-xs font-bold text-amber-400">
|
<span className="mt-4 inline-flex rounded bg-amber-500/20 px-2.5 py-1 text-xs font-bold text-amber-400">
|
||||||
A revisar
|
A revisar
|
||||||
</span>
|
</span>
|
||||||
@@ -1276,4 +1276,4 @@ function maskCEPInput(event) {
|
|||||||
.replace(/\D/g, '')
|
.replace(/\D/g, '')
|
||||||
.replace(/(\d{5})(\d)/, '$1-$2')
|
.replace(/(\d{5})(\d)/, '$1-$2')
|
||||||
.replace(/(-\d{3})\d+?$/, '$1')
|
.replace(/(-\d{3})\d+?$/, '$1')
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,142 +1,64 @@
|
|||||||
import { apiConfig, apiEndpoint, getAuthenticatedHeaders } from '../config/api.js'
|
import { apiConfig, getAuthenticatedHeaders } from '../config/api.js'
|
||||||
import { reportMapper } from '../mappers/reportMapper.js'
|
import { reportMapper } from '../mappers/reportMapper.js'
|
||||||
import { fetchJsonWithFallback, normalizeCollection, normalizeItem } from './repositoryUtils.js'
|
import { getResponseError, normalizeItem } from './repositoryUtils.js'
|
||||||
|
|
||||||
export const reportRepository = {
|
export const reportRepository = {
|
||||||
async getInitialReports() {
|
async getInitialReports(filters = {}) {
|
||||||
const data = await fetchJsonWithFallback(
|
const query = new URLSearchParams()
|
||||||
[
|
query.set('select', '*')
|
||||||
{
|
query.set('order', filters.order || 'created_at.desc')
|
||||||
url: apiEndpoint('/reports'),
|
|
||||||
options: { headers: getAuthenticatedHeaders() },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${apiConfig.restUrl}/reports?select=*,patients(full_name),doctors(name)`,
|
|
||||||
options: { headers: getAuthenticatedHeaders() },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'Falha ao buscar laudos da API.',
|
|
||||||
)
|
|
||||||
|
|
||||||
return normalizeCollection(data, ['reports', 'relatorios', 'laudos', 'data']).map(reportMapper.toUi)
|
if (filters.patientId) {
|
||||||
|
query.set('patient_id', `eq.${filters.patientId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters.status) {
|
||||||
|
query.set('status', `eq.${filters.status}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters.createdBy) {
|
||||||
|
query.set('created_by', `eq.${filters.createdBy}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${apiConfig.restUrl}/reports?${query.toString()}`, {
|
||||||
|
headers: getAuthenticatedHeaders(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(await getResponseError(response, 'Falha ao buscar relatorios medicos.'))
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
return (Array.isArray(data) ? data : []).map(reportMapper.toUi)
|
||||||
},
|
},
|
||||||
|
|
||||||
async create(uiData) {
|
async create(uiData) {
|
||||||
const data = await fetchJsonWithFallback(
|
const response = await fetch(`${apiConfig.restUrl}/reports`, {
|
||||||
[
|
method: 'POST',
|
||||||
{
|
headers: getAuthenticatedHeaders({ Prefer: 'return=representation' }),
|
||||||
url: apiEndpoint('/reports'),
|
body: JSON.stringify(reportMapper.toApi(uiData)),
|
||||||
options: {
|
})
|
||||||
method: 'POST',
|
|
||||||
headers: getAuthenticatedHeaders(),
|
|
||||||
body: JSON.stringify(reportMapper.toApi(uiData)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${apiConfig.restUrl}/reports`,
|
|
||||||
options: {
|
|
||||||
method: 'POST',
|
|
||||||
headers: getAuthenticatedHeaders({ Prefer: 'return=representation' }),
|
|
||||||
body: JSON.stringify(reportMapper.toApi(uiData, 'supabase')),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'Falha ao salvar laudo.',
|
|
||||||
)
|
|
||||||
|
|
||||||
return reportMapper.toUi(normalizeItem(data, ['report', 'relatorio', 'laudo', 'data']))
|
if (!response.ok) {
|
||||||
|
throw new Error(await getResponseError(response, 'Falha ao criar relatorio medico.'))
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
return reportMapper.toUi(normalizeItem(data))
|
||||||
},
|
},
|
||||||
|
|
||||||
async update(id, uiData) {
|
async update(id, uiData) {
|
||||||
const data = await fetchJsonWithFallback(
|
const response = await fetch(`${apiConfig.restUrl}/reports?id=eq.${id}`, {
|
||||||
[
|
method: 'PATCH',
|
||||||
{
|
headers: getAuthenticatedHeaders({ Prefer: 'return=representation' }),
|
||||||
url: apiEndpoint(`/reports/${id}`),
|
body: JSON.stringify(reportMapper.toApi(uiData)),
|
||||||
options: {
|
})
|
||||||
method: 'PATCH',
|
|
||||||
headers: getAuthenticatedHeaders(),
|
|
||||||
body: JSON.stringify(reportMapper.toApi({ ...uiData, id })),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: apiEndpoint('/reports'),
|
|
||||||
options: {
|
|
||||||
method: 'PATCH',
|
|
||||||
headers: getAuthenticatedHeaders(),
|
|
||||||
body: JSON.stringify({ id, ...reportMapper.toApi(uiData) }),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${apiConfig.restUrl}/reports?id=eq.${id}`,
|
|
||||||
options: {
|
|
||||||
method: 'PATCH',
|
|
||||||
headers: getAuthenticatedHeaders({ Prefer: 'return=representation' }),
|
|
||||||
body: JSON.stringify(reportMapper.toApi(uiData, 'supabase')),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'Falha ao atualizar o laudo.',
|
|
||||||
)
|
|
||||||
|
|
||||||
return reportMapper.toUi(normalizeItem(data, ['report', 'relatorio', 'laudo', 'data']))
|
if (!response.ok) {
|
||||||
},
|
throw new Error(await getResponseError(response, 'Falha ao atualizar relatorio medico.'))
|
||||||
|
}
|
||||||
|
|
||||||
getTemplates() {
|
const data = await response.json()
|
||||||
return [
|
return reportMapper.toUi(normalizeItem(data))
|
||||||
{
|
|
||||||
id: 't1',
|
|
||||||
name: 'Atestado Medico Padrao',
|
|
||||||
type: 'Atestado Medico',
|
|
||||||
description: 'Atestado simples para repouso, consulta e CID.',
|
|
||||||
content:
|
|
||||||
'Atesto para os devidos fins que o(a) paciente [NOME DO PACIENTE] esteve em consulta medica nesta data, necessitando de [DIAS] dias de repouso por motivo de saude (CID: [CODIGO]).',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 't2',
|
|
||||||
name: 'Encaminhamento Especializado',
|
|
||||||
type: 'Encaminhamento',
|
|
||||||
description: 'Encaminhamento para avaliacao de especialidade.',
|
|
||||||
content:
|
|
||||||
'Encaminho o(a) paciente [NOME DO PACIENTE] para avaliacao da especialidade de [ESPECIALIDADE] devido ao quadro clinico de [SINTOMAS/DIAGNOSTICO PREVIO].\n\nConduta mantida ate o momento: [MEDICACOES]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 't3',
|
|
||||||
name: 'Laudo de Evolucao Diaria',
|
|
||||||
type: 'Evolucao Clinica',
|
|
||||||
description: 'Modelo para evolucao clinica diaria.',
|
|
||||||
content:
|
|
||||||
'Paciente evolui [BEM/MAL], [COM/SEM] queixas no momento.\nSinais vitais: PA [VALOR], FC [VALOR] bpm, SatO2 [VALOR]%.\nExame fisico: [DESCRICAO].\nConduta: [MANTER/ALTERAR TRATAMENTO OPCOES].',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 't4',
|
|
||||||
name: 'Receituario de Uso Continuo',
|
|
||||||
type: 'Receituario Fixado',
|
|
||||||
description: 'Lista de medicamentos de uso continuo.',
|
|
||||||
content:
|
|
||||||
'Uso continuo:\n1. [MEDICAMENTO] - [DOSE] - Tomar [POSOLOGIA]\n2. [MEDICAMENTO] - [DOSE] - Tomar [POSOLOGIA]',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
getAdminUsers() {
|
|
||||||
return ['Dr. Henrique Cardoso', 'Dra. Marina Lopes', 'Dra. Ana Silva']
|
|
||||||
},
|
|
||||||
|
|
||||||
getCurrentUser() {
|
|
||||||
return 'Dr. Henrique Cardoso'
|
|
||||||
},
|
|
||||||
|
|
||||||
getDoctors() {
|
|
||||||
return ['Dr. Henrique Cardoso', 'Dra. Marina Lopes', 'Dra. Ana Silva', 'Dr. Roberto Santos']
|
|
||||||
},
|
|
||||||
|
|
||||||
getReportTypes() {
|
|
||||||
return [
|
|
||||||
'Atestado Medico',
|
|
||||||
'Encaminhamento',
|
|
||||||
'Evolucao Clinica',
|
|
||||||
'Receituario Fixado',
|
|
||||||
'Laudo de Procedimento',
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user