feat(protese): módulo de OS de prótese (clínica ↔ laboratório/protético)
build-and-push / build (push) Successful in 54s
build-and-push / deploy (push) Successful in 3m38s

- backend: tabelas protese_os/_evento/_foto/_produtos + endpoints /api/protese/*
  (laboratorios, os, bancada, status, foto/raw assinado, produtos); catálogo do protético;
  custo de lab → DESPESA na entrega (idempotente, estorno no delete) + garantia com foto.
- Acesso: helper proteticoAcessivelPelaClinica (interno por vínculo OU diretório) valida o POST
  e o catálogo; catálogos passam por tenantGuard; correção do label da notificação (tipo resolvido).
- Frontend: ProteseView (Bancada do protético / Minhas OS da clínica) + rota /proteses
  + item de menu PRÓTESES no Sidebar (dentista/protético/funcionário).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-06-17 05:20:44 +02:00
parent a991f1fe2c
commit f2eb90ccc8
5 changed files with 961 additions and 6 deletions
+8 -3
View File
@@ -47,6 +47,7 @@ import { AdminGestaoTutoresView } from './views/AdminGestaoTutoresView.tsx';
import { WaitingInviteView } from './views/WaitingInviteView.tsx';
import { SuperAdminView } from './views/SuperAdminView.tsx';
import { DiretorioProfissionaisView } from './views/DiretorioProfissionaisView.tsx';
import { ProteseView } from './views/ProteseView.tsx';
export type ViewKey =
| 'landing'
@@ -59,6 +60,7 @@ export type ViewKey =
| 'financeiro'
| 'comissoes'
| 'glosas'
| 'proteses'
| 'login'
| 'update'
| 'dentistas'
@@ -147,6 +149,7 @@ const ROUTE_MAP: Record<string, ViewKey> = {
'/superadmin/notificacoes': 'superadmin-notificacoes',
'/aguardando-convite': 'aguardando-convite',
'/diretorio-profissionais': 'diretorio-profissionais',
'/proteses': 'proteses',
};
const VIEW_TO_ROUTE: Record<ViewKey, string> = {
@@ -198,6 +201,7 @@ const VIEW_TO_ROUTE: Record<ViewKey, string> = {
'superadmin-financeiro': '/superadmin/financeiro',
'superadmin-notificacoes': '/superadmin/notificacoes',
'diretorio-profissionais': '/diretorio-profissionais',
proteses: '/proteses',
};
/** Resolve current URL to a ViewKey */
@@ -293,10 +297,10 @@ const App: React.FC = () => {
const permissions: Record<string, ViewKey[]> = {
paciente: ['tratamentos', 'notificacoes', 'configuracoes'],
dentista: ['dashboard', 'agenda', 'ortodontia', 'tratamentos', 'lancar-gto', 'notificacoes', 'clinicas', 'configuracoes', 'plugins', 'rx-radiografias', 'salas', 'marketplace-profissionais', 'candidatura-tutor', 'tutoria-painel', 'diretorio-profissionais'],
dentista: ['dashboard', 'agenda', 'ortodontia', 'tratamentos', 'lancar-gto', 'proteses', 'notificacoes', 'clinicas', 'configuracoes', 'plugins', 'rx-radiografias', 'salas', 'marketplace-profissionais', 'candidatura-tutor', 'tutoria-painel', 'diretorio-profissionais'],
biomedico: ['dashboard', 'agenda', 'tratamentos', 'lancar-gto', 'especialidades', 'procedimentos', 'notificacoes', 'clinicas', 'configuracoes', 'plugins', 'salas', 'marketplace-profissionais', 'diretorio-profissionais'],
protetico: ['dashboard', 'agenda', 'tratamentos', 'lancar-gto', 'notificacoes', 'clinicas', 'configuracoes', 'plugins', 'salas', 'marketplace-profissionais', 'diretorio-profissionais'],
funcionario: ['dashboard', 'leads', 'pacientes', 'agenda', 'financeiro', 'tratamentos', 'lancar-gto', 'relatorios', 'notificacoes', 'clinicas', 'configuracoes', 'orcamentos', 'contratos', 'plugins', 'rx-radiografias', 'salas', 'marketplace-profissionais', 'candidatura-tutor', 'diretorio-profissionais'],
protetico: ['dashboard', 'agenda', 'tratamentos', 'lancar-gto', 'proteses', 'notificacoes', 'clinicas', 'configuracoes', 'plugins', 'salas', 'marketplace-profissionais', 'diretorio-profissionais'],
funcionario: ['dashboard', 'leads', 'pacientes', 'agenda', 'financeiro', 'tratamentos', 'lancar-gto', 'proteses', 'relatorios', 'notificacoes', 'clinicas', 'configuracoes', 'orcamentos', 'contratos', 'plugins', 'rx-radiografias', 'salas', 'marketplace-profissionais', 'candidatura-tutor', 'diretorio-profissionais'],
};
return permissions[role]?.includes(view) || false;
@@ -455,6 +459,7 @@ const App: React.FC = () => {
case 'relatorios': return <ReportsView />;
case 'comissoes': return <ComissoesView />;
case 'glosas': return <GlosasView />;
case 'proteses': return <ProteseView />;
case 'cadastro-dentista': return <DentistRegisterView />;
case 'configuracoes': return <ConfiguracoesView />;
case 'orcamentos': return <OrcamentosView />;