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:
2026-05-07 05:51:07 -03:00
parent 64d9527318
commit db7a2fe8f5
17 changed files with 669 additions and 121 deletions

View File

@@ -31,6 +31,8 @@ const viewFilters = [
{ label: 'Mês', value: 'Mes' },
]
const appointmentTypeOptions = ['Retorno', 'Primeira consulta', 'Exame', 'Avaliação pre-op']
export function AgendaPage({ navigate }) {
const [modalPatientSearch, setModalPatientSearch] = useState('')
const [modalDoctorSearch, setModalDoctorSearch] = useState('')
@@ -84,29 +86,35 @@ export function AgendaPage({ navigate }) {
),
].sort((a, b) => a.localeCompare(b, 'pt-BR'))
const filteredPatients = (() => {
const query = modalPatientSearch.trim().toLowerCase()
const query = normalizeSearch(modalPatientSearch)
if (!query) return patients
return patients.filter((patient) =>
[patient.name, patient.full_name, patient.nome, patient.cpf, patient.email]
.filter(Boolean)
.join(' ')
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
.includes(query),
)
})()
const filteredProfessionals = (() => {
const query = modalDoctorSearch.trim().toLowerCase()
const query = normalizeSearch(modalDoctorSearch)
if (!query) return professionals
return professionals.filter((professional) =>
[professional.name, professional.email, professional.unit]
.filter(Boolean)
.join(' ')
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
.includes(query),
)
})()
const selectedPatient = patients.find((patient) => String(patient.id) === String(form.patientId))
const selectedProfessional = professionals.find((professional) => String(professional.id) === String(form.professionalId))
return (
<div className="mx-auto flex max-w-[1180px] flex-col gap-8 text-[#e5e5e5]">
@@ -318,23 +326,25 @@ export function AgendaPage({ navigate }) {
<DarkField label="Paciente">
<input
className="mb-2 h-10 rounded-md border border-[#404040] bg-[#303030] px-3 text-sm text-[#e5e5e5] outline-none transition placeholder:text-[#737373] focus:border-[#3b82f6]"
onChange={(event) => setModalPatientSearch(event.target.value)}
className="h-10 rounded-md border border-[#404040] bg-[#303030] px-3 text-sm text-[#e5e5e5] outline-none transition placeholder:text-[#737373] focus:border-[#3b82f6]"
onChange={(event) => {
setModalPatientSearch(event.target.value)
updateForm('patientId', '')
}}
placeholder="Pesquisar paciente"
type="search"
value={modalPatientSearch}
value={modalPatientSearch || getPatientLabel(selectedPatient)}
/>
<SearchResults
emptyText="Nenhum paciente encontrado."
getLabel={getPatientLabel}
items={filteredPatients.slice(0, 6)}
onSelect={(patient) => {
updateForm('patientId', patient.id)
setModalPatientSearch(getPatientLabel(patient))
}}
selectedId={form.patientId}
/>
<select
className="h-11 rounded-md border border-[#404040] bg-[#303030] px-3 text-sm text-[#e5e5e5] outline-none focus:border-[#3b82f6]"
onChange={(event) => updateForm('patientId', event.target.value)}
value={form.patientId}
>
{filteredPatients.map((patient) => (
<option key={patient.id} value={patient.id}>
{patient.name || patient.full_name || patient.nome}
</option>
))}
</select>
</DarkField>
<div className="grid gap-4 sm:grid-cols-2">
@@ -389,33 +399,42 @@ export function AgendaPage({ navigate }) {
) : (
<>
<input
className="mb-2 h-10 rounded-md border border-[#404040] bg-[#303030] px-3 text-sm text-[#e5e5e5] outline-none transition placeholder:text-[#737373] focus:border-[#3b82f6]"
onChange={(event) => setModalDoctorSearch(event.target.value)}
className="h-10 rounded-md border border-[#404040] bg-[#303030] px-3 text-sm text-[#e5e5e5] outline-none transition placeholder:text-[#737373] focus:border-[#3b82f6]"
onChange={(event) => {
setModalDoctorSearch(event.target.value)
updateForm('professionalId', '')
}}
placeholder="Pesquisar médico"
type="search"
value={modalDoctorSearch}
value={modalDoctorSearch || selectedProfessional?.name || ''}
/>
<SearchResults
emptyText="Nenhum médico encontrado."
getDescription={(professional) => professional.unit || professional.email}
getLabel={(professional) => professional.name}
items={filteredProfessionals.slice(0, 6)}
onSelect={(professional) => {
updateForm('professionalId', professional.id)
setModalDoctorSearch(professional.name)
}}
selectedId={form.professionalId}
/>
<select
className="h-11 rounded-md border border-[#404040] bg-[#303030] px-3 text-sm text-[#e5e5e5] outline-none focus:border-[#3b82f6]"
onChange={(event) => updateForm('professionalId', event.target.value)}
value={form.professionalId}
>
{filteredProfessionals.map((professional) => (
<option key={professional.id} value={professional.id}>
{professional.name}
</option>
))}
</select>
</>
)}
</DarkField>
<DarkField label="Tipo de consulta">
<input
<select
className="h-11 rounded-md border border-[#404040] bg-[#303030] px-3 text-sm text-[#e5e5e5] outline-none focus:border-[#3b82f6]"
onChange={(event) => updateForm('type', event.target.value)}
value={form.type}
/>
>
{appointmentTypeOptions.map((type) => (
<option key={type} value={type}>
{type}
</option>
))}
</select>
</DarkField>
<div className="flex flex-wrap justify-end gap-3 pt-2">
@@ -473,3 +492,44 @@ function DarkModal({ children, onClose, open, title }) {
</div>
)
}
function SearchResults({ emptyText, getDescription, getLabel, items, onSelect, selectedId }) {
return (
<div className="max-h-44 overflow-y-auto rounded-md border border-[#404040] bg-[#1f1f1f]">
{items.length ? (
items.map((item) => {
const isSelected = String(item.id) === String(selectedId)
return (
<button
className={`block w-full px-3 py-2 text-left text-sm transition ${
isSelected ? 'bg-[#3b82f6]/20 text-[#e5e5e5]' : 'text-[#a3a3a3] hover:bg-[#303030] hover:text-[#e5e5e5]'
}`}
key={item.id}
onClick={() => onSelect(item)}
type="button"
>
<span className="block font-semibold">{getLabel(item)}</span>
{getDescription?.(item) ? (
<span className="mt-0.5 block text-xs text-[#737373]">{getDescription(item)}</span>
) : null}
</button>
)
})
) : (
<p className="px-3 py-2 text-xs text-[#737373]">{emptyText}</p>
)}
</div>
)
}
function getPatientLabel(patient) {
return patient?.name || patient?.full_name || patient?.nome || ''
}
function normalizeSearch(value) {
return String(value || '')
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.trim()
.toLowerCase()
}