fix(ui): elimina los 2 casos que quedaban de formulario debajo de un listado

Auditoria completa del frontend: quedaban 2 lugares con el mismo antipatron que
ya se habia corregido en Proyectos/Departamentos/Usuarios/Catalogos.

- ProjectActivitiesTab: "Nueva actividad" se apilaba debajo del listado de
  actividades. Ahora abre en su propio DetailPopup, igual que el resto.
- ProjectInfoTab (pestaña Info de un proyecto): el combo "agregar miembro" se
  apilaba debajo de la lista de miembros. Ahora hay un boton "Agregar miembro"
  en el encabezado de esa tarjeta que abre un DetailPopup chico.

Con esto no queda ningun listado del sistema con un formulario apilado debajo:
todos usan DetailPopup (ver regla en project-md-templates/UI-UX-WINDOWS.md).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-09-03 11:39:35 -05:00
parent b172a8f087
commit 2661d31977
2 changed files with 52 additions and 44 deletions
@@ -88,9 +88,7 @@ export default function ProjectActivitiesTab({ projectId }) {
onRowClick={openActivity} onRowClick={openActivity}
/> />
{creating && ( <DetailPopup open={creating} title={t('activity_new')} onClose={() => setCreating(false)} width={640} height={560}>
<div className="panel-card" style={{ marginTop: 16 }}>
<h3>{t('activity_new')}</h3>
<Field label={t('common_name')}> <Field label={t('common_name')}>
<input className="input" value={form.name} onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))} autoFocus /> <input className="input" value={form.name} onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))} autoFocus />
</Field> </Field>
@@ -126,8 +124,7 @@ export default function ProjectActivitiesTab({ projectId }) {
<Button variant="accent" icon={Save} onClick={save}>{t('common_save')}</Button> <Button variant="accent" icon={Save} onClick={save}>{t('common_save')}</Button>
<Button variant="standard" onClick={() => setCreating(false)}>{t('common_cancel')}</Button> <Button variant="standard" onClick={() => setCreating(false)}>{t('common_cancel')}</Button>
</div> </div>
</div> </DetailPopup>
)}
<DetailPopup open={!!selected} title={selected ? `${selected.code} — ${selected.name}` : ''} onClose={() => setSelected(null)} width={720} height={680}> <DetailPopup open={!!selected} title={selected ? `${selected.code} — ${selected.name}` : ''} onClose={() => setSelected(null)} width={720} height={680}>
{selected && ( {selected && (
@@ -11,6 +11,7 @@ import { Field, FieldRow } from '../../components/Field.jsx';
import { ComboBox } from '../../components/ComboBox.jsx'; import { ComboBox } from '../../components/ComboBox.jsx';
import { StatusBadge, ProgressBar } from '../../components/StatusBadge.jsx'; import { StatusBadge, ProgressBar } from '../../components/StatusBadge.jsx';
import { Badge } from '../../components/Badge.jsx'; import { Badge } from '../../components/Badge.jsx';
import { DetailPopup } from '../../components/DetailPopup.jsx';
function flatten(nodes, acc = []) { function flatten(nodes, acc = []) {
for (const n of nodes) { acc.push(n); if (n.children?.length) flatten(n.children, acc); } for (const n of nodes) { acc.push(n); if (n.children?.length) flatten(n.children, acc); }
@@ -42,6 +43,7 @@ export default function ProjectInfoTab({ project, onChanged }) {
const [editing, setEditing] = useState(false); const [editing, setEditing] = useState(false);
const [form, setForm] = useState(() => toForm(project)); const [form, setForm] = useState(() => toForm(project));
const [addingMember, setAddingMember] = useState(''); const [addingMember, setAddingMember] = useState('');
const [addMemberOpen, setAddMemberOpen] = useState(false);
useEffect(() => { setForm(toForm(project)); }, [project]); useEffect(() => { setForm(toForm(project)); }, [project]);
@@ -75,6 +77,7 @@ export default function ProjectInfoTab({ project, onChanged }) {
await runAction(async () => { await runAction(async () => {
await addProjectMember(project.id, Number(addingMember)); await addProjectMember(project.id, Number(addingMember));
setAddingMember(''); setAddingMember('');
setAddMemberOpen(false);
onChanged(); onChanged();
}); });
} }
@@ -185,20 +188,28 @@ export default function ProjectInfoTab({ project, onChanged }) {
)} )}
<div className="panel-card"> <div className="panel-card">
<h3>{t('project_members')}</h3> <div className="toolbar">
<h3 style={{ margin: 0 }}>{t('project_members')}</h3>
<Button variant="standard" icon={UserPlus} onClick={() => setAddMemberOpen(true)}>{t('project_add_member')}</Button>
</div>
{project.members.map((m) => ( {project.members.map((m) => (
<div key={m.user_id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '6px 0' }}> <div key={m.user_id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '6px 0' }}>
<span style={{ flex: 1, fontSize: '.82rem' }}>{m.name} <span className="text-muted">· {m.email}</span></span> <span style={{ flex: 1, fontSize: '.82rem' }}>{m.name} <span className="text-muted">· {m.email}</span></span>
<button className="icon-btn" onClick={() => confirmRemoveMember(m)}><Trash2 size={16} /></button> <button className="icon-btn" onClick={() => confirmRemoveMember(m)}><Trash2 size={16} /></button>
</div> </div>
))} ))}
<div style={{ display: 'flex', gap: 8, marginTop: 10 }}> {project.members.length === 0 && <div className="text-muted" style={{ fontSize: '.78rem' }}>{t('common_empty')}</div>}
<div style={{ flex: 1 }}> </div>
<DetailPopup open={addMemberOpen} title={t('project_add_member')} onClose={() => setAddMemberOpen(false)} width={420} height={260}>
<Field label={t('project_add_member')}>
<ComboBox options={userOptions} value={addingMember} onChange={setAddingMember} placeholder={t('project_add_member')} /> <ComboBox options={userOptions} value={addingMember} onChange={setAddingMember} placeholder={t('project_add_member')} />
</Field>
<div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
<Button variant="accent" icon={UserPlus} onClick={addMember}>{t('common_save')}</Button>
<Button variant="standard" onClick={() => setAddMemberOpen(false)}>{t('common_cancel')}</Button>
</div> </div>
<Button variant="standard" icon={UserPlus} onClick={addMember}>{t('project_add_member')}</Button> </DetailPopup>
</div>
</div>
</div> </div>
); );
} }