modo-claro
modified: src/hooks/useAgenda.js modified: src/index.css modified: src/main.jsx modified: src/mappers/reportMapper.js modified: src/pages/AgendaPage.jsx modified: src/pages/AuthPages.jsx modified: src/pages/MedicalRecordsPage.jsx modified: src/pages/PatientsPage.jsx modified: src/pages/ReportsPage.jsx modified: src/pages/SettingsPage.jsx modified: src/repositories/analyticsRepository.js modified: src/repositories/authRepository.js modified: src/repositories/patientRepository.js modified: src/repositories/reportRepository.js modified: src/repositories/repositoryUtils.js new file: src/utils/theme.js new file: vercel.json
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import { hasCapability, normalizeRole } from '../config/permissions.js'
|
||||
import { hasCapability } from '../config/permissions.js'
|
||||
import { patientRepository } from '../repositories/patientRepository.js'
|
||||
import { professionalRepository } from '../repositories/professionalRepository.js'
|
||||
import { profileRepository } from '../repositories/profileRepository.js'
|
||||
const ITEMS_PER_PAGE = 25
|
||||
|
||||
const darkInput =
|
||||
@@ -70,11 +68,11 @@ export function PatientsPage({ navigate, role }) {
|
||||
const [page, setPage] = useState(1)
|
||||
|
||||
useEffect(() => {
|
||||
buildPatientRows(role)
|
||||
buildPatientRows()
|
||||
.then((data) => setRows(data))
|
||||
.catch((err) => setError(err.message))
|
||||
.finally(() => setLoading(false))
|
||||
}, [role])
|
||||
}, [])
|
||||
|
||||
const editingPatient = rows.find((patient) => patient.id === editingId)
|
||||
const hasAdvancedFilters = city || state || ageMin || ageMax || lastVisitSince
|
||||
@@ -180,13 +178,13 @@ export function PatientsPage({ navigate, role }) {
|
||||
|
||||
try {
|
||||
if (isNew) {
|
||||
const [created] = await patientRepository.create(patient)
|
||||
const created = normalizeCreatedPatient(await patientRepository.create(patient))
|
||||
const newRow = {
|
||||
...patient,
|
||||
id: created.id,
|
||||
detailId: created.id,
|
||||
name: created.full_name || patient.name,
|
||||
phone: created.phone_mobile || patient.phone,
|
||||
id: created?.id || patient.id,
|
||||
detailId: created?.id || patient.detailId || patient.id,
|
||||
name: created?.full_name || created?.name || patient.name,
|
||||
phone: created?.phone_mobile || created?.phone || patient.phone,
|
||||
}
|
||||
setRows((currentRows) => [newRow, ...currentRows])
|
||||
} else {
|
||||
@@ -368,14 +366,14 @@ export function PatientsPage({ navigate, role }) {
|
||||
{patient.name}
|
||||
</span>
|
||||
<span className="mt-0.5 block whitespace-normal break-words text-xs text-[#a3a3a3]">
|
||||
{patient.insurance || 'Sem convenio'} {patient.vip ? ' | VIP' : ''}
|
||||
{patient.insurance || missingValue('Convênio')} {patient.vip ? ' | VIP' : ''}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</td>
|
||||
<td className="px-6 py-4 align-top whitespace-normal break-words text-[#a3a3a3]">{patient.phone}</td>
|
||||
<td className="px-6 py-4 align-top whitespace-normal break-words text-[#a3a3a3]">{patient.city}</td>
|
||||
<td className="px-6 py-4 align-top text-[#a3a3a3]">{patient.state}</td>
|
||||
<td className="px-6 py-4 align-top whitespace-normal break-words text-[#a3a3a3]">{patient.phone || missingValue('Telefone')}</td>
|
||||
<td className="px-6 py-4 align-top whitespace-normal break-words text-[#a3a3a3]">{patient.city || missingValue('Cidade')}</td>
|
||||
<td className="px-6 py-4 align-top text-[#a3a3a3]">{patient.state || missingValue('Estado')}</td>
|
||||
<td className="px-6 py-4 align-top whitespace-normal break-words text-[#a3a3a3]">{patient.lastVisit || 'Ainda não houve atendimento'}</td>
|
||||
<td className="px-6 py-4 align-top whitespace-normal break-words text-[#a3a3a3]">{patient.nextVisit || 'Nenhum atendimento agendado'}</td>
|
||||
<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)]">
|
||||
@@ -398,7 +396,7 @@ export function PatientsPage({ navigate, role }) {
|
||||
onClick={() => setOpenMenuId(null)}
|
||||
type="button"
|
||||
/>
|
||||
<div className="absolute right-4 top-12 z-50 w-48 rounded-lg border border-[#404040] bg-[#303030] py-1 text-left shadow-lg">
|
||||
<div className="absolute right-4 top-12 z-50 w-48 rounded-md border border-[#404040] bg-[#262626] p-1 text-left shadow-lg">
|
||||
<ActionItem icon="file" label="Ver detalhes" onClick={() => openDetail(patient)} />
|
||||
{canEditPatients ? <ActionItem icon="edit" label="Editar" onClick={() => openForm(patient.id)} /> : null}
|
||||
<ActionItem
|
||||
@@ -870,8 +868,8 @@ export function PatientDetailPage({ navigate, patient, role }) {
|
||||
</header>
|
||||
|
||||
<section className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
<SummaryTile label="Idade" value={`${localPatient.age} anos`} />
|
||||
<SummaryTile label="Risco" value={localPatient.risk} tone={riskColor(localPatient.risk)} />
|
||||
<SummaryTile label="Idade" value={localPatient.age ? `${localPatient.age} anos` : missingValue('Idade')} />
|
||||
<SummaryTile label="Risco" value={localPatient.risk || missingValue('Risco')} tone={localPatient.risk ? riskColor(localPatient.risk) : null} />
|
||||
<SummaryTile label="Última consulta" value={localPatient.lastVisit || 'Ainda não houve atendimento'} />
|
||||
<SummaryTile label="Próxima consulta" value={localPatient.nextVisit || 'Nenhum atendimento agendado'} />
|
||||
</section>
|
||||
@@ -1197,11 +1195,15 @@ function InfoRow({ label, value }) {
|
||||
return (
|
||||
<div>
|
||||
<dt className="font-semibold text-[#737373]">{label}</dt>
|
||||
<dd className="mt-1 text-[#e5e5e5]">{value || 'Não informado'}</dd>
|
||||
<dd className="mt-1 text-[#e5e5e5]">{value || missingValue(label)}</dd>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function missingValue(label) {
|
||||
return `${label} não informado`
|
||||
}
|
||||
|
||||
function formatDisplayDate(value) {
|
||||
if (!value) return ''
|
||||
const [year, month, day] = String(value).split('-')
|
||||
@@ -1296,8 +1298,8 @@ function PageButton({ children, disabled, onClick }) {
|
||||
function ActionItem({ danger = false, icon, label, onClick }) {
|
||||
return (
|
||||
<button
|
||||
className={`flex w-full items-center gap-2 px-4 py-2 text-sm transition ${
|
||||
danger ? 'text-[#ef4444] hover:bg-[#ef4444]/10' : 'text-[#e5e5e5] hover:bg-[#333333]'
|
||||
className={`flex w-full items-center gap-2 rounded-sm px-3 py-2 text-left text-sm font-medium transition ${
|
||||
danger ? 'text-[#f87171] hover:bg-[#303030]' : 'text-[#e5e5e5] hover:bg-[#303030]'
|
||||
}`}
|
||||
onClick={onClick}
|
||||
type="button"
|
||||
@@ -1620,22 +1622,13 @@ function PatientIcon({ className = 'size-4', name }) {
|
||||
)
|
||||
}
|
||||
|
||||
async function buildPatientRows(role) {
|
||||
if (normalizeRole(role) !== 'medico') {
|
||||
return patientRepository.getDirectoryRows()
|
||||
}
|
||||
async function buildPatientRows() {
|
||||
return patientRepository.getDirectoryRows()
|
||||
}
|
||||
|
||||
const [profile, professionals] = await Promise.all([
|
||||
profileRepository.getCurrentUserProfile(),
|
||||
professionalRepository.getAll(),
|
||||
])
|
||||
const currentProfessional = professionalRepository.resolveCurrentProfessional(profile, professionals)
|
||||
|
||||
if (!currentProfessional?.id) {
|
||||
throw new Error('Não foi possível vincular o médico logado a um profissional da base.')
|
||||
}
|
||||
|
||||
return patientRepository.getDirectoryRows({ doctorId: currentProfessional.id })
|
||||
function normalizeCreatedPatient(payload) {
|
||||
if (Array.isArray(payload)) return payload[0] || null
|
||||
return payload?.patient || payload?.data || payload?.created || payload || null
|
||||
}
|
||||
|
||||
function uniqueSlug(value, existingIds) {
|
||||
|
||||
Reference in New Issue
Block a user