feat(classifier): rename transmittals; remove/move files already in one
By-transmittal pane gains the editing it was missing: - Rename a transmittal after creation (✎ on the bin → prompt). The name IS the filing folder (deriveTarget reads bin.name), so renaming changes where copies land, not just the label. - Each placed file row is now draggable — drop it on another transmittal to MOVE it — and carries an ✕ to remove it from the transmittal (clears that axis). previewFromTarget now lets action buttons through so ✕ doesn't trigger preview. Test: rename feeds the derived folder; the row is draggable + has remove; ✕ clears the transmittal; re-place moves it to another bin (55 green). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d951c3a5e7
commit
bc762a7d74
3 changed files with 75 additions and 3 deletions
|
|
@ -444,7 +444,11 @@
|
|||
|
||||
/* placed files under a node */
|
||||
.tnode__files { margin: 0.1rem 0 0.2rem 1.6rem; }
|
||||
.tfile { display: flex; align-items: baseline; gap: 0.4rem; font-size: 0.75rem; padding: 0.05rem 0; }
|
||||
.tfile { display: flex; align-items: baseline; gap: 0.4rem; font-size: 0.75rem; padding: 0.05rem 0; cursor: grab; }
|
||||
.tfile[draggable="true"]:active { cursor: grabbing; }
|
||||
.tfile__remove { opacity: 0; flex: 0 0 auto; align-self: center; line-height: 1; }
|
||||
.tfile:hover .tfile__remove { opacity: 1; }
|
||||
.tfile__remove:hover { color: var(--danger); border-color: var(--danger); }
|
||||
.tfile__orig { color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 14rem; }
|
||||
.tfile__arrow { color: var(--text-muted); }
|
||||
.tfile__name { color: var(--text); }
|
||||
|
|
|
|||
|
|
@ -158,14 +158,18 @@
|
|||
return wrap;
|
||||
}
|
||||
|
||||
// Placed files inside a transmittal bin. Each row is draggable (drag onto
|
||||
// another bin to MOVE it) and carries an ✕ to remove it from the transmittal.
|
||||
function fileList(files) {
|
||||
var box = el('div', 'tnode__files');
|
||||
files.forEach(function (f) {
|
||||
var d = C().deriveTarget(f);
|
||||
var row = el('div', 'tfile' + (d.errors.length ? ' tfile--err' : ''));
|
||||
row.dataset.key = d.key;
|
||||
row.draggable = true;
|
||||
row.addEventListener('dragstart', function (e) { window.app.modules.dnd.setDrag([d.key], e); });
|
||||
var orig = el('span', 'tfile__orig', f.originalFilename + (f.extension ? '.' + f.extension : ''));
|
||||
orig.title = 'Click to preview';
|
||||
orig.title = 'Drag to another transmittal to move · click to preview';
|
||||
row.appendChild(orig);
|
||||
row.appendChild(el('span', 'tfile__arrow', '→'));
|
||||
// Editable derived filename — edit it to re-file the item.
|
||||
|
|
@ -175,6 +179,10 @@
|
|||
name.placeholder = '(incomplete)';
|
||||
name.title = d.errors.length ? d.errors.join('; ') : 'Edit this ZDDC filename to re-file the item';
|
||||
row.appendChild(name);
|
||||
var rm = el('button', 'tnode__act tfile__remove', '✕');
|
||||
rm.dataset.act = 'untransmit';
|
||||
rm.title = 'Remove from this transmittal';
|
||||
row.appendChild(rm);
|
||||
box.appendChild(row);
|
||||
});
|
||||
return box;
|
||||
|
|
@ -421,7 +429,10 @@
|
|||
var row = el('div', 'tnode__row');
|
||||
row.appendChild(el('span', 'tnode__name', bin.name || '(invalid — set date/seq)'));
|
||||
if (placed.length) row.appendChild(el('span', 'tnode__badge', String(placed.length)));
|
||||
row.appendChild(nodeActions([{ act: 'del', label: '🗑', title: 'Delete transmittal' }]));
|
||||
row.appendChild(nodeActions([
|
||||
{ act: 'rename-bin', label: '✎', title: 'Rename transmittal' },
|
||||
{ act: 'del', label: '🗑', title: 'Delete transmittal' },
|
||||
]));
|
||||
wrap.appendChild(row);
|
||||
if (shownFiles.length) wrap.appendChild(fileList(shownFiles));
|
||||
return wrap;
|
||||
|
|
@ -468,6 +479,7 @@
|
|||
}
|
||||
return true;
|
||||
}
|
||||
if (e.target.closest('[data-act]')) return false; // action button — not a preview
|
||||
if (e.target.closest('.tfile__name')) return false;
|
||||
var tf = e.target.closest('.tfile');
|
||||
if (!tf || !tf.dataset.key) return false;
|
||||
|
|
@ -544,6 +556,18 @@
|
|||
render();
|
||||
return;
|
||||
}
|
||||
if (act === 'untransmit') {
|
||||
var tf = btn.closest('.tfile');
|
||||
if (tf && tf.dataset.key) C().place([tf.dataset.key], null, 'transmittal');
|
||||
return;
|
||||
}
|
||||
if (act === 'rename-bin') {
|
||||
var bid = closestNodeId(btn);
|
||||
var bn = C().getNode(bid);
|
||||
var nn = prompt('Rename transmittal (this becomes its folder name):', bn ? bn.name : '');
|
||||
if (nn && nn.trim()) C().renameNode(bid, nn.trim());
|
||||
return;
|
||||
}
|
||||
if (act === 'bincancel') { openForm = null; render(); return; }
|
||||
if (act === 'binadd') {
|
||||
var form = btn.closest('.binform');
|
||||
|
|
|
|||
|
|
@ -1133,3 +1133,47 @@ test('copy: verifies copied bytes; a bad write fails verification and is removed
|
|||
expect(r.removed).toBe(1); // bad copy removed…
|
||||
expect(r.left).toBe(0); // …so a re-run re-copies it
|
||||
});
|
||||
|
||||
test('transmittal: rename a bin (feeds the folder), remove and move a placed file', async ({ page }) => {
|
||||
await page.click('#modeClassifyBtn');
|
||||
const r = await page.evaluate(async () => {
|
||||
const c = window.app.modules.classify, tt = window.app.modules.targetTree;
|
||||
c.reset();
|
||||
const f = { originalFilename: 'doc', extension: 'pdf', folderPath: 'R' };
|
||||
window.app.folderTree = [{ name: 'R', path: 'R', files: [f], children: [] }];
|
||||
const key = c.srcKeyForFile(f);
|
||||
const party = c.addParty('CC');
|
||||
const bin1 = c.addTransmittalBin(party, 'received', { date: '2026-03-14', type: 'TRN', seq: '0007' });
|
||||
const bin2 = c.addTransmittalBin(party, 'received', { date: '2026-03-14', type: 'TRN', seq: '0008' });
|
||||
c.place([key], bin1, 'transmittal');
|
||||
tt.showTab('transmittal'); tt.render();
|
||||
|
||||
// Rename the bin → it becomes the copy folder name.
|
||||
c.renameNode(bin1, 'My Custom Transmittal');
|
||||
const renamed = c.getNode(bin1).name === 'My Custom Transmittal';
|
||||
const folder = c.deriveTarget(f).transmittalFolder;
|
||||
|
||||
// The placed-file row is draggable (move) and carries a remove button.
|
||||
tt.render();
|
||||
const row = document.querySelector('#transmittalTree .tfile[data-key]');
|
||||
const draggable = !!(row && row.draggable);
|
||||
const hasRemove = !!(row && row.querySelector('.tfile__remove[data-act="untransmit"]'));
|
||||
|
||||
// Remove from the transmittal (click ✕).
|
||||
row.querySelector('.tfile__remove').click();
|
||||
const a1 = c.getAssignment(key);
|
||||
const removed = !(a1 && a1.transmittalNodeId);
|
||||
|
||||
// Move = re-place onto another bin (what dropping on bin2 does).
|
||||
c.place([key], bin2, 'transmittal');
|
||||
const movedTo = (c.getAssignment(key) || {}).transmittalNodeId === bin2;
|
||||
|
||||
return { renamed, folder, draggable, hasRemove, removed, movedTo };
|
||||
});
|
||||
expect(r.renamed).toBe(true);
|
||||
expect(r.folder).toBe('My Custom Transmittal'); // rename drives the filing folder
|
||||
expect(r.draggable).toBe(true);
|
||||
expect(r.hasRemove).toBe(true);
|
||||
expect(r.removed).toBe(true);
|
||||
expect(r.movedTo).toBe(true);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue