feat(admin-procedimentos): num_faces (dentística) e num_raizes (endodontia) + oculta ARCO
- Dropdown 1-5 de faces visível quando especialidade é Dentística - Dropdown 1-5 de raízes visível quando especialidade é Endodontia - Opção ARCO COMPLETO oculta nos dois casos (não se aplica) - Select de especialidade agora controlado — reseta num_faces/num_raizes ao trocar - DB: ALTER TABLE procedimentos ADD COLUMN num_faces INTEGER, num_raizes INTEGER (executado) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -242,6 +242,8 @@ export interface Procedimento {
|
|||||||
codigoInterno?: string;
|
codigoInterno?: string;
|
||||||
descricao?: string;
|
descricao?: string;
|
||||||
descricao_mobile?: string;
|
descricao_mobile?: string;
|
||||||
|
num_faces?: number;
|
||||||
|
num_raizes?: number;
|
||||||
especialidadeId: string;
|
especialidadeId: string;
|
||||||
especialidadeNome: string;
|
especialidadeNome: string;
|
||||||
valorParticular: number;
|
valorParticular: number;
|
||||||
|
|||||||
@@ -463,6 +463,8 @@ export const ProcedimentosView: React.FC = () => {
|
|||||||
valorParticular: parseFloat(data.valorParticular),
|
valorParticular: parseFloat(data.valorParticular),
|
||||||
tipo_regiao: editingProcedimento?.tipo_regiao || 'GERAL',
|
tipo_regiao: editingProcedimento?.tipo_regiao || 'GERAL',
|
||||||
exige_face: editingProcedimento?.tipo_regiao === 'DENTE' ? !!editingProcedimento?.exige_face : false,
|
exige_face: editingProcedimento?.tipo_regiao === 'DENTE' ? !!editingProcedimento?.exige_face : false,
|
||||||
|
num_faces: editingProcedimento?.num_faces || undefined,
|
||||||
|
num_raizes: editingProcedimento?.num_raizes || undefined,
|
||||||
valoresPlanos: planValues.map(pv => ({
|
valoresPlanos: planValues.map(pv => ({
|
||||||
planoId: pv.planoId!,
|
planoId: pv.planoId!,
|
||||||
planoNome: planos?.find(p => p.id === pv.planoId)?.nome || '',
|
planoNome: planos?.find(p => p.id === pv.planoId)?.nome || '',
|
||||||
@@ -523,6 +525,12 @@ export const ProcedimentosView: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const normEsp = (s: string) => s.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();
|
||||||
|
const procEspNome = normEsp(especialidades?.find(e => e.id === editingProcedimento?.especialidadeId)?.nome || '');
|
||||||
|
const isDentistica = procEspNome.includes('dentistic');
|
||||||
|
const isEndodontia = procEspNome.includes('endodont');
|
||||||
|
const hideArco = isDentistica || isEndodontia;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminSection title="PROCEDIMENTOS E VALORES" buttonLabel="NOVO PROCEDIMENTO" onButtonClick={() => { setEditingProcedimento({}); setIsModalOpen(true); }}>
|
<AdminSection title="PROCEDIMENTOS E VALORES" buttonLabel="NOVO PROCEDIMENTO" onButtonClick={() => { setEditingProcedimento({}); setIsModalOpen(true); }}>
|
||||||
<div className="bg-white border border-gray-200 rounded-xl shadow-sm overflow-hidden">
|
<div className="bg-white border border-gray-200 rounded-xl shadow-sm overflow-hidden">
|
||||||
@@ -654,7 +662,24 @@ export const ProcedimentosView: React.FC = () => {
|
|||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs font-bold text-gray-500 uppercase">ESPECIALIDADE</label>
|
<label className="text-xs font-bold text-gray-500 uppercase">ESPECIALIDADE</label>
|
||||||
<select name="especialidadeId" defaultValue={editingProcedimento?.especialidadeId} className="w-full border border-gray-300 rounded-lg p-2 bg-white uppercase-select" required>
|
<select
|
||||||
|
name="especialidadeId"
|
||||||
|
value={editingProcedimento?.especialidadeId || ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
const espId = e.target.value;
|
||||||
|
const espNorm = normEsp(especialidades?.find(es => es.id === espId)?.nome || '');
|
||||||
|
const willHide = espNorm.includes('dentistic') || espNorm.includes('endodont');
|
||||||
|
setEditingProcedimento(prev => ({
|
||||||
|
...prev,
|
||||||
|
especialidadeId: espId,
|
||||||
|
tipo_regiao: (willHide && prev?.tipo_regiao === 'ARCO') ? 'DENTE' : prev?.tipo_regiao,
|
||||||
|
num_faces: undefined,
|
||||||
|
num_raizes: undefined,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
className="w-full border border-gray-300 rounded-lg p-2 bg-white uppercase-select"
|
||||||
|
required
|
||||||
|
>
|
||||||
<option value="">SELECIONE UMA ESPECIALIDADE</option>
|
<option value="">SELECIONE UMA ESPECIALIDADE</option>
|
||||||
{especialidades?.map(e => <option key={e.id} value={e.id}>{e.nome}</option>)}
|
{especialidades?.map(e => <option key={e.id} value={e.id}>{e.nome}</option>)}
|
||||||
</select>
|
</select>
|
||||||
@@ -672,11 +697,11 @@ export const ProcedimentosView: React.FC = () => {
|
|||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<label className="text-xs font-bold text-gray-700 uppercase">EXIGÊNCIA DE REGIÃO NO LANÇAMENTO</label>
|
<label className="text-xs font-bold text-gray-700 uppercase">EXIGÊNCIA DE REGIÃO NO LANÇAMENTO</label>
|
||||||
<div className="grid grid-cols-3 gap-2">
|
<div className={`grid gap-2 ${hideArco ? 'grid-cols-2' : 'grid-cols-3'}`}>
|
||||||
{[
|
{[
|
||||||
{ id: 'GERAL', label: 'PROCED. GERAL', desc: 'SEM ODONTOGRAMA' },
|
{ id: 'GERAL', label: 'PROCED. GERAL', desc: 'SEM ODONTOGRAMA' },
|
||||||
{ id: 'DENTE', label: 'DENTE ESPECÍFICO', desc: 'LIBERA NÚMEROS' },
|
{ id: 'DENTE', label: 'DENTE ESPECÍFICO', desc: 'LIBERA NÚMEROS' },
|
||||||
{ id: 'ARCO', label: 'ARCO COMPLETO', desc: 'LIBERA AI / AS' }
|
...(!hideArco ? [{ id: 'ARCO', label: 'ARCO COMPLETO', desc: 'LIBERA AI / AS' }] : [])
|
||||||
].map((opt) => (
|
].map((opt) => (
|
||||||
<label
|
<label
|
||||||
key={opt.id}
|
key={opt.id}
|
||||||
@@ -721,6 +746,42 @@ export const ProcedimentosView: React.FC = () => {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isDentistica && (
|
||||||
|
<div className="pt-2 animate-in slide-in-from-top-2 duration-200">
|
||||||
|
<label className="text-[10px] font-black text-blue-700 uppercase tracking-widest mb-1.5 block">
|
||||||
|
Nº de Faces (Dentística)
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={editingProcedimento?.num_faces || ''}
|
||||||
|
onChange={(e) => setEditingProcedimento(prev => ({ ...prev, num_faces: e.target.value ? parseInt(e.target.value) : undefined }))}
|
||||||
|
className="w-full border border-blue-200 rounded-lg p-2.5 bg-white text-sm font-bold text-gray-700 focus:border-blue-500 outline-none"
|
||||||
|
>
|
||||||
|
<option value="">NÃO DEFINIDO</option>
|
||||||
|
{[1, 2, 3, 4, 5].map(n => (
|
||||||
|
<option key={n} value={n}>{n} {n === 1 ? 'face' : 'faces'}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isEndodontia && (
|
||||||
|
<div className="pt-2 animate-in slide-in-from-top-2 duration-200">
|
||||||
|
<label className="text-[10px] font-black text-blue-700 uppercase tracking-widest mb-1.5 block">
|
||||||
|
Nº de Raízes (Endodontia)
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={editingProcedimento?.num_raizes || ''}
|
||||||
|
onChange={(e) => setEditingProcedimento(prev => ({ ...prev, num_raizes: e.target.value ? parseInt(e.target.value) : undefined }))}
|
||||||
|
className="w-full border border-blue-200 rounded-lg p-2.5 bg-white text-sm font-bold text-gray-700 focus:border-blue-500 outline-none"
|
||||||
|
>
|
||||||
|
<option value="">NÃO DEFINIDO</option>
|
||||||
|
{[1, 2, 3, 4, 5].map(n => (
|
||||||
|
<option key={n} value={n}>{n} {n === 1 ? 'raiz' : 'raízes'}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-4">
|
<div className="pt-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user