// ─────────────────────────────────────────────────────
// Incidencias — gestor de incidencias / mejoras del ERP para Juanjo, Víctor y
// David (capacidad 'incidencias', por usuario). Sobre /api/incidencias.
// Ciclo: abierta → «Marcar resuelta» (con «qué hemos hecho») → resuelta
// (pendiente de validar) → «✓ Validar» por alguien distinto de quien la
// resolvió → validada (archivada). Reabrir (con motivo) desde resuelta o
// validada. Siempre la misma incidencia, con todo el ciclo en el historial.
// Adjuntos por evento en /api/archivos (entity_type 'incidencias',
// categoria 'evento-<id>'). Reutiliza ArchivoThumb/archivoSize (archivos.jsx)
// y los modales de sistema.jsx.
// Expuesto como window.IncidenciasView.
// ─────────────────────────────────────────────────────

const INC_ESTADOS = {
  abierta:  { label: 'Abierta',     cls: 'warn' },
  resuelta: { label: 'Por validar', cls: 'info' },
  validada: { label: 'Validada',    cls: 'ok' },
};
const INC_FILTROS = [
  ['abiertas', 'Abiertas', 'abiertas'],
  ['por-validar', 'Por validar', 'por_validar'],
  ['archivadas', 'Archivadas', 'archivadas'],
  ['todas', 'Todas', 'todas'],
];
const INC_TIPOS = {
  problema: { label: 'Problema', cls: 'err' },
  mejora:   { label: 'Mejora',   cls: 'info' },
};
const INC_ACCEPT = '.jpg,.jpeg,.png,.webp,.gif,.heic,.pdf,.doc,.docx,.xls,.xlsx,.csv,.ppt,.pptx,.txt,.dwg,.dxf,.dwf,.step,.stp,.iges,.igs,.3dm,.skp,.zip';

const incNumero = (id) => `INC-${String(id).padStart(3, '0')}`;
const incFecha = (f) => {
  if (!f) return '—';
  const d = new Date(f);
  return d.toLocaleDateString('es-ES', { day: '2-digit', month: '2-digit', year: 'numeric' })
    + ' ' + d.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' });
};

// Sube los ficheros elegidos a un evento, de 5 en 5 (límite de /api/archivos).
async function incSubirAdjuntos(incidenciaId, eventoId, files) {
  for (let i = 0; i < files.length; i += 5) {
    const fd = new FormData();
    fd.append('entity_type', 'incidencias');
    fd.append('entity_id', String(incidenciaId));
    fd.append('categoria', `evento-${eventoId}`);
    files.slice(i, i + 5).forEach(f => fd.append('files', f));
    await API.upload('/api/archivos', fd);
  }
}

// Selector de adjuntos: lista lo elegido y permite quitar antes de enviar.
const IncFilePicker = ({ files, onChange }) => {
  const inputRef = React.useRef(null);
  const add = (ev) => {
    const nuevos = Array.from(ev.target.files || []);
    onChange([...files, ...nuevos]);
    if (inputRef.current) inputRef.current.value = '';
  };
  return (
    <div>
      <label className="btn ghost sm" style={{ cursor: 'pointer' }}>
        <Icon name="upload" size={12} /> Adjuntar archivos
        <input ref={inputRef} type="file" multiple accept={INC_ACCEPT} style={{ display: 'none' }} onChange={add} />
      </label>
      <span className="muted" style={{ fontSize: 11.5, marginLeft: 8 }}>Fotos, PDF, Office… (máx. 25 MB cada uno)</span>
      {files.length > 0 && (
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 8 }}>
          {files.map((f, i) => (
            <span key={i} className="pill" style={{ gap: 6 }}>
              {f.name} <span className="muted">· {archivoSize(f.size)}</span>
              <button type="button" className="icon-btn" style={{ width: 16, height: 16 }} title="Quitar"
                onClick={() => onChange(files.filter((_, j) => j !== i))}><Icon name="x" size={10} /></button>
            </span>
          ))}
        </div>
      )}
    </div>
  );
};

// Adjuntos ya subidos: miniatura de las imágenes y tarjeta para el resto.
// Clic → se abre en otra pestaña con el enlace firmado.
const IncAdjuntos = ({ files, onToast }) => {
  if (!files || !files.length) return null;
  const abrir = async (f) => {
    const w = window.open('', '_blank');
    try {
      const { url } = await API.post(`/api/archivos/${f.id}/link`);
      if (w) w.location = url;
    } catch (e) {
      if (w) w.close();
      onToast(e.message || 'Error al abrir archivo', true);
    }
  };
  return (
    <div className="archivos-grid" style={{ marginTop: 8 }}>
      {files.map(f => (
        <button key={f.id} type="button" className="archivo-card" onClick={() => abrir(f)} title={f.original_name}>
          <span className="archivo-thumb"><ArchivoThumb file={f} /></span>
          <span className="archivo-meta">
            <span className="archivo-name">{f.original_name}</span>
            <span className="archivo-sub">{archivoSize(f.size)}</span>
          </span>
        </button>
      ))}
    </div>
  );
};

// Alta y edición (título, tipo, descripción; adjuntos solo en el alta).
const IncFormModal = ({ inicial, onClose, onSaved, onToast }) => {
  const editando = !!inicial;
  const [titulo, setTitulo] = React.useState(inicial ? inicial.titulo : '');
  const [tipo, setTipo] = React.useState(inicial ? inicial.tipo : 'problema');
  const [descripcion, setDescripcion] = React.useState(inicial ? inicial.descripcion : '');
  const [files, setFiles] = React.useState([]);
  const [saving, setSaving] = React.useState(false);

  const submit = async (e) => {
    e.preventDefault();
    setSaving(true);
    try {
      if (editando) {
        await API.put(`/api/incidencias/${inicial.id}`, { titulo, tipo, descripcion });
        onToast('Incidencia actualizada ✓');
        onSaved(inicial.id);
      } else {
        const r = await API.post('/api/incidencias', { titulo, tipo, descripcion });
        try {
          if (files.length) await incSubirAdjuntos(r.id, r.evento_id, files);
          onToast(`${incNumero(r.id)} creada ✓`);
        } catch (err) {
          onToast(`${incNumero(r.id)} creada, pero falló algún adjunto: ${err.message}`, true);
        }
        onSaved(r.id);
      }
      onClose();
    } catch (err) {
      onToast(err.message || 'Error al guardar', true);
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="modal-bg open" {...sysOverlay(() => onClose())}>
      <div className="modal" {...sysModal(620)}>
        <SysModalHeader title={editando ? `Editar ${incNumero(inicial.id)}` : 'Nueva incidencia'} onClose={onClose} />
        <form onSubmit={submit} autoComplete="off">
          <div className="fields-grid" style={{ padding: '16px 18px' }}>
            <div className="field full">
              <label>Título</label>
              <input required autoFocus value={titulo} onChange={e => setTitulo(e.target.value)}
                placeholder="Resumen en una frase" />
            </div>
            <div className="field full">
              <label>Tipo</label>
              <div className="seg">
                {Object.entries(INC_TIPOS).map(([k, t]) => (
                  <button type="button" key={k} className={tipo === k ? 'on' : ''} onClick={() => setTipo(k)}>{t.label}</button>
                ))}
              </div>
            </div>
            <div className="field full">
              <label>Descripción</label>
              <textarea rows={8} value={descripcion} onChange={e => setDescripcion(e.target.value)}
                placeholder="Qué pasa, dónde (pantalla, expediente, cliente…) y qué esperabas que pasara." />
            </div>
            {!editando && (
              <div className="field full">
                <label>Adjuntos</label>
                <IncFilePicker files={files} onChange={setFiles} />
              </div>
            )}
          </div>
          <SysModalFooter onClose={onClose} saving={saving} label={editando ? 'Guardar' : 'Crear incidencia'} />
        </form>
      </div>
    </div>
  );
};

// Formulario de acción dentro del panel: comentar, resolver, validar o reabrir.
const INC_ACCIONES = {
  comentar: { titulo: 'Comentario', label: 'Comentar', placeholder: 'Añade información, una duda, una respuesta…', url: 'comentarios', campo: 'texto', ok: 'Comentario añadido ✓' },
  resolver: { titulo: 'Qué hemos hecho / cómo se ha resuelto', label: 'Marcar resuelta', placeholder: 'Explica la solución: qué se ha cambiado y dónde verlo. Adjunta capturas si ayuda.', url: 'resolver', campo: 'resolucion', ok: 'Marcada como resuelta ✓ · pendiente de validar', obligatorio: true },
  validar:  { titulo: 'Comentario (opcional)', label: '✓ Validar', placeholder: 'Comprobado, funciona bien…', url: 'validar', campo: 'texto', ok: 'Incidencia validada ✓', opcional: true },
  reabrir:  { titulo: 'Motivo de la reapertura', label: 'Reabrir incidencia', placeholder: 'Qué sigue fallando o qué falta.', url: 'reabrir', campo: 'motivo', ok: 'Incidencia reabierta ✓', obligatorio: true },
};

const IncAccionForm = ({ accion, incidenciaId, onCancel, onDone, onToast }) => {
  const cfg = INC_ACCIONES[accion];
  const [txt, setTxt] = React.useState('');
  const [files, setFiles] = React.useState([]);
  const [saving, setSaving] = React.useState(false);

  const submit = async (e) => {
    e.preventDefault();
    setSaving(true);
    try {
      const r = await API.post(`/api/incidencias/${incidenciaId}/${cfg.url}`, { [cfg.campo]: txt });
      try {
        if (files.length) await incSubirAdjuntos(incidenciaId, r.evento_id, files);
        onToast(cfg.ok);
      } catch (err) {
        onToast(`Guardado, pero falló algún adjunto: ${err.message}`, true);
      }
      onDone();
    } catch (err) {
      onToast(err.message || 'Error al guardar', true);
    } finally {
      setSaving(false);
    }
  };

  return (
    <form onSubmit={submit} style={{ border: '1px solid var(--line)', borderRadius: 8, padding: 12, marginBottom: 16, background: 'var(--surface-2)' }}>
      <div className="field">
        <label>{cfg.titulo}{cfg.obligatorio && ' (obligatorio)'}</label>
        <textarea rows={cfg.opcional ? 3 : 5} required={!cfg.opcional} autoFocus value={txt} onChange={e => setTxt(e.target.value)} placeholder={cfg.placeholder} />
      </div>
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8, marginTop: 10, flexWrap: 'wrap' }}>
        <div style={{ flex: 1, minWidth: 220 }}><IncFilePicker files={files} onChange={setFiles} /></div>
        <button type="button" className="btn ghost sm" onClick={onCancel}>Cancelar</button>
        <button type="submit" className="btn primary sm" disabled={saving || (!cfg.opcional && !txt.trim())}>{saving ? 'Guardando…' : cfg.label}</button>
      </div>
    </form>
  );
};

// Un paso del historial.
const IncEvento = ({ ev, files, onToast }) => {
  const quien = <strong>{ev.usuario || 'Alguien'}</strong>;
  // 'cerrada' = eventos anteriores a la validación (se muestran como resuelta).
  const resuelta = { bg: 'var(--info-bg)', color: 'var(--info)', titulo: 'Resuelta · qué se ha hecho' };
  const cajas = {
    resuelta,
    cerrada:   resuelta,
    validada:  { bg: 'var(--ok-bg)',   color: 'var(--ok)',   titulo: 'Validada' },
    reabierta: { bg: 'var(--warn-bg)', color: 'var(--warn)', titulo: 'Reabierta · motivo' },
  };
  const caja = cajas[ev.tipo];
  const frase = {
    creada: 'abrió la incidencia',
    comentario: 'comentó',
    resuelta: 'la marcó como resuelta',
    cerrada: 'la marcó como resuelta',
    validada: 'la validó',
    reabierta: 'la reabrió',
    editada: 'editó el título o la descripción',
  }[ev.tipo] || ev.tipo;
  return (
    <div style={{ display: 'flex', gap: 10, padding: '10px 0', borderTop: '1px solid var(--line)' }}>
      <div style={{ width: 8, height: 8, borderRadius: '50%', marginTop: 6, flex: 'none', background: caja ? caja.color : 'var(--ink-4)' }} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 12.5 }}>
          {quien} {frase} <span className="muted">· {incFecha(ev.created_at)}</span>
        </div>
        {ev.texto && (
          caja ? (
            <div style={{ marginTop: 6, padding: '8px 10px', borderRadius: 6, background: caja.bg }}>
              <div style={{ fontSize: 10.5, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '.05em', color: caja.color }}>{caja.titulo}</div>
              <div style={{ whiteSpace: 'pre-wrap', fontSize: 13, marginTop: 2 }}>{ev.texto}</div>
            </div>
          ) : (
            <div style={{ whiteSpace: 'pre-wrap', fontSize: 13, marginTop: 4 }}>{ev.texto}</div>
          )
        )}
        <IncAdjuntos files={files} onToast={onToast} />
      </div>
    </div>
  );
};

// Panel lateral con el detalle y el historial.
const IncDrawer = ({ incidenciaId, user, onClose, onChanged, onToast }) => {
  const [data, setData] = React.useState(null);
  const [accion, setAccion] = React.useState(null); // null | 'comentar' | 'resolver' | 'validar' | 'reabrir'
  const [editar, setEditar] = React.useState(false);

  const load = async () => {
    try { setData(await API.get(`/api/incidencias/${incidenciaId}`)); }
    catch (e) { onToast(e.message || 'Error', true); }
  };
  React.useEffect(() => { setData(null); setAccion(null); if (incidenciaId) load(); }, [incidenciaId]);

  const tras = () => { setAccion(null); load(); onChanged(); };

  // Adjuntos agrupados por evento; los que no casan con ninguno, al alta.
  const eventos = (data && data.eventos) || [];
  const porEvento = {};
  const alta = eventos.find(e => e.tipo === 'creada');
  ((data && data.archivos) || []).forEach(f => {
    const m = /^evento-(\d+)$/.exec(f.categoria || '');
    const evId = m && eventos.some(e => e.id === Number(m[1])) ? Number(m[1]) : (alta ? alta.id : 0);
    (porEvento[evId] = porEvento[evId] || []).push(f);
  });

  const open = !!incidenciaId;
  const est = data && (INC_ESTADOS[data.estado] || { label: data.estado, cls: '' });
  const tipo = data && (INC_TIPOS[data.tipo] || { label: data.tipo, cls: '' });
  const puedeEditar = data && data.estado === 'abierta' && data.autor === user.username;
  // Valida cualquiera con el permiso salvo quien la resolvió.
  const resolvioYo = data && data.cerrada_por_nombre === user.username;

  return (
    <>
      <div className={`drawer-bg ${open ? 'open' : ''}`} onClick={onClose} />
      <div className={`drawer ${open ? 'open' : ''}`} style={{ width: 'min(760px, 100%)' }}>
        <div className="dr-head">
          {data && <span className="id mono">{incNumero(data.id)}</span>}
          <h2>{data ? data.titulo : 'Cargando…'}</h2>
          <div className="actions">
            {puedeEditar && <button className="icon-btn" title="Editar" onClick={() => setEditar(true)}><Icon name="edit" /></button>}
            <button className="icon-btn" onClick={onClose}><Icon name="x" /></button>
          </div>
        </div>
        <div className="dr-body">
          {!data && <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)' }}>Cargando…</div>}
          {data && (
            <div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', marginBottom: 12 }}>
                <span className={`pill ${est.cls}`}><span className="d" />{est.label}</span>
                <span className={`pill ${tipo.cls}`}>{tipo.label}</span>
                <span className="muted" style={{ fontSize: 12 }}>Abierta por <strong>{data.autor || '—'}</strong> el {incFecha(data.created_at)}</span>
                <div style={{ flex: 1 }} />
                {!accion && (
                  <>
                    <button className="btn ghost sm" onClick={() => setAccion('comentar')}>Comentar</button>
                    {data.estado === 'abierta' && (
                      <button className="btn primary sm" onClick={() => setAccion('resolver')}><Icon name="check" size={12} /> Marcar resuelta</button>
                    )}
                    {data.estado !== 'abierta' && (
                      <button className="btn ghost sm" onClick={() => setAccion('reabrir')}>Reabrir</button>
                    )}
                    {data.estado === 'resuelta' && !resolvioYo && (
                      <button className="btn primary sm" onClick={() => setAccion('validar')}>✓ Validar</button>
                    )}
                  </>
                )}
              </div>

              {data.estado !== 'abierta' && (() => {
                const val = data.estado === 'validada';
                const color = val ? 'var(--ok)' : 'var(--info)';
                return (
                  <div style={{ border: `1px solid ${color}`, background: val ? 'var(--ok-bg)' : 'var(--info-bg)', borderRadius: 8, padding: '10px 12px', marginBottom: 14 }}>
                    <div style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '.05em', color }}>
                      Resuelta · {data.cerrada_por_nombre || '—'} · {incFecha(data.cerrada_at)}
                      {val
                        ? <> · Validada por {data.validada_por_nombre || '—'} · {incFecha(data.validada_at)}</>
                        : ' · pendiente de validar'}
                    </div>
                    <div style={{ whiteSpace: 'pre-wrap', fontSize: 13.5, marginTop: 4 }}>{data.resolucion}</div>
                    {!val && (
                      <div className="muted" style={{ fontSize: 12, marginTop: 6 }}>
                        {resolvioYo
                          ? 'La tiene que validar otra persona (no quien la resolvió).'
                          : 'Compruébalo: si está bien, pulsa «✓ Validar»; si no, «Reabrir» y cuenta qué falla.'}
                      </div>
                    )}
                  </div>
                );
              })()}

              {accion && (
                <IncAccionForm key={accion} accion={accion} incidenciaId={data.id}
                  onCancel={() => setAccion(null)} onDone={tras} onToast={onToast} />
              )}

              <div className="section-title">Descripción</div>
              <div style={{ whiteSpace: 'pre-wrap', fontSize: 13.5, lineHeight: 1.5 }}>
                {data.descripcion || <span className="muted">Sin descripción.</span>}
              </div>
              <IncAdjuntos files={alta ? porEvento[alta.id] : null} onToast={onToast} />

              <div className="section-title" style={{ marginTop: 18 }}>Historial</div>
              {eventos.map(ev => (
                <IncEvento key={ev.id} ev={ev} files={ev.tipo === 'creada' ? null : porEvento[ev.id]} onToast={onToast} />
              ))}
            </div>
          )}
        </div>
      </div>
      {editar && data && (
        <IncFormModal inicial={data} onClose={() => setEditar(false)} onSaved={() => tras()} onToast={onToast} />
      )}
    </>
  );
};

const IncidenciasView = ({ user, onToast }) => {
  const [rows, setRows] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [estado, setEstado] = React.useState(() => {
    try {
      const v = localStorage.getItem('inc_estado');
      return INC_FILTROS.some(f => f[0] === v) ? v : 'abiertas';
    } catch { return 'abiertas'; }
  });
  const [contadores, setContadores] = React.useState(null);
  const [search, setSearch] = React.useState('');
  const [abierta, setAbierta] = React.useState(null); // id de la incidencia en el panel
  const [nueva, setNueva] = React.useState(false);
  const searchTimer = React.useRef(null);

  const load = async (est = estado, q = search) => {
    setLoading(true);
    try {
      const params = { estado: est };
      if (q) params.search = q;
      const [lista, cont] = await Promise.all([
        API.get('/api/incidencias', params),
        API.get('/api/incidencias/contadores', q ? { search: q } : undefined),
      ]);
      setRows(lista);
      setContadores(cont);
    } catch (e) {
      onToast(e.message || 'Error', true);
    } finally {
      setLoading(false);
    }
  };
  React.useEffect(() => { load(); }, []);

  // Abrir una incidencia concreta (aviso de la campana o enlace #incidencias/<id>).
  React.useEffect(() => {
    if (window.__erpPendingIncidenciaId) {
      setAbierta(window.__erpPendingIncidenciaId);
      window.__erpPendingIncidenciaId = null;
    }
    const h = (e) => {
      const id = e.detail && e.detail.id;
      if (!id) return;
      window.__erpPendingIncidenciaId = null;
      setAbierta(id);
      load();
    };
    window.addEventListener('erp:open-incidencia', h);
    return () => window.removeEventListener('erp:open-incidencia', h);
  }, []);

  const onEstado = (v) => {
    setEstado(v);
    try { localStorage.setItem('inc_estado', v); } catch {}
    load(v, search);
  };
  const onSearch = (v) => {
    setSearch(v);
    clearTimeout(searchTimer.current);
    searchTimer.current = setTimeout(() => load(estado, v), 300);
  };

  return (
    <div style={{ padding: '16px 24px 24px' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14, flexWrap: 'wrap' }}>
        <h2 style={{ margin: 0, fontSize: 18 }}>Incidencias</h2>
        <span className="muted" style={{ fontSize: 12 }}>{rows.length} {rows.length === 1 ? 'incidencia' : 'incidencias'}</span>
        <div className="seg">
          {INC_FILTROS.map(([k, l, c]) => (
            <button type="button" key={k} className={estado === k ? 'on' : ''} onClick={() => onEstado(k)}>
              {l}{contadores && <span className="muted" style={{ marginLeft: 5, fontWeight: 400 }}>{contadores[c]}</span>}
            </button>
          ))}
        </div>
        <div style={{ flex: 1 }} />
        <div className="tb-search">
          <Icon name="search" />
          <input placeholder="Buscar por nº, título o descripción…" value={search} onChange={e => onSearch(e.target.value)} />
        </div>
        <button className="btn primary" onClick={() => setNueva(true)}><Icon name="plus" size={12} /> Nueva incidencia</button>
      </div>

      <div className="tbl-wrap">
        <div className="tbl-scroll">
          <table className="tbl">
            <thead>
              <tr>
                <th style={{ width: 90 }}>Nº</th>
                <th>Título</th>
                <th style={{ width: 90 }}>Tipo</th>
                <th style={{ width: 110 }}>Estado</th>
                <th style={{ width: 130 }}>Fecha</th>
                <th style={{ width: 130 }}>Autor</th>
                <th style={{ width: 80, textAlign: 'right' }}>Adjuntos</th>
              </tr>
            </thead>
            <tbody>
              {loading && <tr><td colSpan="7" style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--ink-3)' }}>Cargando…</td></tr>}
              {!loading && rows.map(r => {
                const est = INC_ESTADOS[r.estado] || { label: r.estado, cls: '' };
                const tipo = INC_TIPOS[r.tipo] || { label: r.tipo, cls: '' };
                return (
                  <tr key={r.id} onClick={() => setAbierta(r.id)} style={{ cursor: 'pointer' }}>
                    <td><code style={{ fontSize: 12 }}>{incNumero(r.id)}</code></td>
                    <td className="strong">
                      {r.titulo}
                      {r.n_reaperturas > 0 && <span className="muted" style={{ fontWeight: 400, fontSize: 11.5 }}> · reabierta {r.n_reaperturas === 1 ? '1 vez' : `${r.n_reaperturas} veces`}</span>}
                    </td>
                    <td><span className={`pill ${tipo.cls}`}>{tipo.label}</span></td>
                    <td><span className={`pill ${est.cls}`}><span className="d" />{est.label}</span></td>
                    <td className="muted" style={{ fontSize: 12, whiteSpace: 'nowrap' }}>{incFecha(r.created_at)}</td>
                    <td className="muted">{r.autor || '—'}</td>
                    <td className="muted" style={{ textAlign: 'right' }}>{r.n_adjuntos || '—'}</td>
                  </tr>
                );
              })}
              {!loading && !rows.length && (
                <tr><td colSpan="7" style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--ink-3)' }}>
                  {search ? 'Sin resultados.' : ({
                    abiertas: 'No hay incidencias abiertas.',
                    'por-validar': 'No hay incidencias pendientes de validar.',
                    archivadas: 'No hay incidencias archivadas.',
                  }[estado] || 'Sin resultados.')}
                </td></tr>
              )}
            </tbody>
          </table>
        </div>
      </div>

      <IncDrawer incidenciaId={abierta} user={user} onClose={() => setAbierta(null)} onChanged={() => load()} onToast={onToast} />
      {nueva && (
        <IncFormModal onClose={() => setNueva(false)} onSaved={(id) => { load(); setAbierta(id); }} onToast={onToast} />
      )}
    </div>
  );
};

window.IncidenciasView = IncidenciasView;
