style(HistoriaClinica): Agrupar tabs en menu select unificado y mostrar botones de accion en linea
This commit is contained in:
+137
@@ -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
@@ -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);
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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){}
|
||||
|
||||
@@ -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);
|
||||
@@ -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
@@ -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) {}
|
||||
|
||||
+139
-101
@@ -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>
|
||||
|
||||
<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 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>
|
||||
|
||||
<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 */}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
Reference in New Issue
Block a user