develop #83

Merged
M-Gabrielly merged 426 commits from develop into main 2025-12-04 04:13:15 +00:00
6 changed files with 123 additions and 48 deletions
Showing only changes of commit f6fad55ff3 - Show all commits

View File

@ -309,7 +309,7 @@ export default function AgendamentoPage() {
</div>
{/* legenda dinâmica: mostra as cores presentes nos agendamentos do dia atual */}
<div className="sm:absolute sm:top-2 sm:right-2 mt-2 sm:mt-0 z-40">
<div className="sm:absolute sm:top-2 sm:right-2 mt-2 sm:mt-0 z-10">
<DynamicLegend events={managerEvents} />
</div>
</div>

View File

@ -178,6 +178,33 @@ export default function LaudosEditorPage() {
}
}, []);
// Auto-salvar no localStorage sempre que houver mudanças (com debounce)
useEffect(() => {
const timeoutId = setTimeout(() => {
// Capturar conteúdo atual do editor antes de salvar
const currentContent = editorRef.current?.innerHTML || content;
const draft = {
pacienteSelecionado,
content: currentContent,
campos,
solicitanteId,
solicitanteNome,
prazoDate,
prazoTime,
imagens,
lastSaved: new Date().toISOString(),
};
// Só salvar se houver conteúdo ou dados preenchidos
if (currentContent || pacienteSelecionado || campos.exame || campos.diagnostico || imagens.length > 0) {
localStorage.setItem('laudoDraft', JSON.stringify(draft));
}
}, 1000); // Aguarda 1 segundo após última mudança
return () => clearTimeout(timeoutId);
}, [pacienteSelecionado, content, campos, solicitanteId, solicitanteNome, prazoDate, prazoTime, imagens]);
// Tentar obter o registro de médico correspondente ao usuário autenticado
useEffect(() => {
let mounted = true;
@ -247,6 +274,23 @@ export default function LaudosEditorPage() {
}
}, [content]);
// Função para trocar de aba salvando conteúdo antes
const handleTabChange = (newTab: string) => {
// Salvar conteúdo do editor antes de trocar
if (editorRef.current) {
const editorContent = editorRef.current.innerHTML;
setContent(editorContent);
}
setActiveTab(newTab);
};
// Restaurar conteúdo do editor quando voltar para a aba editor
useEffect(() => {
if (activeTab === 'editor' && editorRef.current && content) {
editorRef.current.innerHTML = content;
}
}, [activeTab]);
// Desfazer
const handleUndo = () => {
if (historyIndex > 0) {
@ -321,11 +365,15 @@ export default function LaudosEditorPage() {
// Salvar rascunho no localStorage
const saveDraft = () => {
// Capturar conteúdo atual do editor antes de salvar
const currentContent = editorRef.current?.innerHTML || content;
const draft = {
pacienteSelecionado,
content,
content: currentContent,
campos,
solicitanteId,
solicitanteNome,
prazoDate,
prazoTime,
imagens,
@ -389,6 +437,9 @@ export default function LaudosEditorPage() {
return;
}
// Capturar conteúdo atual do editor antes de salvar
const currentContent = editorRef.current?.innerHTML || content;
const userId = user?.id || '00000000-0000-0000-0000-000000000001';
let composedDueAt = undefined;
@ -404,7 +455,7 @@ export default function LaudosEditorPage() {
diagnosis: campos.diagnostico || '',
conclusion: campos.conclusao || '',
cid_code: campos.cid || '',
content_html: content,
content_html: currentContent,
content_json: {},
requested_by: solicitanteId || userId,
due_at: composedDueAt ?? new Date().toISOString(),
@ -414,6 +465,10 @@ export default function LaudosEditorPage() {
if (createNewReport) {
await createNewReport(payload as any);
// Limpar rascunho salvo após sucesso
localStorage.removeItem('laudoDraft');
toast({
title: 'Laudo criado com sucesso!',
description: 'O laudo foi liberado e salvo.',
@ -536,7 +591,7 @@ export default function LaudosEditorPage() {
{/* Tabs */}
<div className="flex border-b border-border bg-card overflow-x-auto flex-shrink-0">
<button
onClick={() => setActiveTab('editor')}
onClick={() => handleTabChange('editor')}
className={`px-2 sm:px-4 py-2 text-xs sm:text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${
activeTab === 'editor'
? 'border-blue-500 text-blue-600'
@ -547,7 +602,7 @@ export default function LaudosEditorPage() {
Editor
</button>
<button
onClick={() => setActiveTab('imagens')}
onClick={() => handleTabChange('imagens')}
className={`px-2 sm:px-4 py-2 text-xs sm:text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${
activeTab === 'imagens'
? 'border-blue-500 text-blue-600'
@ -558,7 +613,7 @@ export default function LaudosEditorPage() {
Imagens ({imagens.length})
</button>
<button
onClick={() => setActiveTab('campos')}
onClick={() => handleTabChange('campos')}
className={`px-2 sm:px-4 py-2 text-xs sm:text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${
activeTab === 'campos'
? 'border-blue-500 text-blue-600'

View File

@ -69,32 +69,47 @@ export default function EditarLaudoPage() {
// Estado para rastrear alinhamento ativo
const [activeAlignment, setActiveAlignment] = useState('left');
// Salvar conteúdo no localStorage sempre que muda
// Salvar conteúdo no localStorage sempre que muda (com debounce)
useEffect(() => {
if (content && laudoId) {
localStorage.setItem(`laudo-draft-${laudoId}`, content);
const timeoutId = setTimeout(() => {
if (laudoId) {
// Capturar conteúdo atual do editor antes de salvar
const currentContent = editorRef.current?.innerHTML || content;
const draft = {
content: currentContent,
campos,
lastSaved: new Date().toISOString(),
};
localStorage.setItem(`laudo-draft-${laudoId}`, JSON.stringify(draft));
}
}, [content, laudoId]);
}, 1000); // Aguarda 1 segundo após última mudança
return () => clearTimeout(timeoutId);
}, [content, campos, laudoId]);
// Sincronizar conteúdo com o editor
useEffect(() => {
if (editorRef.current && content) {
if (editorRef.current.innerHTML !== content) {
editorRef.current.innerHTML = content;
}
}
}, [content]);
// Função para trocar de aba salvando conteúdo antes
const handleTabChange = (newTab: string) => {
// Salvar conteúdo do editor antes de trocar
if (editorRef.current) {
const editorContent = editorRef.current.innerHTML;
setContent(editorContent);
}
setActiveTab(newTab);
};
// Restaurar conteúdo quando volta para a aba editor
useEffect(() => {
if (activeTab === 'editor' && editorRef.current && content) {
editorRef.current.focus();
const range = document.createRange();
const sel = window.getSelection();
range.setStart(editorRef.current, editorRef.current.childNodes.length);
range.collapse(true);
sel?.removeAllRanges();
sel?.addRange(range);
editorRef.current.innerHTML = content;
}
}, [activeTab]);
@ -166,20 +181,36 @@ export default function EditarLaudoPage() {
const contentHtml = r.content_html || r.conteudo_html || '';
// Verificar se existe rascunho salvo no localStorage
const draftContent = typeof window !== 'undefined' ? localStorage.getItem(`laudo-draft-${laudoId}`) : null;
const finalContent = draftContent || contentHtml;
let finalContent = contentHtml;
let finalCampos = {
cid: r.cid_code || r.cid || '',
diagnostico: r.diagnosis || r.diagnostico || '',
conclusao: r.conclusion || r.conclusao || '',
exame: r.exam || r.exame || '',
especialidade: r.especialidade || '',
mostrarData: !r.hide_date,
mostrarAssinatura: !r.hide_signature,
};
if (typeof window !== 'undefined') {
const draftData = localStorage.getItem(`laudo-draft-${laudoId}`);
if (draftData) {
try {
const draft = JSON.parse(draftData);
if (draft.content) finalContent = draft.content;
if (draft.campos) finalCampos = { ...finalCampos, ...draft.campos };
} catch (err) {
// Se falhar parse, tentar como string simples (formato antigo)
finalContent = draftData;
}
}
}
setCampos(finalCampos);
setContent(finalContent);
if (editorRef.current) {
editorRef.current.innerHTML = finalContent;
// Colocar cursor no final do texto
editorRef.current.focus();
const range = document.createRange();
const sel = window.getSelection();
range.setStart(editorRef.current, editorRef.current.childNodes.length);
range.collapse(true);
sel?.removeAllRanges();
sel?.addRange(range);
}
} catch (err) {
console.warn('Erro ao carregar laudo:', err);
@ -357,7 +388,7 @@ export default function EditarLaudoPage() {
{/* Tabs */}
<div className="flex border-b border-border bg-card overflow-x-auto flex-shrink-0">
<button
onClick={() => setActiveTab('editor')}
onClick={() => handleTabChange('editor')}
className={`px-2 sm:px-4 py-2 text-xs sm:text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${
activeTab === 'editor'
? 'border-blue-500 text-blue-600'
@ -368,7 +399,7 @@ export default function EditarLaudoPage() {
Editor
</button>
<button
onClick={() => setActiveTab('campos')}
onClick={() => handleTabChange('campos')}
className={`px-2 sm:px-4 py-2 text-xs sm:text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${
activeTab === 'campos'
? 'border-blue-500 text-blue-600'

View File

@ -876,17 +876,6 @@ export default function ResultadosClient() {
</Select>
</div>
{/* Mais filtros / Voltar */}
<div className="sm:col-span-4">
<Button
variant="outline"
className="h-10 w-full rounded-full border border-primary/30 bg-primary/5 text-primary hover:bg-primary hover:text-primary-foreground"
>
<Filter className="mr-2 h-4 w-4" />
Mais filtros
</Button>
</div>
{/* Voltar */}
<div className="sm:col-span-12">
<Button

View File

@ -1396,13 +1396,13 @@ const ProfissionalPage = () => {
{/* Filtros */}
<div className="p-4 border-b border-border">
<div className="flex flex-wrap items-center gap-4">
<div className="flex flex-wrap items-start gap-4">
<div className="relative flex-1 min-w-[200px]">
{/* Search input integrado com busca por ID */}
<SearchBox />
</div>
<div className="flex items-center gap-2">
<div className="flex items-center gap-2 mt-0">
<div className="flex items-center gap-1 text-sm">
<CalendarIcon className="w-4 h-4" />
<Input type="date" value={startDate ?? ''} onChange={(e) => { setStartDate(e.target.value); setSelectedRange('custom'); }} className="p-1 text-sm h-10" />
@ -1411,7 +1411,7 @@ const ProfissionalPage = () => {
</div>
</div>
<div className="flex gap-2 items-center">
<div className="flex gap-2 items-center mt-0">
{/* date range buttons: Semana / Mês */}
<DateRangeButtons />
</div>

View File

@ -79,7 +79,7 @@ export function PagesHeader({ title = "", subtitle = "" }: { title?: string, sub
</Avatar>
</Button>
{dropdownOpen && (
<div className="absolute right-0 mt-2 w-64 sm:w-80 bg-popover border border-border rounded-md shadow-lg z-50 text-popover-foreground animate-in fade-in slide-in-from-top-2">
<div className="absolute right-0 mt-2 w-64 sm:w-80 bg-popover border border-border rounded-md shadow-lg z-[100] text-popover-foreground animate-in fade-in slide-in-from-top-2">
<div className="p-3 sm:p-4 border-b border-border">
<div className="flex flex-col space-y-1">
<p className="text-xs sm:text-sm font-semibold leading-none">