style(HistoriaClinica): Agrupar tabs en menu select unificado y mostrar botones de accion en linea

This commit is contained in:
2026-08-12 20:39:17 +00:00
parent 369112d384
commit fce539abc2
14 changed files with 747 additions and 110 deletions
+137
View File
@@ -0,0 +1,137 @@
const fs = require('fs');
const patterns = [
{
// Glucemias
find: `<div className="flex justify-between">\n <div className="flex gap-2">\n {canEdit && (\n <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>\n <Plus className="h-4 w-4 mr-2" />Nueva\n </Button>\n )}\n <Button variant="outline" onClick={() => setEvolDialog(true)} disabled={listGlucemias.length === 0}>\n <TrendingUp className="h-4 w-4 mr-2" />Evolución\n </Button>\n </div>\n </div>`,
replace: `{portalNode ? createPortal(\n <>\n {canEdit && (\n <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>\n <Plus className="h-4 w-4 mr-2" />Nueva\n </Button>\n )}\n <Button variant="outline" onClick={() => setEvolDialog(true)} disabled={listGlucemias.length === 0}>\n <TrendingUp className="h-4 w-4 mr-2" />Evolución\n </Button>\n </>,\n portalNode\n ) : (\n <div className="flex justify-between mb-4">\n <div className="flex gap-2">\n {canEdit && (\n <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>\n <Plus className="h-4 w-4 mr-2" />Nueva\n </Button>\n )}\n <Button variant="outline" onClick={() => setEvolDialog(true)} disabled={listGlucemias.length === 0}>\n <TrendingUp className="h-4 w-4 mr-2" />Evolución\n </Button>\n </div>\n </div>\n )}`
},
{
// Laboratorios
find: `<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">\n {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Laboratorio</Button>}\n <Input placeholder="Buscar por código..." value={searchTerm} onChange={e => setSearchTerm(e.target.value)} className="max-w-xs" />\n </div>`,
replace: `
{portalNode ? createPortal(
<>
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Laboratorio</Button>}
<Input placeholder="Buscar..." value={searchTerm} onChange={e => setSearchTerm(e.target.value)} className="max-w-[150px] h-9" />
</>,
portalNode
) : (
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Laboratorio</Button>}
<Input placeholder="Buscar por código..." value={searchTerm} onChange={e => setSearchTerm(e.target.value)} className="max-w-xs" />
</div>
)}
`.trim()
},
{
// Evoluciones
find: `<div className="flex">\n {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Evolución</Button>}\n </div>`,
replace: `
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Evolución</Button>,
portalNode
) : (
<div className="flex mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Evolución</Button>}
</div>
)}
`.trim()
},
{
// EAB
find: `<div className="flex">\n {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Gasometría</Button>}\n </div>`,
replace: `
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Gasometría</Button>,
portalNode
) : (
<div className="flex mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Gasometría</Button>}
</div>
)}
`.trim()
},
{
// Cultivos
find: `<div className="flex">\n {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Cultivo</Button>}\n </div>`,
replace: `
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Cultivo</Button>,
portalNode
) : (
<div className="flex mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Cultivo</Button>}
</div>
)}
`.trim()
},
{
// Estudios
find: `<div className="flex">\n {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Estudio</Button>}\n </div>`,
replace: `
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Estudio</Button>,
portalNode
) : (
<div className="flex mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Estudio</Button>}
</div>
)}
`.trim()
},
{
// Interconsultas
find: `<div className="flex">\n {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Interconsulta</Button>}\n </div>`,
replace: `
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Interconsulta</Button>,
portalNode
) : (
<div className="flex mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Interconsulta</Button>}
</div>
)}
`.trim()
},
{
// ATB
find: `<div className="flex">\n {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo ATB</Button>}\n </div>`,
replace: `
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo ATB</Button>,
portalNode
) : (
<div className="flex mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo ATB</Button>}
</div>
)}
`.trim()
},
{
// Pendientes
find: `<div className="flex items-center gap-2">\n {canEdit && <Button variant="outline" onClick={() => { setPendienteEditar(null); resetForm(); setModalAbierto(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Pendiente</Button>}\n </div>`,
replace: `
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { setPendienteEditar(null); resetForm(); setModalAbierto(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Pendiente</Button>,
portalNode
) : (
<div className="flex items-center gap-2 mb-4">
{canEdit && <Button variant="outline" onClick={() => { setPendienteEditar(null); resetForm(); setModalAbierto(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Pendiente</Button>}
</div>
)}
`.trim()
}
];
let code = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
for (let p of patterns) {
if (code.includes(p.find)) {
code = code.replace(p.find, p.replace);
} else {
console.log("NOT FOUND:", p.find.substring(0, 50));
}
}
fs.writeFileSync('src/sections/HistoriaClinica.tsx', code);
+24
View File
@@ -0,0 +1,24 @@
const fs = require('fs');
let code = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
const repl = `
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />Nuevo
</Button>,
portalNode
) : (
<div className="flex justify-between mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />Nuevo
</Button>}
</div>
)}
`.trim();
code = code.replace(
/<div className="flex justify-between">\s*\{canEdit && <Button variant="outline" onClick=\{\(\) => \{ reset\(\); setDialog\(true\); \}\}>\s*<Plus className="h-4 w-4 mr-2" \/>Nuevo\s*<\/Button>\}\s*<\/div>/g,
repl
);
fs.writeFileSync('src/sections/HistoriaClinica.tsx', code);
+21
View File
@@ -0,0 +1,21 @@
const fs = require('fs');
function fix(filename) {
let code = fs.readFileSync(filename, 'utf8');
// Regex to find the <div className="flex">...</div> or similar that wraps the add button.
// We can just find `{canEdit && <Button ...` and wrap it in a portal.
// Actually, some have multiple buttons, like SeccionGlucemias has `<Button>Nueva</Button>` and `<Button>Evolución</Button>`
// A more robust approach:
// Instead of replacing the JSX, let's just create a string replacement loop for known patterns.
code = code.replace(
/<div className="flex">\s*(\{canEdit && <Button variant="outline" onClick=\{.*?>.*?<\/Button>\})\s*<\/div>/g,
`{portalNode ? createPortal($1, portalNode) : <div className="flex mb-4">$1</div>}`
);
// Wait, some use `<div className="flex justify-between">` ... Let's just find the first `<div className="flex` block in each Seccion.
fs.writeFileSync(filename, code);
}
// This might be too brittle. I'll edit each manually using sed or perl, or precise strings.
+46
View File
@@ -0,0 +1,46 @@
const fs = require('fs');
let code = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
const regex = /<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">\s*<div className="flex gap-2">\s*\{canEdit && <Button variant="outline" onClick=\{\(\) => \{ reset\(\); setDialog\(true\); \}\}>\s*<Plus className="h-4 w-4 mr-2" \/>\s*Nuevo Estudio\s*<\/Button>\}\s*<\/div>\s*<Select value=\{filtro\} onValueChange=\{\(v: 'todos' \| 'internacion'\) => setFiltro\(v\)\}>\s*<SelectTrigger className="w-\[180px\]">\s*<SelectValue placeholder="Filtrar" \/>\s*<\/SelectTrigger>\s*<SelectContent>\s*<SelectItem value="todos">Todos los estudios<\/SelectItem>\s*<SelectItem value="internacion">Solo esta internación<\/SelectItem>\s*<\/SelectContent>\s*<\/Select>\s*<\/div>/g;
const repl = `
{portalNode ? createPortal(
<>
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />
Nuevo Estudio
</Button>}
<Select value={filtro} onValueChange={(v: 'todos' | 'internacion') => setFiltro(v)}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Filtrar" />
</SelectTrigger>
<SelectContent>
<SelectItem value="todos">Todos los estudios</SelectItem>
<SelectItem value="internacion">Solo esta internación</SelectItem>
</SelectContent>
</Select>
</>,
portalNode
) : (
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-4">
<div className="flex gap-2">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />
Nuevo Estudio
</Button>}
</div>
<Select value={filtro} onValueChange={(v: 'todos' | 'internacion') => setFiltro(v)}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Filtrar" />
</SelectTrigger>
<SelectContent>
<SelectItem value="todos">Todos los estudios</SelectItem>
<SelectItem value="internacion">Solo esta internación</SelectItem>
</SelectContent>
</Select>
</div>
)}
`.trim();
code = code.replace(regex, repl);
fs.writeFileSync('src/sections/HistoriaClinica.tsx', code);
+26
View File
@@ -0,0 +1,26 @@
const fs = require('fs');
let code = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
const replIC = `
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />
Nueva IC
</Button>,
portalNode
) : (
<div className="flex justify-between items-center mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />
Nueva IC
</Button>}
</div>
)}
`.trim();
code = code.replace(
/<div className="flex justify-between items-center">\s*\{canEdit && <Button variant="outline" onClick=\{\(\) => \{ reset\(\); setDialog\(true\); \}\}>\s*<Plus className="h-4 w-4 mr-2" \/>\s*Nueva IC\s*<\/Button>\}\s*<\/div>/g,
replIC
);
fs.writeFileSync('src/sections/HistoriaClinica.tsx', code);
+38
View File
@@ -0,0 +1,38 @@
const fs = require('fs');
let code = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
const regex = /<div className="flex items-center gap-2 w-full sm:w-auto justify-end">\s*<Button variant="outline" size="sm" onClick=\{handleCopiarPendientes\} title="Copiar pendientes activos">\s*<Copy className="h-4 w-4 mr-1\.5" \/>\s*Copiar\s*<\/Button>\s*\{canEdit && \(\s*<Button size="sm" onClick=\{\(\) => \{ resetForm\(\); setModalAbierto\(true\); \}\}>\s*<Plus className="h-4 w-4 mr-1\.5" \/>\s*Nuevo Pendiente\s*<\/Button>\s*\)\}\s*<\/div>/g;
const repl = `
{portalNode ? createPortal(
<>
<Button variant="outline" size="sm" onClick={handleCopiarPendientes} title="Copiar pendientes activos">
<Copy className="h-4 w-4 mr-1.5" />
Copiar
</Button>
{canEdit && (
<Button size="sm" onClick={() => { resetForm(); setModalAbierto(true); }}>
<Plus className="h-4 w-4 mr-1.5" />
Nuevo Pendiente
</Button>
)}
</>,
portalNode
) : (
<div className="flex items-center gap-2 w-full sm:w-auto justify-end mb-4">
<Button variant="outline" size="sm" onClick={handleCopiarPendientes} title="Copiar pendientes activos">
<Copy className="h-4 w-4 mr-1.5" />
Copiar
</Button>
{canEdit && (
<Button size="sm" onClick={() => { resetForm(); setModalAbierto(true); }}>
<Plus className="h-4 w-4 mr-1.5" />
Nuevo Pendiente
</Button>
)}
</div>
)}
`.trim();
code = code.replace(regex, repl);
fs.writeFileSync('src/sections/HistoriaClinica.tsx', code);
+22
View File
@@ -0,0 +1,22 @@
const fs = require('fs');
function fix(filename) {
let code = fs.readFileSync(filename, 'utf8');
// Fix duplicate portalNode
code = code.replace(/portalNode , portalNode/g, 'portalNode');
// Add type definition: replace `canEdit?: boolean;` with `canEdit?: boolean; portalNode?: HTMLDivElement | null;`
code = code.replace(/canEdit\?: boolean;/g, 'canEdit?: boolean;\n portalNode?: HTMLDivElement | null;');
// For SeccionPendientes which has `canEdit: boolean })`
code = code.replace(/canEdit: boolean \}\)/g, 'canEdit: boolean; portalNode?: HTMLDivElement | null; })');
// For SeccionIndicaciones (if it uses `canEdit?: boolean`)
fs.writeFileSync(filename, code);
}
fix('src/sections/HistoriaClinica.tsx');
try { fix('src/sections/SeccionIndicaciones.tsx'); } catch(e){}
+58
View File
@@ -0,0 +1,58 @@
const fs = require('fs');
let code = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
code = code.replace("import { useState } from 'react';", "import { useState } from 'react';\nimport { createPortal } from 'react-dom';");
const portalState = "const [tabActivo, setTabActivo] = useState('evoluciones');\n const [portalNode, setPortalNode] = useState<HTMLDivElement | null>(null);";
code = code.replace("const [tabActivo, setTabActivo] = useState('evoluciones');", portalState);
const tabsListRegex = /<ScrollArea className="w-full rounded-md border bg-background whitespace-nowrap">([\s\S]*?)<\/ScrollArea>/;
const newTabsList = `
<div className="flex flex-col sm:flex-row sm:items-center gap-4 mb-4">
<Select value={tabActivo} onValueChange={setTabActivo}>
<SelectTrigger className="w-full sm:w-[280px] font-semibold bg-gray-50 border-gray-300">
<SelectValue placeholder="Menú de Secciones" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Menú</SelectLabel>
<SelectItem value="evoluciones">Evoluciones ({evoluciones.length})</SelectItem>
<SelectItem value="indicaciones">Indicaciones ({indicacionesActivas.length})</SelectItem>
<SelectItem value="glucemias">Glucemias ({glucemias.length})</SelectItem>
<SelectItem value="laboratorios">Laboratorio ({laboratorios.length})</SelectItem>
<SelectItem value="acidobase">EAB ({acidosBase.length})</SelectItem>
<SelectItem value="cultivos">Cultivos ({cultivos.length})</SelectItem>
<SelectItem value="atb">ATB ({atb.length})</SelectItem>
<SelectItem value="estudios">Estudios ({estudiosComplementarios.length})</SelectItem>
<SelectItem value="interconsultas">Interconsultas ({interconsultas.length})</SelectItem>
<SelectItem value="pendientes">Pendientes ({pendientesActivos.length})</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<div ref={setPortalNode} id="menu-actions" className="flex flex-1 items-center gap-2 overflow-x-auto min-h-[40px]"></div>
</div>
`;
code = code.replace(tabsListRegex, newTabsList.trim());
// We also need to add portalNode={portalNode} to all SeccionXXX components
code = code.replace(/<SeccionGlucemias (.*?)\/>/g, '<SeccionGlucemias portalNode={portalNode} $1/>');
code = code.replace(/<SeccionLaboratorios (.*?)\/>/g, '<SeccionLaboratorios portalNode={portalNode} $1/>');
code = code.replace(/<SeccionEvoluciones (.*?)\/>/g, '<SeccionEvoluciones portalNode={portalNode} $1/>');
code = code.replace(/<SeccionAcidosBase (.*?)\/>/g, '<SeccionAcidosBase portalNode={portalNode} $1/>');
code = code.replace(/<SeccionCultivos (.*?)\/>/g, '<SeccionCultivos portalNode={portalNode} $1/>');
code = code.replace(/<SeccionATB (.*?)\/>/g, '<SeccionATB portalNode={portalNode} $1/>');
code = code.replace(/<SeccionIndicaciones (.*?)\/>/g, '<SeccionIndicaciones portalNode={portalNode} $1/>');
code = code.replace(/<SeccionEstudiosComplementarios (.*?)\/>/g, '<SeccionEstudiosComplementarios portalNode={portalNode} $1/>');
code = code.replace(/<SeccionInterconsultas (.*?)\/>/g, '<SeccionInterconsultas portalNode={portalNode} $1/>');
code = code.replace(/<SeccionPendientes (.*?)\/>/g, '<SeccionPendientes portalNode={portalNode} $1/>');
// Update signatures
function updateSignature(name) {
const regex = new RegExp(`function ${name}\\(\\{ (.*?)canEdit \\}: \\{([\\s\\S]*?)canEdit\\?: boolean;([\\s\\S]*?)\\}\\) \\{`, 'g');
// Wait, some might not have canEdit?: boolean optional.
}
// Actually, it's easier to do this individually or just with a regex
fs.writeFileSync('src/sections/HistoriaClinica.tsx', code);
+39
View File
@@ -0,0 +1,39 @@
const fs = require('fs');
let code = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
code = code.replace("import { useState } from 'react';", "import { useState } from 'react';\nimport { createPortal } from 'react-dom';");
const portalState = "const [tabActivo, setTabActivo] = useState('evoluciones');\n const [portalNode, setPortalNode] = useState<HTMLDivElement | null>(null);";
code = code.replace("const [tabActivo, setTabActivo] = useState('evoluciones');", portalState);
const tabsListRegex = /<ScrollArea className="w-full rounded-md border bg-background whitespace-nowrap">([\s\S]*?)<\/ScrollArea>/;
const newTabsList = `
<div className="flex flex-col sm:flex-row sm:items-center gap-4 mb-4">
<Select value={tabActivo} onValueChange={setTabActivo}>
<SelectTrigger className="w-full sm:w-[280px] font-semibold bg-gray-50 border-gray-300">
<SelectValue placeholder="Menú de Secciones" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Menú</SelectLabel>
<SelectItem value="evoluciones">Evoluciones ({evoluciones.length})</SelectItem>
<SelectItem value="indicaciones">Indicaciones ({indicacionesActivas.length})</SelectItem>
<SelectItem value="glucemias">Glucemias ({glucemias.length})</SelectItem>
<SelectItem value="laboratorios">Laboratorio ({laboratorios.length})</SelectItem>
<SelectItem value="acidobase">EAB ({acidosBase.length})</SelectItem>
<SelectItem value="cultivos">Cultivos ({cultivos.length})</SelectItem>
<SelectItem value="atb">ATB ({atb.length})</SelectItem>
<SelectItem value="estudios">Estudios ({estudiosComplementarios.length})</SelectItem>
<SelectItem value="interconsultas">Interconsultas ({interconsultas.length})</SelectItem>
<SelectItem value="pendientes">Pendientes ({pendientesActivos.length})</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<div ref={setPortalNode} id="menu-actions" className="flex flex-1 items-center gap-2 overflow-x-auto min-h-[40px]"></div>
</div>
`.trim();
code = code.replace(tabsListRegex, newTabsList);
fs.writeFileSync('src/sections/HistoriaClinica.tsx', code);
+45
View File
@@ -0,0 +1,45 @@
const fs = require('fs');
function processFile(filename) {
let code = fs.readFileSync(filename, 'utf8');
// Add import if not present
if (!code.includes("import { createPortal }")) {
code = code.replace("import { useState }", "import { useState } from 'react';\nimport { createPortal }");
if (!code.includes("import { createPortal }")) {
code = "import { createPortal } from 'react-dom';\n" + code;
}
}
// Update component signatures
const components = [
'SeccionGlucemias', 'SeccionLaboratorios', 'SeccionEvoluciones',
'SeccionAcidosBase', 'SeccionCultivos', 'SeccionATB',
'SeccionEstudiosComplementarios', 'SeccionInterconsultas', 'SeccionPendientes',
'SeccionIndicaciones'
];
components.forEach(name => {
// 1. Add portalNode to the destructured props
const regex1 = new RegExp(`function ${name}\\(\\{ (.*?) \\}: \\{`, 'g');
code = code.replace(regex1, `function ${name}({ $1, portalNode }: {`);
// Some don't have spaces around the brackets
const regex1b = new RegExp(`function ${name}\\(\\{(.*?)\\}: \\{`, 'g');
code = code.replace(regex1b, `function ${name}({$1, portalNode}: {`);
// 2. Add portalNode?: HTMLDivElement | null; to the type definition
// Usually ends with canEdit?: boolean; }) or canEdit: boolean })
const regex2 = new RegExp(`(canEdit\\??: boolean;?\\s*)\\}\\)`, 'g');
// Actually we can just find the end of the props type definition for that component.
// It's safer to just replace `canEdit` or something similar, but let's do a more robust approach.
});
fs.writeFileSync(filename, code);
}
processFile('src/sections/HistoriaClinica.tsx');
try {
processFile('src/sections/SeccionIndicaciones.tsx');
} catch (e) {}
+138 -100
View File
@@ -1,4 +1,5 @@
import { useState } from 'react';
import { createPortal } from 'react-dom';
import { toast } from 'sonner';
import { User } from 'lucide-react';
import { useHospitalStore } from '@/hooks/useHospitalStore';
@@ -249,6 +250,7 @@ export function HistoriaClinica({
canEdit,
}: HistoriaClinicaProps) {
const [tabActivo, setTabActivo] = useState('evoluciones');
const [portalNode, setPortalNode] = useState<HTMLDivElement | null>(null);
const { pendientes } = useHospitalStore();
const indicacionesActivas = (indicadores || []).filter(i => i.estado === 'Activa');
const pendientesActivos = (pendientes || []).filter(p => p.pacienteId === paciente.id && p.estado === 'pendiente');
@@ -536,81 +538,49 @@ export function HistoriaClinica({
<div className="grid grid-cols-1 w-full">
<div className="w-full overflow-x-auto rounded-md pb-2">
<Tabs className="w-full" value={tabActivo} onValueChange={setTabActivo}>
<ScrollArea className="w-full rounded-md border bg-background whitespace-nowrap">
<TabsList className="w-full">
<TabsTrigger value="evoluciones">
<FileText className="h-3 w-3 sm:h-4 sm:w-4" />
<span>Evoluciones</span>
({evoluciones.length})
</TabsTrigger>
<div className="flex flex-col sm:flex-row sm:items-center gap-4 mb-4">
<Select value={tabActivo} onValueChange={setTabActivo}>
<SelectTrigger className="w-full sm:w-[280px] font-semibold bg-gray-50 border-gray-300">
<SelectValue placeholder="Menú de Secciones" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Menú</SelectLabel>
<SelectItem value="evoluciones">Evoluciones ({evoluciones.length})</SelectItem>
<SelectItem value="indicaciones">Indicaciones ({indicacionesActivas.length})</SelectItem>
<SelectItem value="glucemias">Glucemias ({glucemias.length})</SelectItem>
<SelectItem value="laboratorios">Laboratorio ({laboratorios.length})</SelectItem>
<SelectItem value="acidobase">EAB ({acidosBase.length})</SelectItem>
<SelectItem value="cultivos">Cultivos ({cultivos.length})</SelectItem>
<SelectItem value="atb">ATB ({atb.length})</SelectItem>
<SelectItem value="estudios">Estudios ({estudiosComplementarios.length})</SelectItem>
<SelectItem value="interconsultas">Interconsultas ({interconsultas.length})</SelectItem>
<SelectItem value="pendientes">Pendientes ({pendientesActivos.length})</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<TabsTrigger value="indicaciones">
<List className="h-3 w-3 sm:h-4 sm:w-4" />
<span>Indicaciones</span>
({indicacionesActivas.length})
</TabsTrigger>
<TabsTrigger value="glucemias">
<Droplet className="h-3 w-3 sm:h-4 sm:w-4" />
<span>Glucemias</span>
({glucemias.length})
</TabsTrigger>
<TabsTrigger value="laboratorios">
<FlaskConical className="h-3 w-3 sm:h-4 sm:w-4" />
<span>Laboratorio</span>
({laboratorios.length})
</TabsTrigger>
<TabsTrigger value="acidobase">
<Activity className="h-3 w-3 sm:h-4 sm:w-4" />
<span>EAB</span>
({acidosBase.length})
</TabsTrigger>
<TabsTrigger value="cultivos">
<Microscope className="h-3 w-3 sm:h-4 sm:w-4" />
<span>Cultivos</span>
({cultivos.length})
</TabsTrigger>
<TabsTrigger value="atb">
<Pill className="h-3 w-3 sm:h-4 sm:w-4" />
<span>ATB</span>
({atb.length})
</TabsTrigger>
<TabsTrigger value="estudios">
<ClipboardList className="h-3 w-3 sm:h-4 sm:w-4" />
<span>Estudios</span>
({estudiosComplementarios.length})
</TabsTrigger>
<TabsTrigger value="interconsultas">
<Users className="h-3 w-3 sm:h-4 sm:w-4" />
<span>Interconsultas</span>
({interconsultas.length})
</TabsTrigger>
<TabsTrigger value="pendientes">
<ListTodo className="h-3 w-3 sm:h-4 sm:w-4" />
<span>Pendientes</span>
({pendientesActivos.length})
</TabsTrigger>
</TabsList>
<ScrollBar orientation="horizontal" />
</ScrollArea>
<div ref={setPortalNode} id="menu-actions" className="flex flex-1 items-center gap-2 overflow-x-auto min-h-[40px]"></div>
</div>
<TabsContent value="glucemias" className="mt-4 min-w-0 w-full overflow-hidden">
<SeccionGlucemias glucemias={glucemias} patientId={paciente.id} internacionId={internacion.id} add={onAgregarGlucemia} update={onActualizarGlucemia} del={onEliminarGlucemia} canEdit={canEdit} />
<SeccionGlucemias portalNode={portalNode} glucemias={glucemias} patientId={paciente.id} internacionId={internacion.id} add={onAgregarGlucemia} update={onActualizarGlucemia} del={onEliminarGlucemia} canEdit={canEdit} />
</TabsContent>
<TabsContent value="laboratorios" className="mt-4 min-w-0 w-full overflow-hidden">
<SeccionLaboratorios lab={laboratorios} patientId={paciente.id} internacionId={internacion.id} add={onAgregarLaboratorio} update={onActualizarLaboratorio} del={onEliminarLaboratorio} addAcidoBase={onAgregarAcidoBase} canEdit={canEdit} />
<SeccionLaboratorios portalNode={portalNode} lab={laboratorios} patientId={paciente.id} internacionId={internacion.id} add={onAgregarLaboratorio} update={onActualizarLaboratorio} del={onEliminarLaboratorio} addAcidoBase={onAgregarAcidoBase} canEdit={canEdit} />
</TabsContent>
<TabsContent value="evoluciones" className="mt-4">
<SeccionEvoluciones evos={evoluciones} internacionId={internacion.id} add={onAgregarEvolucion} update={onActualizarEvolucion} del={onEliminarEvolucion} canEdit={canEdit} />
<SeccionEvoluciones portalNode={portalNode} evos={evoluciones} internacionId={internacion.id} add={onAgregarEvolucion} update={onActualizarEvolucion} del={onEliminarEvolucion} canEdit={canEdit} />
</TabsContent>
<TabsContent value="acidobase" className="mt-4">
<SeccionAcidosBase ab={acidosBase} patientId={paciente.id} internacionId={internacion.id} add={onAgregarAcidoBase} update={onActualizarAcidoBase} del={onEliminarAcidoBase} canEdit={canEdit} />
<SeccionAcidosBase portalNode={portalNode} ab={acidosBase} patientId={paciente.id} internacionId={internacion.id} add={onAgregarAcidoBase} update={onActualizarAcidoBase} del={onEliminarAcidoBase} canEdit={canEdit} />
</TabsContent>
<TabsContent value="cultivos" className="mt-4">
<SeccionCultivos cults={cultivos} patient={paciente} internacionId={internacion.id} add={onAgregarCultivo} update={onActualizarCultivo} del={onEliminarCultivo} canEdit={canEdit} />
<SeccionCultivos portalNode={portalNode} cults={cultivos} patient={paciente} internacionId={internacion.id} add={onAgregarCultivo} update={onActualizarCultivo} del={onEliminarCultivo} canEdit={canEdit} />
</TabsContent>
<TabsContent value="atb" className="mt-4">
@@ -680,7 +650,7 @@ export function HistoriaClinica({
);
}
function SeccionGlucemias({ glucemias, patientId, internacionId, add, update, del, canEdit }: {
function SeccionGlucemias({ glucemias, patientId, internacionId, add, update, del, canEdit, portalNode}: {
glucemias: Glucemia[];
patientId: string;
internacionId: string;
@@ -688,6 +658,7 @@ function SeccionGlucemias({ glucemias, patientId, internacionId, add, update, de
update: (id: string, data: Partial<Glucemia>) => void;
del: (id: string) => void;
canEdit?: boolean;
portalNode?: HTMLDivElement | null;
}) {
const [dialog, setDialog] = useState(false);
const [evolDialog, setEvolDialog] = useState(false);
@@ -768,8 +739,8 @@ function SeccionGlucemias({ glucemias, patientId, internacionId, add, update, de
return (
<div className="space-y-4 w-full max-w-full min-w-0">
<div className="flex justify-between">
<div className="flex gap-2">
{portalNode ? createPortal(
<>
{canEdit && (
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />Nueva
@@ -778,8 +749,22 @@ function SeccionGlucemias({ glucemias, patientId, internacionId, add, update, de
<Button variant="outline" onClick={() => setEvolDialog(true)} disabled={listGlucemias.length === 0}>
<TrendingUp className="h-4 w-4 mr-2" />Evolución
</Button>
</>,
portalNode
) : (
<div className="flex justify-between mb-4">
<div className="flex gap-2">
{canEdit && (
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />Nueva
</Button>
)}
<Button variant="outline" onClick={() => setEvolDialog(true)} disabled={listGlucemias.length === 0}>
<TrendingUp className="h-4 w-4 mr-2" />Evolución
</Button>
</div>
</div>
</div>
)}
<Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-md">
@@ -940,7 +925,7 @@ function SeccionGlucemias({ glucemias, patientId, internacionId, add, update, de
);
}
function SeccionLaboratorios({ lab, patientId, internacionId, add, update, del, addAcidoBase, canEdit }: {
function SeccionLaboratorios({ lab, patientId, internacionId, add, update, del, addAcidoBase, canEdit, portalNode}: {
lab: Laboratorio[];
patientId: string;
internacionId: string;
@@ -949,6 +934,7 @@ function SeccionLaboratorios({ lab, patientId, internacionId, add, update, del,
del: (id: string) => void;
addAcidoBase?: (a: Omit<AcidoBase, 'id'>) => void;
canEdit?: boolean;
portalNode?: HTMLDivElement | null;
}) {
const [dialog, setDialog] = useState(false);
const [obsDialog, setObsDialog] = useState(false);
@@ -1864,13 +1850,14 @@ function SeccionLaboratorios({ lab, patientId, internacionId, add, update, del,
);
}
function SeccionEvoluciones({ evos, internacionId, add, update, del, canEdit }: {
function SeccionEvoluciones({ evos, internacionId, add, update, del, canEdit, portalNode}: {
evos: Evolucion[];
internacionId: string;
add: (e: Omit<Evolucion, 'id'>) => void;
update: (id: string, data: Partial<Evolucion>) => void;
del: (id: string) => void;
canEdit?: boolean;
portalNode?: HTMLDivElement | null;
}) {
const { currentUser } = useHospitalStore();
const [dialog, setDialog] = useState(false);
@@ -1974,9 +1961,14 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del, canEdit }:
return (
<div className="space-y-4">
<div className="flex">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Evolución</Button>}
</div>
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Evolución</Button>,
portalNode
) : (
<div className="flex mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Evolución</Button>}
</div>
)}
<Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto">
<DialogHeader><DialogTitle>{edit ? 'Editar' : 'Nueva'} Evolución</DialogTitle></DialogHeader>
@@ -2181,7 +2173,7 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del, canEdit }:
);
}
function SeccionAcidosBase({ ab, patientId, internacionId, add, update, del, canEdit }: {
function SeccionAcidosBase({ ab, patientId, internacionId, add, update, del, canEdit, portalNode}: {
ab: AcidoBase[];
patientId: string;
internacionId: string;
@@ -2189,6 +2181,7 @@ function SeccionAcidosBase({ ab, patientId, internacionId, add, update, del, can
update: (id: string, datos: Partial<AcidoBase>) => void;
del: (id: string) => void;
canEdit?: boolean;
portalNode?: HTMLDivElement | null;
}) {
const [dialog, setDialog] = useState(false);
const [editDialog, setEditDialog] = useState(false);
@@ -2260,9 +2253,14 @@ function SeccionAcidosBase({ ab, patientId, internacionId, add, update, del, can
return (
<div className="space-y-4">
<div className="flex">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Gasometría</Button>}
</div>
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Gasometría</Button>,
portalNode
) : (
<div className="flex mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Gasometría</Button>}
</div>
)}
<Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader><DialogTitle>Nueva Gasometría Arterial</DialogTitle></DialogHeader>
@@ -2400,7 +2398,7 @@ function SeccionAcidosBase({ ab, patientId, internacionId, add, update, del, can
);
}
function SeccionCultivos({ cults, patient, internacionId, add, update, del, canEdit }: {
function SeccionCultivos({ cults, patient, internacionId, add, update, del, canEdit, portalNode}: {
cults: Cultivo[];
patient: Paciente;
internacionId: string;
@@ -2408,6 +2406,7 @@ function SeccionCultivos({ cults, patient, internacionId, add, update, del, canE
update: (id: string, data: Partial<Cultivo>) => void;
del: (id: string) => void;
canEdit?: boolean;
portalNode?: HTMLDivElement | null;
}) {
const [dialog, setDialog] = useState(false);
const [resDialog, setResDialog] = useState(false);
@@ -2505,9 +2504,14 @@ function SeccionCultivos({ cults, patient, internacionId, add, update, del, canE
return (
<div className="space-y-4">
<div className="flex">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Cultivo</Button>}
</div>
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Cultivo</Button>,
portalNode
) : (
<div className="flex mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Cultivo</Button>}
</div>
)}
<Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-lg">
<DialogHeader><DialogTitle>Nuevo Cultivo</DialogTitle></DialogHeader>
@@ -2698,7 +2702,7 @@ function SeccionCultivos({ cults, patient, internacionId, add, update, del, canE
);
}
function SeccionEstudiosComplementarios({ estudios, internacionId, pacienteId, add, update, del, canEdit }: {
function SeccionEstudiosComplementarios({ estudios, internacionId, pacienteId, add, update, del, canEdit, portalNode}: {
estudios: EstudioComplementario[];
internacionId: string;
pacienteId: string;
@@ -2706,6 +2710,7 @@ function SeccionEstudiosComplementarios({ estudios, internacionId, pacienteId, a
update: (id: string, datos: Partial<EstudioComplementario>) => void;
del: (id: string) => void;
canEdit?: boolean;
portalNode?: HTMLDivElement | null;
}) {
const [dialog, setDialog] = useState(false);
const [filtro, setFiltro] = useState<'todos' | 'internacion'>('internacion');
@@ -2847,7 +2852,7 @@ function SeccionEstudiosComplementarios({ estudios, internacionId, pacienteId, a
);
}
function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add, update, del, canEdit }: {
function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add, update, del, canEdit, portalNode}: {
interconsultas: Interconsulta[];
pacienteId: string;
internacionId: string;
@@ -2855,6 +2860,7 @@ function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add,
update: (id: string, datos: Partial<Interconsulta>) => void;
del: (id: string) => void;
canEdit?: boolean;
portalNode?: HTMLDivElement | null;
}) {
const [dialog, setDialog] = useState(false);
const [fecha, setFecha] = useState(getLocalToday());
@@ -2902,12 +2908,20 @@ function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add,
return (
<div className="space-y-4">
<div className="flex justify-between items-center">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />
Nueva IC
</Button>}
</div>
</Button>,
portalNode
) : (
<div className="flex justify-between items-center mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />
Nueva IC
</Button>}
</div>
)}
<Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-lg">
@@ -3020,7 +3034,7 @@ function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add,
);
}
function SeccionATB({ atb, internacionId, pacienteId, add, update, del, canEdit }: {
function SeccionATB({ atb, internacionId, pacienteId, add, update, del, canEdit, portalNode}: {
atb: ATB[];
internacionId: string;
pacienteId: string;
@@ -3028,6 +3042,7 @@ function SeccionATB({ atb, internacionId, pacienteId, add, update, del, canEdit
update: (id: string, data: Partial<ATB>) => void;
del: (id: string) => void;
canEdit?: boolean;
portalNode?: HTMLDivElement | null;
}) {
const [dialog, setDialog] = useState(false);
const [edit, setEdit] = useState<ATB | null>(null);
@@ -3085,11 +3100,18 @@ function SeccionATB({ atb, internacionId, pacienteId, add, update, del, canEdit
return (
<div className="space-y-4">
<div className="flex justify-between">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />Nuevo
</Button>}
</div>
</Button>,
portalNode
) : (
<div className="flex justify-between mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />Nuevo
</Button>}
</div>
)}
<Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-md">
@@ -3159,7 +3181,7 @@ function SeccionATB({ atb, internacionId, pacienteId, add, update, del, canEdit
);
}
function SeccionPendientes({ pacienteId, internacionId, canEdit }: { pacienteId: string; internacionId: string; canEdit: boolean }) {
function SeccionPendientes({ pacienteId, internacionId, canEdit, portalNode}: { pacienteId: string; internacionId: string; canEdit: boolean; portalNode?: HTMLDivElement | null; }) {
const { pendientes, agregarPendiente, actualizarPendiente, eliminarPendiente, currentUser } = useHospitalStore();
const [modalAbierto, setModalAbierto] = useState(false);
const [pendienteEditar, setPendienteEditar] = useState<Pendiente | null>(null);
@@ -3368,18 +3390,34 @@ function SeccionPendientes({ pacienteId, internacionId, canEdit }: { pacienteId:
</Select>
</div>
<div className="flex items-center gap-2 w-full sm:w-auto justify-end">
<Button variant="outline" size="sm" onClick={handleCopiarPendientes} title="Copiar pendientes activos">
<Copy className="h-4 w-4 mr-1.5" />
Copiar
</Button>
{canEdit && (
<Button size="sm" onClick={() => { resetForm(); setModalAbierto(true); }}>
<Plus className="h-4 w-4 mr-1.5" />
Nuevo Pendiente
{portalNode ? createPortal(
<>
<Button variant="outline" size="sm" onClick={handleCopiarPendientes} title="Copiar pendientes activos">
<Copy className="h-4 w-4 mr-1.5" />
Copiar
</Button>
)}
</div>
{canEdit && (
<Button size="sm" onClick={() => { resetForm(); setModalAbierto(true); }}>
<Plus className="h-4 w-4 mr-1.5" />
Nuevo Pendiente
</Button>
)}
</>,
portalNode
) : (
<div className="flex items-center gap-2 w-full sm:w-auto justify-end mb-4">
<Button variant="outline" size="sm" onClick={handleCopiarPendientes} title="Copiar pendientes activos">
<Copy className="h-4 w-4 mr-1.5" />
Copiar
</Button>
{canEdit && (
<Button size="sm" onClick={() => { resetForm(); setModalAbierto(true); }}>
<Plus className="h-4 w-4 mr-1.5" />
Nuevo Pendiente
</Button>
)}
</div>
)}
</div>
{/* List of Pendientes */}
+23 -9
View File
@@ -1,4 +1,5 @@
import { useState } from 'react';
import { createPortal } from 'react';
import { toast } from 'sonner';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
@@ -13,7 +14,7 @@ import type { Indicacion, IndicacionTipo, ViaAdministracion, TipoPlanHidratacion
import { useHospitalStore } from '@/hooks/useHospitalStore';
import { getNombreProfesional, getLocalToday } from '@/lib/utils';
export function SeccionIndicaciones({ recomendaciones, internacionId, pacienteId, add, update, del, movimientos, onAgregarMovimiento, canEdit, atbList = [], addATB, updateATB }: {
export function SeccionIndicaciones({ recomendaciones, internacionId, pacienteId, add, update, del, movimientos, onAgregarMovimiento, canEdit, atbList = [], addATB, updateATB, portalNode}: {
recomendaciones: Indicacion[];
internacionId: string;
pacienteId?: string;
@@ -23,6 +24,7 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, pacienteId
movimientos?: unknown[];
onAgregarMovimiento?: (m: unknown) => void;
canEdit?: boolean;
portalNode?: HTMLDivElement | null;
atbList?: ATB[];
addATB?: (a: Omit<ATB, 'id'>) => Promise<unknown>;
updateATB?: (id: string, datos: Partial<ATB>) => void;
@@ -283,14 +285,26 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, pacienteId
return (
<div className="space-y-2.5">
<div className="flex justify-between items-center">
{canEdit && <Button size="sm" variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-1.5" />Nueva Indicación
</Button>}
<Button size="sm" variant="ghost" onClick={() => setShowHistorial(!showHistorial)}>
{showHistorial ? 'Ocultar Historial' : `Ver Historial (${movs.length})`}
</Button>
</div>
{portalNode ? createPortal(
<>
{canEdit && <Button size="sm" variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-1.5" />Nueva Indicación
</Button>}
<Button size="sm" variant="ghost" onClick={() => setShowHistorial(!showHistorial)}>
{showHistorial ? 'Ocultar Historial' : `Ver Historial (${movs.length})`}
</Button>
</>,
portalNode
) : (
<div className="flex justify-between items-center mb-4">
{canEdit && <Button size="sm" variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-1.5" />Nueva Indicación
</Button>}
<Button size="sm" variant="ghost" onClick={() => setShowHistorial(!showHistorial)}>
{showHistorial ? 'Ocultar Historial' : `Ver Historial (${movs.length})`}
</Button>
</div>
)}
<Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-lg">
+92
View File
@@ -0,0 +1,92 @@
const fs = require('fs');
let hc = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
// Laboratorios
hc = hc.replace(
/<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">\s*\{canEdit && <Button variant="outline" onClick=\{\(\) => \{ reset\(\); setDialog\(true\); \}\}><Plus className="h-4 w-4 mr-2" \/>Nuevo Laboratorio<\/Button>\}\s*<Input placeholder="Buscar por código..." value=\{searchTerm\} onChange=\{e => setSearchTerm\(e\.target\.value\)\} className="max-w-xs" \/>\s*<\/div>/g,
`{portalNode ? createPortal(
<>
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Laboratorio</Button>}
<Input placeholder="Buscar por código..." value={searchTerm} onChange={e => setSearchTerm(e.target.value)} className="max-w-xs" />
</>,
portalNode
) : (
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Laboratorio</Button>}
<Input placeholder="Buscar por código..." value={searchTerm} onChange={e => setSearchTerm(e.target.value)} className="max-w-xs" />
</div>
)}`
);
// General pattern for those inside `<div className="flex">`
const blocks = [
'Nueva Evolución',
'Nueva Gasometría',
'Nuevo Cultivo',
'Nuevo Estudio',
'Nueva Interconsulta',
'Nuevo ATB'
];
blocks.forEach(text => {
const regex = new RegExp(`<div className="flex">\\s*\\{canEdit && <Button variant="outline" onClick=\\{\\(\\) => \\{ reset\\(\\); setDialog\\(true\\); \\}\\}\\><Plus className="h-4 w-4 mr-2" \\/>${text}<\\/Button>\\}\\s*<\\/div>`, 'g');
hc = hc.replace(regex, `{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />${text}</Button>,
portalNode
) : (
<div className="flex mb-4">
{canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />${text}</Button>}
</div>
)}`);
});
// Pendientes
hc = hc.replace(
/<div className="flex items-center gap-2">\s*\{canEdit && <Button variant="outline" onClick=\{\(\) => \{ setPendienteEditar\(null\); resetForm\(\); setModalAbierto\(true\); \}\}><Plus className="h-4 w-4 mr-2" \/>Nuevo Pendiente<\/Button>\}\s*<\/div>/g,
`{portalNode ? createPortal(
canEdit && <Button variant="outline" onClick={() => { setPendienteEditar(null); resetForm(); setModalAbierto(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Pendiente</Button>,
portalNode
) : (
<div className="flex items-center gap-2 mb-4">
{canEdit && <Button variant="outline" onClick={() => { setPendienteEditar(null); resetForm(); setModalAbierto(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Pendiente</Button>}
</div>
)}`
);
fs.writeFileSync('src/sections/HistoriaClinica.tsx', hc);
let si = fs.readFileSync('src/sections/SeccionIndicaciones.tsx', 'utf8');
// Also import createPortal
if (!si.includes("createPortal")) {
si = si.replace("import { useState }", "import { useState } from 'react';\nimport { createPortal }");
}
si = si.replace(
/<div className="flex justify-between items-center">\s*\{canEdit && <Button size="sm" variant="outline" onClick=\{\(\) => \{ reset\(\); setDialog\(true\); \}\}>\s*<Plus className="h-4 w-4 mr-1\.5" \/>Nueva Indicación\s*<\/Button>\}\s*<Button size="sm" variant="ghost" onClick=\{\(\) => setShowHistorial\(!showHistorial\)\}>\s*\{showHistorial \? 'Ocultar Historial' : \`Ver Historial \(\$\{movs\.length\}\)\`\}\s*<\/Button>\s*<\/div>/g,
`{portalNode ? createPortal(
<>
{canEdit && <Button size="sm" variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-1.5" />Nueva Indicación
</Button>}
<Button size="sm" variant="ghost" onClick={() => setShowHistorial(!showHistorial)}>
{showHistorial ? 'Ocultar Historial' : \`Ver Historial (\${movs.length})\`}
</Button>
</>,
portalNode
) : (
<div className="flex justify-between items-center mb-4">
{canEdit && <Button size="sm" variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-1.5" />Nueva Indicación
</Button>}
<Button size="sm" variant="ghost" onClick={() => setShowHistorial(!showHistorial)}>
{showHistorial ? 'Ocultar Historial' : \`Ver Historial (\${movs.length})\`}
</Button>
</div>
)}`
);
fs.writeFileSync('src/sections/SeccionIndicaciones.tsx', si);
+37
View File
@@ -0,0 +1,37 @@
const fs = require('fs');
let code = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
const regex = /<div className="flex justify-between">\s*<div className="flex gap-2">\s*\{canEdit && \(\s*<Button variant="outline" onClick=\{.*?>\s*<Plus className="h-4 w-4 mr-2" \/>Nueva\s*<\/Button>\s*\)\}\s*<Button variant="outline" onClick=\{.*?\} disabled=\{listGlucemias.length === 0\}>\s*<TrendingUp className="h-4 w-4 mr-2" \/>Evolución\s*<\/Button>\s*<\/div>\s*<\/div>/g;
const replacement = `
{portalNode ? createPortal(
<>
{canEdit && (
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />Nueva
</Button>
)}
<Button variant="outline" onClick={() => setEvolDialog(true)} disabled={listGlucemias.length === 0}>
<TrendingUp className="h-4 w-4 mr-2" />Evolución
</Button>
</>,
portalNode
) : (
<div className="flex justify-between mb-4">
<div className="flex gap-2">
{canEdit && (
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />Nueva
</Button>
)}
<Button variant="outline" onClick={() => setEvolDialog(true)} disabled={listGlucemias.length === 0}>
<TrendingUp className="h-4 w-4 mr-2" />Evolución
</Button>
</div>
</div>
)}
`.trim();
code = code.replace(regex, replacement);
fs.writeFileSync('src/sections/HistoriaClinica.tsx', code);