Merge branch 'claude/admiring-chaplygin-7fde72'
This commit is contained in:
@@ -80,10 +80,9 @@ const DENTES_ADULTO = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const DENTES_CRIANCA = {
|
const DENTES_CRIANCA = {
|
||||||
// Balanced 4x2 distribution with nulls for spacing (matching image layout)
|
|
||||||
q1: [null, 53, 52, 51, null, null, 55, 54],
|
q1: [null, 53, 52, 51, null, null, 55, 54],
|
||||||
q2: [61, 62, 63, null, 64, 65, null, null],
|
q2: [61, 62, 63, null, 64, 65, null, null],
|
||||||
q4: [null, 43, 42, 41, null, null, 46, 45],
|
q4: [null, 83, 82, 81, null, null, 85, 84], // fix: eram 43-46 (permanentes)
|
||||||
q3: [71, 72, 73, null, 74, 75, null, null]
|
q3: [71, 72, 73, null, 74, 75, null, null]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -103,6 +102,7 @@ export const LancarGTO: React.FC = () => {
|
|||||||
const [selectedEsp, setSelectedEsp] = useState<string>('');
|
const [selectedEsp, setSelectedEsp] = useState<string>('');
|
||||||
const [selectedProc, setSelectedProc] = useState<Procedimento | null>(null);
|
const [selectedProc, setSelectedProc] = useState<Procedimento | null>(null);
|
||||||
const [pacienteTipo, setPacienteTipo] = useState<'ADULTO' | 'CRIANCA'>('ADULTO');
|
const [pacienteTipo, setPacienteTipo] = useState<'ADULTO' | 'CRIANCA'>('ADULTO');
|
||||||
|
const [showMixed, setShowMixed] = useState(false);
|
||||||
const [selectedDentes, setSelectedDentes] = useState<string[]>([]);
|
const [selectedDentes, setSelectedDentes] = useState<string[]>([]);
|
||||||
const [selectedArco, setSelectedArco] = useState<'AI' | 'AS' | null>(null);
|
const [selectedArco, setSelectedArco] = useState<'AI' | 'AS' | null>(null);
|
||||||
const [face, setFace] = useState('');
|
const [face, setFace] = useState('');
|
||||||
@@ -244,9 +244,22 @@ export const LancarGTO: React.FC = () => {
|
|||||||
|
|
||||||
const totalGeral = store.items.reduce((sum, i) => sum + i.valorTotal, 0);
|
const totalGeral = store.items.reduce((sum, i) => sum + i.valorTotal, 0);
|
||||||
|
|
||||||
|
const toggleMixed = () => {
|
||||||
|
if (showMixed) {
|
||||||
|
// Ao fechar: remove os dentes do tipo expandido da seleção
|
||||||
|
if (pacienteTipo === 'ADULTO') {
|
||||||
|
setSelectedDentes(prev => prev.filter(d => parseInt(d) < 50));
|
||||||
|
} else {
|
||||||
|
setSelectedDentes(prev => prev.filter(d => parseInt(d) >= 50));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setShowMixed(prev => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
const renderDenteBtn = (num: number | string | null, keyIdx?: number) => {
|
const renderDenteBtn = (num: number | string | null, keyIdx?: number) => {
|
||||||
if (num === null) return <div key={`empty-${keyIdx}`} className="w-9 h-9" />;
|
if (num === null) return <div key={`empty-${keyIdx}`} className="w-7 h-7 sm:w-9 sm:h-9" />;
|
||||||
const sStr = num.toString();
|
const sStr = num.toString();
|
||||||
|
const isDeciduo = parseInt(sStr) >= 50;
|
||||||
const isSelected = selectedDentes.includes(sStr);
|
const isSelected = selectedDentes.includes(sStr);
|
||||||
const isDisabled = !!selectedProc && selectedProc.tipo_regiao !== 'DENTE';
|
const isDisabled = !!selectedProc && selectedProc.tipo_regiao !== 'DENTE';
|
||||||
|
|
||||||
@@ -255,12 +268,16 @@ export const LancarGTO: React.FC = () => {
|
|||||||
key={sStr}
|
key={sStr}
|
||||||
onClick={() => toggleDente(sStr)}
|
onClick={() => toggleDente(sStr)}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
className={`w-9 h-9 rounded-lg border-2 text-[11px] font-black transition-all shadow-sm flex items-center justify-center
|
className={`w-7 h-7 sm:w-9 sm:h-9 rounded-lg border-2 text-[9px] sm:text-[11px] font-black transition-all shadow-sm flex items-center justify-center
|
||||||
${isSelected
|
${isSelected
|
||||||
? 'bg-blue-600 border-blue-700 text-white scale-110 z-10'
|
? isDeciduo
|
||||||
|
? 'bg-amber-500 border-amber-600 text-white scale-110 z-10'
|
||||||
|
: 'bg-blue-600 border-blue-700 text-white scale-110 z-10'
|
||||||
: isDisabled
|
: isDisabled
|
||||||
? 'bg-gray-50 border-gray-100 text-gray-300 cursor-not-allowed opacity-50'
|
? 'bg-gray-50 border-gray-100 text-gray-300 cursor-not-allowed opacity-50'
|
||||||
: 'bg-white border-gray-200 text-gray-700 hover:border-blue-400 hover:bg-blue-50'}
|
: isDeciduo
|
||||||
|
? 'bg-white border-amber-200 text-amber-700 hover:border-amber-400 hover:bg-amber-50'
|
||||||
|
: 'bg-white border-gray-200 text-gray-700 hover:border-blue-400 hover:bg-blue-50'}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{sStr}
|
{sStr}
|
||||||
@@ -281,18 +298,18 @@ export const LancarGTO: React.FC = () => {
|
|||||||
<div className="col-span-12 lg:col-span-8 space-y-6">
|
<div className="col-span-12 lg:col-span-8 space-y-6">
|
||||||
|
|
||||||
{/* ODONTOGRAM BOX */}
|
{/* ODONTOGRAM BOX */}
|
||||||
<div className="bg-white rounded-3xl border border-gray-100 shadow-xl p-8 space-y-6">
|
<div className="bg-white rounded-3xl border border-gray-100 shadow-xl p-3 sm:p-8 space-y-4 sm:space-y-6">
|
||||||
<div className="flex justify-center mb-4">
|
<div className="flex justify-center mb-2 sm:mb-4">
|
||||||
<div className="inline-flex bg-gray-100 p-1 rounded-xl shadow-inner">
|
<div className="inline-flex bg-gray-100 p-1 rounded-xl shadow-inner">
|
||||||
<button
|
<button
|
||||||
onClick={() => { setPacienteTipo('ADULTO'); setSelectedDentes([]); }}
|
onClick={() => { setPacienteTipo('ADULTO'); setSelectedDentes([]); setShowMixed(false); }}
|
||||||
className={`px-8 py-2 rounded-lg text-xs font-black transition-all uppercase ${pacienteTipo === 'ADULTO' ? 'bg-blue-600 text-white shadow-md scale-105' : 'text-gray-500 hover:text-gray-800'}`}
|
className={`px-5 sm:px-8 py-2 rounded-lg text-xs font-black transition-all uppercase ${pacienteTipo === 'ADULTO' ? 'bg-blue-600 text-white shadow-md scale-105' : 'text-gray-500 hover:text-gray-800'}`}
|
||||||
>
|
>
|
||||||
Adulto
|
Adulto
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => { setPacienteTipo('CRIANCA'); setSelectedDentes([]); }}
|
onClick={() => { setPacienteTipo('CRIANCA'); setSelectedDentes([]); setShowMixed(false); }}
|
||||||
className={`px-8 py-2 rounded-lg text-xs font-black transition-all uppercase ${pacienteTipo === 'CRIANCA' ? 'bg-blue-600 text-white shadow-md scale-105' : 'text-gray-500 hover:text-gray-800'}`}
|
className={`px-5 sm:px-8 py-2 rounded-lg text-xs font-black transition-all uppercase ${pacienteTipo === 'CRIANCA' ? 'bg-blue-600 text-white shadow-md scale-105' : 'text-gray-500 hover:text-gray-800'}`}
|
||||||
>
|
>
|
||||||
Criança
|
Criança
|
||||||
</button>
|
</button>
|
||||||
@@ -300,35 +317,34 @@ export const LancarGTO: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Dynamic Odontogram Grid */}
|
{/* Dynamic Odontogram Grid */}
|
||||||
<div className="relative border-t border-b border-gray-50 py-10">
|
<div className="relative border-t border-b border-gray-50 py-6 sm:py-10">
|
||||||
{/* Visual Separators */}
|
|
||||||
<div className="absolute left-1/2 top-4 bottom-4 w-[2px] bg-gray-200 -translate-x-1/2"></div>
|
<div className="absolute left-1/2 top-4 bottom-4 w-[2px] bg-gray-200 -translate-x-1/2"></div>
|
||||||
<div className="absolute top-1/2 left-4 right-4 h-[2px] bg-gray-200 -translate-y-1/2"></div>
|
<div className="absolute top-1/2 left-2 right-2 h-[2px] bg-gray-200 -translate-y-1/2"></div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-x-12 gap-y-16">
|
<div className="grid grid-cols-2 gap-x-2 sm:gap-x-12 gap-y-6 sm:gap-y-16">
|
||||||
{/* Q1 & Q2 (SUPERIOR) */}
|
{/* Q1 & Q2 (SUPERIOR) */}
|
||||||
<div className="flex justify-end items-center gap-2 pr-2">
|
<div className="flex justify-end items-center gap-1 sm:gap-2 pr-1 sm:pr-2">
|
||||||
{pacienteTipo === 'ADULTO' && renderDenteBtn(DENTES_ADULTO.extras.q1)}
|
{pacienteTipo === 'ADULTO' && renderDenteBtn(DENTES_ADULTO.extras.q1)}
|
||||||
<div className="grid grid-cols-4 gap-2">
|
<div className="grid grid-cols-4 gap-1 sm:gap-2">
|
||||||
{(pacienteTipo === 'ADULTO' ? DENTES_ADULTO.q1 : DENTES_CRIANCA.q1).map((d, i) => renderDenteBtn(d, i))}
|
{(pacienteTipo === 'ADULTO' ? DENTES_ADULTO.q1 : DENTES_CRIANCA.q1).map((d, i) => renderDenteBtn(d, i))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-start items-center gap-2 pl-2">
|
<div className="flex justify-start items-center gap-1 sm:gap-2 pl-1 sm:pl-2">
|
||||||
<div className="grid grid-cols-4 gap-2">
|
<div className="grid grid-cols-4 gap-1 sm:gap-2">
|
||||||
{(pacienteTipo === 'ADULTO' ? DENTES_ADULTO.q2 : DENTES_CRIANCA.q2).map((d, i) => renderDenteBtn(d, i))}
|
{(pacienteTipo === 'ADULTO' ? DENTES_ADULTO.q2 : DENTES_CRIANCA.q2).map((d, i) => renderDenteBtn(d, i))}
|
||||||
</div>
|
</div>
|
||||||
{pacienteTipo === 'ADULTO' && renderDenteBtn(DENTES_ADULTO.extras.q2)}
|
{pacienteTipo === 'ADULTO' && renderDenteBtn(DENTES_ADULTO.extras.q2)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Q4 & Q3 (INFERIOR) */}
|
{/* Q4 & Q3 (INFERIOR) */}
|
||||||
<div className="flex justify-end items-center gap-2 pr-2">
|
<div className="flex justify-end items-center gap-1 sm:gap-2 pr-1 sm:pr-2">
|
||||||
{pacienteTipo === 'ADULTO' && renderDenteBtn(DENTES_ADULTO.extras.q4)}
|
{pacienteTipo === 'ADULTO' && renderDenteBtn(DENTES_ADULTO.extras.q4)}
|
||||||
<div className="grid grid-cols-4 gap-2">
|
<div className="grid grid-cols-4 gap-1 sm:gap-2">
|
||||||
{(pacienteTipo === 'ADULTO' ? DENTES_ADULTO.q4 : DENTES_CRIANCA.q4).map((d, i) => renderDenteBtn(d, i))}
|
{(pacienteTipo === 'ADULTO' ? DENTES_ADULTO.q4 : DENTES_CRIANCA.q4).map((d, i) => renderDenteBtn(d, i))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-start items-center gap-2 pl-2">
|
<div className="flex justify-start items-center gap-1 sm:gap-2 pl-1 sm:pl-2">
|
||||||
<div className="grid grid-cols-4 gap-2">
|
<div className="grid grid-cols-4 gap-1 sm:gap-2">
|
||||||
{(pacienteTipo === 'ADULTO' ? DENTES_ADULTO.q3 : DENTES_CRIANCA.q3).map((d, i) => renderDenteBtn(d, i))}
|
{(pacienteTipo === 'ADULTO' ? DENTES_ADULTO.q3 : DENTES_CRIANCA.q3).map((d, i) => renderDenteBtn(d, i))}
|
||||||
</div>
|
</div>
|
||||||
{pacienteTipo === 'ADULTO' && renderDenteBtn(DENTES_ADULTO.extras.q3)}
|
{pacienteTipo === 'ADULTO' && renderDenteBtn(DENTES_ADULTO.extras.q3)}
|
||||||
@@ -336,6 +352,61 @@ export const LancarGTO: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* SEÇÃO MISTO — expande abaixo do grid principal */}
|
||||||
|
{showMixed && (
|
||||||
|
<div className="border-t-2 border-dashed border-amber-200 pt-4 space-y-3 animate-in fade-in slide-in-from-top-2 duration-200">
|
||||||
|
<p className="text-[9px] font-black text-amber-500 uppercase tracking-widest text-center">
|
||||||
|
{pacienteTipo === 'ADULTO' ? '— DENTES DECÍDUOS —' : '— DENTES PERMANENTES —'}
|
||||||
|
</p>
|
||||||
|
<div className="relative py-4 sm:py-6">
|
||||||
|
<div className="absolute left-1/2 top-2 bottom-2 w-[2px] bg-amber-100 -translate-x-1/2"></div>
|
||||||
|
<div className="absolute top-1/2 left-2 right-2 h-[2px] bg-amber-100 -translate-y-1/2"></div>
|
||||||
|
<div className="grid grid-cols-2 gap-x-2 sm:gap-x-12 gap-y-6 sm:gap-y-16">
|
||||||
|
{/* Q1 & Q2 MISTO */}
|
||||||
|
<div className="flex justify-end items-center gap-1 sm:gap-2 pr-1 sm:pr-2">
|
||||||
|
{pacienteTipo === 'CRIANCA' && renderDenteBtn(DENTES_ADULTO.extras.q1)}
|
||||||
|
<div className="grid grid-cols-4 gap-1 sm:gap-2">
|
||||||
|
{(pacienteTipo === 'ADULTO' ? DENTES_CRIANCA.q1 : DENTES_ADULTO.q1).map((d, i) => renderDenteBtn(d, i))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-start items-center gap-1 sm:gap-2 pl-1 sm:pl-2">
|
||||||
|
<div className="grid grid-cols-4 gap-1 sm:gap-2">
|
||||||
|
{(pacienteTipo === 'ADULTO' ? DENTES_CRIANCA.q2 : DENTES_ADULTO.q2).map((d, i) => renderDenteBtn(d, i))}
|
||||||
|
</div>
|
||||||
|
{pacienteTipo === 'CRIANCA' && renderDenteBtn(DENTES_ADULTO.extras.q2)}
|
||||||
|
</div>
|
||||||
|
{/* Q4 & Q3 MISTO */}
|
||||||
|
<div className="flex justify-end items-center gap-1 sm:gap-2 pr-1 sm:pr-2">
|
||||||
|
{pacienteTipo === 'CRIANCA' && renderDenteBtn(DENTES_ADULTO.extras.q4)}
|
||||||
|
<div className="grid grid-cols-4 gap-1 sm:gap-2">
|
||||||
|
{(pacienteTipo === 'ADULTO' ? DENTES_CRIANCA.q4 : DENTES_ADULTO.q4).map((d, i) => renderDenteBtn(d, i))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-start items-center gap-1 sm:gap-2 pl-1 sm:pl-2">
|
||||||
|
<div className="grid grid-cols-4 gap-1 sm:gap-2">
|
||||||
|
{(pacienteTipo === 'ADULTO' ? DENTES_CRIANCA.q3 : DENTES_ADULTO.q3).map((d, i) => renderDenteBtn(d, i))}
|
||||||
|
</div>
|
||||||
|
{pacienteTipo === 'CRIANCA' && renderDenteBtn(DENTES_ADULTO.extras.q3)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* BOTÃO EXPANSOR — dentição mista */}
|
||||||
|
<button
|
||||||
|
onClick={toggleMixed}
|
||||||
|
className={`w-full py-2.5 rounded-2xl border-2 font-black text-[11px] uppercase transition-all flex items-center justify-center gap-2
|
||||||
|
${showMixed
|
||||||
|
? 'border-amber-300 text-amber-600 bg-amber-50 hover:bg-amber-100'
|
||||||
|
: 'border-dashed border-amber-200 text-amber-400 hover:border-amber-400 hover:bg-amber-50'}`}
|
||||||
|
>
|
||||||
|
{showMixed
|
||||||
|
? <><X size={13} /> {pacienteTipo === 'ADULTO' ? 'REMOVER DECÍDUOS' : 'REMOVER PERMANENTES'}</>
|
||||||
|
: <><PlusCircle size={13} /> {pacienteTipo === 'ADULTO' ? '+ INCLUIR DENTES DECÍDUOS' : '+ INCLUIR DENTES PERMANENTES'}</>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
|
||||||
<div className="flex justify-center gap-4 pt-4">
|
<div className="flex justify-center gap-4 pt-4">
|
||||||
<button
|
<button
|
||||||
onClick={() => selectArco('AI')}
|
onClick={() => selectArco('AI')}
|
||||||
|
|||||||
Reference in New Issue
Block a user