Files
VPS 4 Builder 672c3b01e5
build-and-promote / build (push) Has been skipped
build-and-promote / promote (push) Successful in 58s
feat(protese): Modo 1 fatia 1a — leitura da OS por link seguro /p/TOKEN (sem conta)
Provedor de Prótese SEM conta (doc/MODO1-LINK-SEGURO-PROTESE):

- Token por OS: tabela protese_os_link (aleatório 24B base64url, expira 60 dias, revogável,
  conta acessos). POST /protese/os/:id/link (gera/recupera) + POST .../link/revogar.
- Leitura pública GET /api/p/:token (sem auth): payload curado com a mesma blindagem soLab —
  SEM paciente_id (CPF) e SEM valor cobrado ao paciente; mostra trabalho, etapas, componentes,
  fotos, custódia, ocorrências, histórico e custo do lab.
- Página pública ProtesePublicView (mobile-first 320px+), interceptada no index.tsx antes do
  app autenticado; CTA "Criar conta grátis". Botão Gerar link / WhatsApp / Copiar na aba
  Monitoramento (modo clínica).
- Validado em DEV: leitura, blindagem (CPF/valor ocultos), token inválido→404, revogação→404.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 06:53:02 +02:00

60 lines
2.4 KiB
TypeScript

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
import { ProtesePublicView } from './views/ProtesePublicView.tsx';
import { ErrorBoundary } from './components/ErrorBoundary.tsx';
import { ToastProvider } from './contexts/ToastContext.tsx';
import { ConfirmProvider } from './contexts/ConfirmContext.tsx';
import './index.css';
// CSS imports are now handled in index.html via <link> tags for AI Studio compatibility,
// but we add it here too for Vite dev server robustness.
/**
* AI Studio Compatible Entrypoint
*
* This version restores the necessary providers (`ErrorBoundary`, `ToastProvider`)
* to allow the application to run correctly after the initial bootstrap.
* It uses the modern `createRoot` API, which is required for React 18 and newer.
*/
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
// Auto-reload em chunk obsoleto pós-deploy (par do watchdog inline no index.html).
// Timestamp evita loop: só recarrega se a última tentativa foi há mais de 30s.
window.addEventListener('vite:preloadError', () => {
try {
const last = +(sessionStorage.getItem('__sd_stale_reload__') || 0);
if (Date.now() - last < 30000) return;
sessionStorage.setItem('__sd_stale_reload__', String(Date.now()));
} catch { /* ignore */ }
window.location.reload();
});
const rootElement = document.getElementById('root');
if (rootElement) {
const root = createRoot(rootElement);
// Modo 1 — link seguro: /p/TOKEN é página PÚBLICA (sem login), fora do app autenticado.
const publicMatch = window.location.pathname.match(/^\/p\/(.+)$/);
root.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<ErrorBoundary>
<ToastProvider>
<ConfirmProvider>
{publicMatch ? <ProtesePublicView token={decodeURIComponent(publicMatch[1])} /> : <App />}
</ConfirmProvider>
</ToastProvider>
</ErrorBoundary>
</QueryClientProvider>
</React.StrictMode>
);
} else {
console.error("CRITICAL: Root element #root not found. App could not be mounted.");
const errorDiv = document.getElementById('global-error-display');
if (errorDiv) {
errorDiv.style.display = 'block';
errorDiv.innerText = 'ERRO CRÍTICO: O ELEMENTO #ROOT NÃO FOI ENCONTRADO NO HTML.';
}
}