forked from fixin.me/fixin.me
Replace JS-generated wide table with ERB partial and Turbo Streams
- Add _wide_table.html.erb partial (server-rendered pivot table) - Add load_measurements helper in controller to prepare @wide_groups and @wide_quantities for all mutating actions - Update index view to render the wide_table partial in #measurements-wide - Add/update create, destroy, update turbo_stream views to refresh the wide table atomically after each mutation - Remove buildWideTable() and editMeasurementWide() from application.js - Fix create.turbo_stream.erb condition (empty readouts are vacuously all persisted) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,98 +22,6 @@ function groupMeasurements() {
|
||||
});
|
||||
}
|
||||
|
||||
function buildWideTable() {
|
||||
var tbody = document.getElementById('measurements');
|
||||
var wideContainer = document.getElementById('measurements-wide');
|
||||
if (!tbody || !wideContainer) return;
|
||||
|
||||
var rows = Array.from(tbody.querySelectorAll('tr[data-taken-at]'));
|
||||
|
||||
if (rows.length === 0) { wideContainer.innerHTML = ''; return; }
|
||||
|
||||
// Unique quantities in alphabetical order
|
||||
var qOrder = [], qSeen = new Set();
|
||||
rows.forEach(function(r) {
|
||||
var id = r.dataset.quantityId;
|
||||
if (id && !qSeen.has(id)) { qSeen.add(id); qOrder.push({id: id, name: r.dataset.quantityName}); }
|
||||
});
|
||||
qOrder.sort(function(a, b) { return a.name.localeCompare(b.name); });
|
||||
|
||||
// Group rows by taken_at, preserving order
|
||||
var groups = [], groupMap = new Map();
|
||||
rows.forEach(function(r) {
|
||||
var key = r.dataset.takenAt || '';
|
||||
if (!groupMap.has(key)) { var g = {rows: []}; groups.push(g); groupMap.set(key, g); }
|
||||
groupMap.get(key).rows.push(r);
|
||||
});
|
||||
|
||||
var takenAtHeader = (document.querySelector('[data-column="taken-at"]') || {}).textContent || '';
|
||||
var createdAtHeader = (document.querySelector('[data-column="created-at"]') || {}).textContent || '';
|
||||
|
||||
var table = document.createElement('table');
|
||||
table.className = 'items-table';
|
||||
|
||||
// Header
|
||||
var thead = table.createTHead();
|
||||
var hrow = thead.insertRow();
|
||||
[takenAtHeader].concat(qOrder.map(function(q) { return q.name; })).concat([createdAtHeader]).forEach(function(text) {
|
||||
var th = document.createElement('th');
|
||||
th.textContent = text;
|
||||
hrow.appendChild(th);
|
||||
});
|
||||
|
||||
// Body
|
||||
var tbodyEl = table.createTBody();
|
||||
groups.forEach(function(group) {
|
||||
var tr = tbodyEl.insertRow();
|
||||
|
||||
// Taken at
|
||||
var tdTime = tr.insertCell();
|
||||
var takenAtEl = group.rows[0].querySelector('.taken-at');
|
||||
tdTime.textContent = takenAtEl ? takenAtEl.textContent : '';
|
||||
|
||||
// One cell per quantity
|
||||
qOrder.forEach(function(q) {
|
||||
var td = tr.insertCell();
|
||||
var readoutRow = group.rows.find(function(r) { return r.dataset.quantityId === q.id; });
|
||||
if (readoutRow) {
|
||||
td.className = 'ralign';
|
||||
var wrap = document.createElement('span');
|
||||
wrap.className = 'wide-cell';
|
||||
var editLink = readoutRow.querySelector('a.link');
|
||||
if (editLink) {
|
||||
var editUrl = editLink.href + (editLink.href.includes('?') ? '&' : '?') + 'view=wide';
|
||||
var btn = document.createElement('button');
|
||||
btn.className = 'link';
|
||||
btn.type = 'button';
|
||||
btn.dataset.editUrl = editUrl;
|
||||
btn.addEventListener('click', function() { editMeasurementWide(this.dataset.editUrl); this.blur(); });
|
||||
btn.textContent = readoutRow.dataset.value;
|
||||
wrap.appendChild(btn);
|
||||
wrap.appendChild(document.createTextNode('\u00a0' + readoutRow.dataset.unit));
|
||||
} else {
|
||||
wrap.appendChild(document.createTextNode(readoutRow.dataset.value + '\u00a0' + readoutRow.dataset.unit));
|
||||
}
|
||||
var srcActions = readoutRow.querySelector('td.flex');
|
||||
if (srcActions) srcActions.querySelectorAll('form').forEach(function(f) {
|
||||
var cloned = f.cloneNode(true);
|
||||
var span = cloned.querySelector('button span');
|
||||
if (span) span.remove();
|
||||
wrap.appendChild(cloned);
|
||||
});
|
||||
td.appendChild(wrap);
|
||||
}
|
||||
});
|
||||
|
||||
// Created at (from first row of group)
|
||||
var tdCreated = tr.insertCell();
|
||||
var createdAtEl = group.rows[0].querySelector('.created-at');
|
||||
tdCreated.textContent = createdAtEl ? createdAtEl.textContent : '';
|
||||
});
|
||||
|
||||
wideContainer.innerHTML = '';
|
||||
wideContainer.appendChild(table);
|
||||
}
|
||||
|
||||
function getMeasurementsView() {
|
||||
return localStorage.getItem('measurements-view') || 'compact';
|
||||
@@ -121,7 +29,6 @@ function getMeasurementsView() {
|
||||
|
||||
function applyMeasurementsView(view) {
|
||||
document.body.dataset.measurementsView = view;
|
||||
if (view === 'wide') buildWideTable();
|
||||
}
|
||||
|
||||
function setMeasurementsView(view) {
|
||||
@@ -137,7 +44,6 @@ document.addEventListener('turbo:load', function() {
|
||||
applyMeasurementsView(getMeasurementsView());
|
||||
new MutationObserver(function() {
|
||||
groupMeasurements();
|
||||
if (getMeasurementsView() === 'wide') buildWideTable();
|
||||
}).observe(tbody, {
|
||||
childList: true, subtree: true,
|
||||
attributes: true, attributeFilter: ['style']
|
||||
@@ -172,26 +78,6 @@ window.detailsObserver = new MutationObserver((mutations) => {
|
||||
mutations[0].target.dispatchEvent(new Event('change', {bubbles: true}))
|
||||
});
|
||||
|
||||
function editMeasurementWide(url) {
|
||||
fetch(url, {
|
||||
headers: {
|
||||
'Accept': 'text/vnd.turbo-stream.html',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
Turbo.renderStreamMessage(html);
|
||||
requestAnimationFrame(() => {
|
||||
var panel = document.getElementById('measurement_edit_form');
|
||||
if (panel && panel.firstElementChild) {
|
||||
panel.scrollIntoView({behavior: 'smooth', block: 'nearest'});
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => console.error('editMeasurementWide failed:', err));
|
||||
}
|
||||
window.editMeasurementWide = editMeasurementWide
|
||||
|
||||
function readoutUnitChanged(select) {
|
||||
var button = select.closest('tr').querySelector('.set-default-unit');
|
||||
|
||||
Reference in New Issue
Block a user