asxpio/public/admin-invoice-form.js
Sergei Poljanski 226ccb8192
invoicing: admin and public views
Three admin templates:

- index: invoice list with status badge and link to detail.
- new: client + terms fields, dynamic line-item rows backed by
  admin-invoice-form.js (add/remove + re-index of items[N][...] names).
- show: invoice detail, public URL to share, toggle-paid button.

One public template, invoice_public.erb, shown at /i/:uuid: client name,
number, total, status, and a single big download button to /i/:uuid/pdf.

All admin pages are noindex; same for the public landing (it's a
client-specific URL, not search-discoverable content).
2026-05-26 17:54:40 +03:00

28 lines
1,017 B
JavaScript

(function () {
const tbody = document.querySelector('#items-table tbody');
const addBtn = document.getElementById('add-row');
function reindex() {
Array.from(tbody.querySelectorAll('tr.item-row')).forEach((row, i) => {
row.querySelectorAll('input').forEach((input) => {
input.name = input.name.replace(/items\[\d+\]/, 'items[' + i + ']');
});
});
}
addBtn.addEventListener('click', function () {
const last = tbody.querySelector('tr.item-row:last-child');
const clone = last.cloneNode(true);
clone.querySelectorAll('input').forEach((input) => { input.value = ''; });
tbody.appendChild(clone);
reindex();
});
tbody.addEventListener('click', function (e) {
if (!e.target.matches('.remove-row')) return;
const rows = tbody.querySelectorAll('tr.item-row');
if (rows.length <= 1) return;
e.target.closest('tr.item-row').remove();
reindex();
});
})();