feat(protese): Fase 4 — PF/PJ + CNPJ + Equipe (técnicos do laboratório)

Provedor de Prótese, Fase 4 (doc/PROVEDOR-PROTESE-DECISAO.md):

- PF/PJ: clinicas.pessoa_tipo (individual | laboratório) + documento (CPF/CNPJ) editáveis.
  Tela "Laboratório & Equipe" (lab-equipe). GET/PATCH /protese/lab.
- Equipe: novo vinculos.role='tecnico_protese'. Dono convida por e-mail (ensureUsuarioPorEmail
  → credencial temporária). GET/POST/DELETE /protese/lab/equipe (só dono).
- Acesso estendido: proteseOsAccesso reconhece o técnico (vínculo no laboratorio_id) e a bancada
  passa a usar labDoUsuario (dono OU técnico). Técnico opera a produção; menu reduzido e sem
  clientes/financeiro/equipe (endpoints retornam vazio/403). Gestão é exclusiva do dono.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-06-24 05:40:10 +02:00
parent c8d679eb70
commit ae48feaa8a
6 changed files with 243 additions and 9 deletions
+9 -2
View File
@@ -65,6 +65,7 @@ export const Sidebar: React.FC<SidebarProps> = ({ activeTab, setActiveTab }) =>
{ id: 'lab-bancada', label: 'BANCADA', icon: FlaskConical },
{ id: 'lab-clientes', label: 'CLIENTES', icon: Building2 },
{ id: 'lab-financeiro', label: 'FINANCEIRO', icon: DollarSign },
{ id: 'lab-equipe', label: 'EQUIPE', icon: UsersRound },
{ id: 'dashboard', label: 'VISÃO GERAL', icon: LayoutDashboard },
{ id: 'agenda', label: 'AGENDA', icon: CalendarIcon },
{ id: 'pacientes', label: 'PACIENTES', icon: Users },
@@ -102,7 +103,7 @@ export const Sidebar: React.FC<SidebarProps> = ({ activeTab, setActiveTab }) =>
// PAINEL DA SALA só existe quando o workspace ativo é uma sala.
if (item.id === 'sala-home') return (activeWorkspace as any)?.tipo === 'sala';
// PAINEL DO LAB / BANCADA só existem no workspace de laboratório.
if (['lab-home', 'lab-bancada', 'lab-clientes', 'lab-financeiro'].includes(item.id)) return (activeWorkspace as any)?.tipo === 'laboratorio';
if (['lab-home', 'lab-bancada', 'lab-clientes', 'lab-financeiro', 'lab-equipe'].includes(item.id)) return (activeWorkspace as any)?.tipo === 'laboratorio';
// Contexto de SALA (workspace tipo='sala'): isola da clínica — menu base só conta/notificações.
// (MINHAS SALAS / ALUGAR SALAS são itens de plugin, mostrados à parte.)
@@ -110,7 +111,13 @@ export const Sidebar: React.FC<SidebarProps> = ({ activeTab, setActiveTab }) =>
// Contexto de LABORATÓRIO (workspace tipo='laboratorio'): área própria do protético —
// painel do lab + bancada de produção, sem as telas clínicas da clínica.
if ((activeWorkspace as any)?.tipo === 'laboratorio') return ['lab-home', 'lab-bancada', 'lab-clientes', 'lab-financeiro', 'notificacoes', 'configuracoes'].includes(item.id);
if ((activeWorkspace as any)?.tipo === 'laboratorio') {
// Técnico (vínculo tecnico_protese) só opera a produção; gestão é do dono.
const ehTecnico = (activeWorkspace as any)?.role === 'tecnico_protese';
return ehTecnico
? ['lab-home', 'lab-bancada', 'notificacoes', 'configuracoes'].includes(item.id)
: ['lab-home', 'lab-bancada', 'lab-clientes', 'lab-financeiro', 'lab-equipe', 'notificacoes', 'configuracoes'].includes(item.id);
}
// plugins e gestão de tutores: exclusivo superadmin
if (item.id === 'plugins' || item.id === 'admin-gestao-tutores') return false;