feat(ui): dropdowns viram modal com busca (SearchSelect) na Avaliação/GTO e Nova OS
build-and-promote / build (push) Has been skipped
build-and-promote / promote (push) Successful in 55s

Mesmo padrão do seletor de protético, generalizado para os demais dropdowns:

- Componente SearchSelect (modal genérico: trigger + busca + lista filtrável por rótulo/hint).
- LancarGTO: Especialidade/Área, Produto/Prótese, Procedimento, Dentista e Convênio/Credenciada
  — todos os <select> convertidos (0 selects restantes).
- Nova OS (ProteseView): Produto e Dentista convertidos.
- Cotação (RFQ): input de busca na lista de laboratórios a convidar (continua multi-seleção).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-06-25 07:29:49 +02:00
parent e6d8b532e3
commit d83e0c82b2
3 changed files with 111 additions and 47 deletions
+30 -38
View File
@@ -14,6 +14,7 @@ import {
} from 'lucide-react';
import { PageHeader } from '../components/PageHeader.tsx';
import { ProteticoPicker } from '../components/ProteticoPicker.tsx';
import { SearchSelect } from '../components/SearchSelect.tsx';
import { HybridBackend } from '../services/backend.ts';
import { Paciente, Dentista, Procedimento, Especialidade } from '../types.ts';
import { useToast } from '../contexts/ToastContext.tsx';
@@ -457,10 +458,10 @@ export const LancarGTO: React.FC = () => {
<Stethoscope size={20} strokeWidth={3} /> 02. Especialidade
</div>
<div className="space-y-3">
<select
<SearchSelect
value={selectedEsp}
onChange={(e) => {
setSelectedEsp(e.target.value);
onChange={(v) => {
setSelectedEsp(v);
setSelectedProc(null);
setSelectedDentes([]);
setSelectedArco(null);
@@ -468,11 +469,9 @@ export const LancarGTO: React.FC = () => {
setProtValor('');
setProtObs('');
}}
className="w-full p-4 bg-gray-50 border-2 border-gray-100 rounded-2xl font-black text-gray-700 text-sm focus:border-teal-400 outline-none transition-all uppercase"
>
<option value="">ESCOLHA A ÁREA...</option>
{especialidades.map(es => <option key={es.id} value={es.id}>{es.nome}</option>)}
</select>
options={especialidades.map(es => ({ value: es.id, label: es.nome }))}
placeholder="ESCOLHA A ÁREA..." title="Especialidade"
/>
{selectedEsp && isProtese && (
<div className="space-y-3">
@@ -484,18 +483,17 @@ export const LancarGTO: React.FC = () => {
</div>
) : (
<>
<select
<SearchSelect
value={selectedProdutoId}
onChange={(e) => {
setSelectedProdutoId(e.target.value);
const pr = labProdutos.find(p => p.id === e.target.value);
onChange={(v) => {
setSelectedProdutoId(v);
const pr = labProdutos.find(p => p.id === v);
setProtValor(pr ? String(pr.preco ?? '') : '');
}}
options={labProdutos.map(p => ({ value: p.id, label: p.nome, hint: `R$ ${Number(p.preco || 0).toFixed(2)}` }))}
placeholder="ESCOLHA A PRÓTESE..." title="Produto do laboratório"
className="w-full p-4 bg-white border-2 border-teal-100 rounded-2xl font-black text-gray-800 text-sm shadow-inner uppercase"
>
<option value="">ESCOLHA A PRÓTESE...</option>
{labProdutos.map(p => <option key={p.id} value={p.id}>{p.nome} R$ {Number(p.preco || 0).toFixed(2)}</option>)}
</select>
/>
{selectedProduto && (
<>
@@ -543,21 +541,20 @@ export const LancarGTO: React.FC = () => {
<p className="text-[10px] font-bold text-amber-500 uppercase mt-1">Cadastre em Procedimentos para usá-lo aqui</p>
</div>
) : (
<select
<SearchSelect
value={selectedProc?.id || ''}
onChange={(e) => {
const pr = procedimentos.find(p => p.id === e.target.value);
onChange={(v) => {
const pr = procedimentos.find(p => p.id === v);
setSelectedProc(pr || null);
setSelectedDentes([]);
setSelectedArco(null);
setArcoFace('');
setArcoObs('');
}}
options={filteredProcedimentos.map(p => ({ value: p.id, label: p.nome }))}
placeholder="BUSCAR PROCEDIMENTO" title="Procedimento"
className="w-full p-4 bg-white border-2 border-teal-100 rounded-2xl font-black text-gray-800 text-sm shadow-inner uppercase"
>
<option value="">BUSCAR PROCEDIMENTO</option>
{filteredProcedimentos.map(p => <option key={p.id} value={p.id}>{p.nome}</option>)}
</select>
/>
)}
{selectedProc && (
@@ -588,28 +585,23 @@ export const LancarGTO: React.FC = () => {
<div className="flex items-center gap-3 text-[#2d6a4f] font-black text-base uppercase tracking-wider">
<Check size={20} strokeWidth={3} className="bg-green-100 p-1 rounded-full text-green-700" /> 03. Dentista Executor
</div>
<select
<SearchSelect
value={store.dentista?.id || ''}
onChange={(e) => {
const d = dentistas.find(d => d.id === e.target.value);
store.setDentista(d || null);
}}
onChange={(v) => { const d = dentistas.find(d => d.id === v); store.setDentista(d || null); }}
options={dentistas.map(d => ({ value: d.id, label: d.nome }))}
placeholder="SELECIONE O PROFISSIONAL" title="Dentista executor"
className="w-full p-4 bg-gray-50 border-2 border-gray-100 rounded-2xl font-black text-gray-800 text-sm focus:border-green-500 outline-none transition-all uppercase"
>
<option value="">SELECIONE O PROFISSIONAL</option>
{dentistas.map(d => <option key={d.id} value={d.id}>{d.nome}</option>)}
</select>
/>
<div className="pt-1">
<label className="text-[11px] font-black text-gray-400 uppercase tracking-widest mb-1.5 block">Convênio / Credenciada</label>
<select
<SearchSelect
value={store.credenciada || ''}
onChange={(e) => store.setCredenciada(e.target.value)}
onChange={(v) => store.setCredenciada(v)}
options={convenios.map(c => ({ value: c.id, label: c.nome }))}
placeholder="SELECIONE O CONVÊNIO" title="Convênio / Credenciada"
className="w-full p-4 bg-gray-50 border-2 border-gray-100 rounded-2xl font-black text-gray-800 text-sm focus:border-green-500 outline-none transition-all uppercase"
>
<option value="">SELECIONE O CONVÊNIO</option>
{convenios.map(c => <option key={c.id} value={c.id}>{c.nome}</option>)}
</select>
/>
</div>
</div>
);