fix(ui): reemplaza panel-debajo-del-listado por DetailPopup de tamaño fijo

El detalle de un item de listado (actividad, departamento, usuario, proyecto nuevo,
catalogo) ya no se apila debajo ni al costado del listado: ahora abre en un popup que
cubre la ventana, deja el listado inactivo detras, nunca se cierra con clic afuera (solo
con el boton Cerrar o una accion explicita del formulario) y mantiene un alto FIJO por
instancia para que cambiar de pestaña interna (Info/Subtareas/Dependencias/Comentarios/
Documentos) no reajuste su tamaño.

De paso corrige un bug real de stacking: ContentDialog no usaba portal a document.body,
así que un dialogo de confirmacion abierto desde dentro de un DetailPopup quedaba oculto
detras de este. Ahora ambos son portales con una jerarquia de z-index explicita
(Flyout 100 < DetailPopup 200 < ContentDialog 250 < Toast 300).

En el detalle de actividad, Subtareas y Dependencias pasan de estar apiladas dentro de la
pestaña Info a ser cada una su propia pestaña.

Regla documentada en project-md-templates/UI-UX-WINDOWS.md (componente DetailPopup) para
que aplique a futuros proyectos de escritorio Windows del grupo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-09-03 11:21:07 -05:00
parent 1d22adc42e
commit 1e6e7fecdd
10 changed files with 224 additions and 142 deletions
@@ -8,6 +8,7 @@ import { Button } from '../../components/Button.jsx';
import { Field, FieldRow } from '../../components/Field.jsx';
import { ComboBox } from '../../components/ComboBox.jsx';
import { Badge } from '../../components/Badge.jsx';
import { DetailPopup } from '../../components/DetailPopup.jsx';
const COLOR_OPTIONS = ['accent', 'success', 'warning', 'danger', 'muted'].map((c) => ({ value: c, label: c }));
@@ -93,35 +94,33 @@ export function CatalogEditor({ catalogKey, fields, title }) {
]}
/>
{editing && (
<div className="panel-card" style={{ marginTop: 12, maxWidth: 480 }}>
<FieldRow>
{fields.map((f) => (
<Field key={f.key} label={f.label}>
{f.type === 'color' ? (
<ComboBox options={COLOR_OPTIONS} value={form[f.key]} onChange={(v) => setForm((s) => ({ ...s, [f.key]: v }))} />
) : f.type === 'checkbox' ? (
<label className="checkbox-row">
<input type="checkbox" checked={!!form[f.key]} onChange={(e) => setForm((s) => ({ ...s, [f.key]: e.target.checked }))} />
{f.label}
</label>
) : (
<input
className="input"
type={f.type === 'number' ? 'number' : 'text'}
value={form[f.key]}
onChange={(e) => setForm((s) => ({ ...s, [f.key]: f.type === 'number' ? Number(e.target.value) : e.target.value }))}
/>
)}
</Field>
))}
</FieldRow>
<div style={{ display: 'flex', gap: 8 }}>
<Button size="sm" variant="accent" icon={Save} onClick={save}>{t('common_save')}</Button>
<Button size="sm" variant="standard" onClick={() => setEditing(false)}>{t('common_cancel')}</Button>
</div>
<DetailPopup open={editing} title={selected ? t('common_edit') : t('common_new')} onClose={() => setEditing(false)} width={480} height={380}>
<FieldRow>
{fields.map((f) => (
<Field key={f.key} label={f.label}>
{f.type === 'color' ? (
<ComboBox options={COLOR_OPTIONS} value={form[f.key]} onChange={(v) => setForm((s) => ({ ...s, [f.key]: v }))} />
) : f.type === 'checkbox' ? (
<label className="checkbox-row">
<input type="checkbox" checked={!!form[f.key]} onChange={(e) => setForm((s) => ({ ...s, [f.key]: e.target.checked }))} />
{f.label}
</label>
) : (
<input
className="input"
type={f.type === 'number' ? 'number' : 'text'}
value={form[f.key]}
onChange={(e) => setForm((s) => ({ ...s, [f.key]: f.type === 'number' ? Number(e.target.value) : e.target.value }))}
/>
)}
</Field>
))}
</FieldRow>
<div style={{ display: 'flex', gap: 8 }}>
<Button size="sm" variant="accent" icon={Save} onClick={save}>{t('common_save')}</Button>
<Button size="sm" variant="standard" onClick={() => setEditing(false)}>{t('common_cancel')}</Button>
</div>
)}
</DetailPopup>
</div>
);
}