2.0.0: una sola lista de acciones por carpeta
La pantalla «Carpeta» pasa a ser la lista de todo lo que se puede hacer ahora mismo, en orden, con la cifra real de cada cosa y su botón. Era la simplificación de fondo de la auditoría: antes había que bajar tres niveles de navegación para saber qué faltaba en una carpeta. - acciones() y GET /api/carpeta/acciones: todas las cifras salen de consultas baratas (0,04-0,23 s en las carpetas reales); el plan solo se usa si ya estaba calculado. - Se conecta la detección de repetidos, que funcionaba pero no tenía botón. - _gb da terabytes; el peso de «por nombrar» deja de contarse dos veces al anidarse (salían totales mayores que el propio disco). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Este commit está contenido en:
@@ -177,6 +177,22 @@
|
||||
.actualizacion{border-color:var(--acc)!important;background:var(--acc2)!important;margin-bottom:18px}
|
||||
/* nombrar */
|
||||
.nombrar{list-style:none;margin:0;padding:0}
|
||||
.acc{display:flex;flex-direction:column;gap:8px;margin-bottom:18px}
|
||||
.ac{display:grid;grid-template-columns:26px minmax(0,1fr) auto;gap:14px;align-items:center;background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:12px 14px}
|
||||
.ac.hecho{background:transparent;border-style:dashed;opacity:.62}
|
||||
.ac.espera{opacity:.72}
|
||||
.ac .ico{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;flex:none;background:var(--acc2);color:var(--acc)}
|
||||
.ac.hecho .ico{background:var(--ok2);color:var(--ok)}
|
||||
.ac.espera .ico{background:var(--panel2);color:var(--mut)}
|
||||
.ac .tx{min-width:0}
|
||||
.ac .tx b{font-size:14px;display:block}
|
||||
.ac .tx .q{font-size:12.5px;color:var(--mut);margin-top:1px}
|
||||
.ac .tx .d{font-size:12px;color:var(--mut);margin-top:3px}
|
||||
.ac .der{display:flex;flex-direction:column;align-items:flex-end;gap:5px;flex:none}
|
||||
.ac .cifra{font-size:13px;font-weight:700;white-space:nowrap;font-variant-numeric:tabular-nums}
|
||||
.ac.hecho .cifra{font-weight:500;color:var(--mut)}
|
||||
.ac button{white-space:nowrap}
|
||||
@media (max-width:820px){ .ac{grid-template-columns:26px minmax(0,1fr)} .ac .der{grid-column:2;align-items:flex-start} }
|
||||
.paso{background:var(--panel);border:1px solid var(--line);border-radius:8px;margin-bottom:10px}
|
||||
.paso .pc{display:flex;gap:12px;align-items:center;padding:12px 14px}
|
||||
.paso .num{width:26px;height:26px;border-radius:50%;background:var(--acc2);color:var(--acc);font-weight:700;display:flex;align-items:center;justify-content:center;flex:none;font-size:13px}
|
||||
@@ -312,7 +328,8 @@
|
||||
</div>
|
||||
<div id="inicioLleno">
|
||||
<div class="cab"><div><h2 class="v" id="cabTitulo">…</h2><div class="sub" id="cabSub"></div></div><div class="der"><button class="lnk" id="btnEscanear">Volver a escanear</button><button class="lnk" id="btnAsistente">Asistente paso a paso</button><button class="lnk" id="btnDesconectar" title="La carpeta deja de aparecer en la lista de la izquierda. No borra ni mueve nada del disco.">Quitar de la lista</button></div></div>
|
||||
<div class="sig" id="siguiente"></div>
|
||||
<div id="acciones" class="acc"></div>
|
||||
<div class="sig" id="siguiente" style="display:none"></div>
|
||||
<div class="row">
|
||||
<div class="box plano"><h3>Qué hay en la carpeta</h3><div id="pesoBarra" class="peso"></div><div id="pesoLinea" class="peso-linea"></div><div id="inicioDiag" style="margin-top:8px;font-size:12.5px"></div></div>
|
||||
<div class="box plano"><h3>Últimos cambios en esta carpeta</h3><div id="inicioLotes" class="mut">Todavía no se ha movido nada.</div></div>
|
||||
@@ -689,6 +706,7 @@ async function cargarCarpeta(){
|
||||
const e = CARPETA.escaneo;
|
||||
$('#cabSub').innerHTML = `<span class="path">${esc(r.ruta)}</span> · ${esc(ESTADO.tipos[r.tipo] || r.tipo)}${e ? ` · ${num(e.n)} archivos · ${gb(e.bytes)} · escaneada ${fecha(e.ts)}` : ' · sin escanear'}`;
|
||||
pintarIndice(); pintarSiguiente(pasosDe(CARPETA, r));
|
||||
pintarAcciones().catch(e => console.warn(e));
|
||||
const d = CARPETA.diagnostico;
|
||||
if (!d) { $('#pesoBarra').innerHTML = ''; $('#pesoLinea').innerHTML = ''; $('#inicioDiag').innerHTML = '<span class="mut">Escanea la carpeta para ver qué hay.</span>'; }
|
||||
else {
|
||||
@@ -700,6 +718,28 @@ async function cargarCarpeta(){
|
||||
$('#inicioLotes').innerHTML = lotes.length ? lotes.map(l => `<div class="ev"><span class="h">${esc(fechaCorta(l.ts))}</span><span class="q"><b>${esc(queSeHizo(l.lote))}</b> · ${l.movimientos} mov.${l.errores ? ` <span class="bad">· ${l.errores} avisos</span>` : ''}</span>${l.lote && l.lote.startsWith('optimizar') ? '' : `<button data-l="${esc(l.lote)}">Deshacer</button>`}</div>`).join('') : '<span class="mut">Todavía no se ha movido nada en esta carpeta.</span>';
|
||||
$$('#inicioLotes button[data-l]').forEach(bt => bt.onclick = () => deshacer(bt.dataset.l));
|
||||
}
|
||||
// ---------------- lo que se puede hacer en esta carpeta ----------------
|
||||
// Una sola lista, en orden, con la cifra de cada cosa y un botón. Sustituye a repartir el trabajo
|
||||
// entre pestañas dentro de pestañas: el usuario ve de un vistazo qué falta y cuánto pesa.
|
||||
const IR_ACCION = {
|
||||
plan: p => { irA('revisar'); subRev('plan'); if (!PLAN) $('#btnPlan').click(); else if (p) { PLAN_ABIERTOS = new Set([p]); pintarPlan(); } },
|
||||
nombrar: () => { irA('revisar'); subRev('nombrar'); },
|
||||
stock: () => { irA('revisar'); subRev('stock'); if (!STOCK.length) $('#btnStock').click(); },
|
||||
repetidos: () => { irA('revisar'); subRev('hay'); const d = $('#dups'); if (d) d.scrollIntoView({block: 'center', behavior: 'smooth'}); },
|
||||
convertir: () => { irA('convertir'); $('#btnBuscarClips').click(); },
|
||||
respaldar: () => irA('respaldar'),
|
||||
escanear: () => $('#btnEscanear').click(),
|
||||
};
|
||||
async function pintarAcciones(){
|
||||
const raiz = raizActual(); if (!raiz) return;
|
||||
const d = await api('/api/carpeta/acciones?raiz=' + encodeURIComponent(raiz));
|
||||
if (raizActual() !== raiz) return; // cambió de carpeta mientras llegaba
|
||||
ACCIONES = d.acciones || [];
|
||||
const quedan = ACCIONES.filter(a => a.estado === 'toca').length;
|
||||
$('#acciones').innerHTML = `<div class="cabsec">${quedan ? `Lo que se puede hacer ahora · ${quedan}` : 'Esta carpeta está al día'}</div>` +
|
||||
ACCIONES.map((a, i) => `<div class="ac ${a.estado}"><span class="ico">${a.estado === 'hecho' ? '✓' : a.estado === 'espera' ? '·' : i + 1}</span><div class="tx"><b>${esc(a.titulo)}</b><div class="q">${esc(a.que)}</div>${a.detalle ? `<div class="d">${esc(a.detalle)}</div>` : ''}</div><div class="der">${a.cifra ? `<span class="cifra">${esc(a.cifra)}</span>` : ''}${a.boton ? `<button class="${a.estado === 'espera' ? 'lnk' : 'p'}" data-a="${i}">${esc(a.boton)}</button>` : ''}</div></div>`).join('');
|
||||
$$('#acciones [data-a]').forEach(b => b.onclick = () => { const a = ACCIONES[+b.dataset.a]; (IR_ACCION[a.ir] || (() => {}))(a.paso); });
|
||||
}
|
||||
function queSeHizo(lote){
|
||||
if (!lote) return 'Cambio';
|
||||
if (lote === 'optimizar' || lote.startsWith('optimizar-')) return 'Reemplazó originales por la versión convertida';
|
||||
@@ -866,7 +906,7 @@ const PASOS_PLAN = [
|
||||
{k: 'resto', t: 'Ordenar lo demás', q: 'Exportados, proyectos, música, marca y demás van a su carpeta. Lo dudoso queda sin marcar: la app no lo mueve salvo que lo marques tú.', vacio: 'Nada más que ordenar.'},
|
||||
];
|
||||
const SEG_TXT = {alta: 'seguro', media: 'probable', baja: 'dudoso'};
|
||||
let PLAN_ABIERTOS = new Set();
|
||||
let PLAN_ABIERTOS = new Set(), ACCIONES = [];
|
||||
function estadoPlan(){
|
||||
const hay = !!PLAN;
|
||||
$('#btnArbol').disabled = !hay;
|
||||
@@ -941,7 +981,7 @@ function aplicarFilas(m, btn, titulo){
|
||||
PLAN = null; MAPA = null;
|
||||
aviso(`${res.movimientos} movimiento${res.movimientos !== 1 ? 's' : ''} hecho${res.movimientos !== 1 ? 's' : ''}.` + (res.errores.length ? ` ${res.errores.length} no se pudieron.` : ''), res.errores.length ? 'bad' : 'ok', {texto: 'Deshacer esto', ir: () => deshacer(res.lote)});
|
||||
if (res.errores.length) modal('Algunos no se pudieron mover', `<pre>${esc(res.errores.join('\n'))}</pre>`, null);
|
||||
await cargarEstado(); await calcularPlan();
|
||||
await cargarEstado(); await calcularPlan(); pintarAcciones().catch(() => {});
|
||||
}));
|
||||
$('#modalSi').textContent = 'Aplicar';
|
||||
}
|
||||
|
||||
Referencia en una nueva incidencia
Block a user