Files
AtreidesAssetsFree/client/pages/reservations.js
T

93 lines
5.5 KiB
JavaScript
Raw Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:877ef362c70027a6a3b598d45680633fae5e6da42177c2b820d61d28df5d8d58
import { listSelectableAccounts } from '../data/accounts.js';
import { listBookableAssets } from '../data/assets.js';
import { createReservation, deleteReservation, listReservations, listReservationsForAccount, updateReservation } from '../data/reservations.js';
import { tableViewCfg } from '../data/preferences.js';
import { t } from '../i18n/index.js';
import { assetLabelHtml } from '../ui/asset-label.js';
import * as dialog from '../ui/dialog.js';
import { renderTable } from '../ui/table.js';
import { takeRowFocus } from '../ui/row-focus.js';
import { fmtUnit, val, toSubunit, fromSubunit } from '../ui/format.js';
import { esc } from '../ui/escape.js';
import { setPageHint } from '../ui/page-hint.js';
import { localDate, localDateNl, toUtcIso } from '../../shared/time.js';
import { bookReservationFlow, reservationAccount, reservationDir, reservationMeta } from '../actions/reservation-book.js';
export async function render(el, { accountId = null, assetId = null, assetKinds = null } = {}) {
el.innerHTML = `<div id="tbl"></div>`;
const onPage = accountId == null;
if (onPage) setPageHint(el, { title: t('nav.reservations'), rows: [t('reservations.hint')] });
const allowed = a => (assetId == null || a.id === assetId) && (assetKinds == null || assetKinds.includes(a.kind));
const assets = (await listBookableAssets()).filter(allowed);
const assetById = id => assets.find(a => a.id === Number(id));
const rows = async () => (onPage ? await listReservations() : await listReservationsForAccount(accountId))
.filter(r => assetId == null || r.assetId === assetId);
const now = new Date().toISOString();
const dirLabel = d => t(d === 'out' ? 'reservations.dirOut' : 'reservations.dirIn');
const accountOpts = async current => (await listSelectableAccounts({ ookId: Number(current) || null }))
.map(a => ({ value: a.id, label: a.name }));
const columns = [
{ key: 'occurredAt', label: t('reservations.colDate'), sortable: true,
render: r => `${esc(localDateNl(r.occurredAt))}${r.occurredAt <= now
? ` <span class="badge badge-accent">${t('reservations.due')}</span>` : ''}`,
edit: { type: 'date', value: r => (r ? localDate(r.occurredAt) : localDate(now)) } },
...(onPage ? [{ key: '_account', label: t('reservations.colAccount'), sortable: true,
sortValue: r => reservationAccount(r).name || '',
render: r => esc(reservationAccount(r).name || '-'),
group: { value: r => reservationAccount(r).name || '-', label: v => v },
edit: { type: 'select', sticky: true, value: r => (r ? reservationAccount(r).id : ''),
options: vals => accountOpts(vals._account) } }] : []),
{ key: '_dir', label: t('reservations.colDirection'), sortable: true, sortValue: r => reservationDir(r),
render: r => dirLabel(reservationDir(r)),
edit: { type: 'select', sticky: true, value: r => (r ? reservationDir(r) : 'out'),
options: () => [{ value: 'out', label: dirLabel('out') }, { value: 'in', label: dirLabel('in') }] } },
{ key: 'assetId', label: t('reservations.colAsset'), sortable: true, sortValue: r => r.asset_symbol,
render: r => assetLabelHtml(r),
edit: { type: 'select', sticky: true, options: () => assets.map(a => ({ value: a.id, label: `${a.symbol}: ${a.name}` })) } },
{ key: 'amount', label: t('reservations.colAmount'), align: 'num', sortable: true, sortValue: r => r.amount,
render: r => val(fmtUnit(r.amount, reservationMeta(r))),
edit: { type: 'number', attrs: { min: 0, step: 'any' },
value: r => (r ? fromSubunit(r.amount, reservationMeta(r)) : '') } },
{ key: 'note', label: t('reservations.colNote'), text: true,
render: r => `<span class="t-muted-cell">${esc(r.note || '')}</span>`,
edit: { type: 'text', expand: true } },
];
const toPayload = v => {
const asset = assetById(v.assetId);
const qty = parseFloat(v.amount);
const account = onPage ? Number(v._account) || null : accountId;
if (!v.occurredAt || !asset || !account || !Number.isFinite(qty) || qty <= 0) throw new Error(t('reservations.fill'));
return {
accountId: account, assetId: asset.id, direction: v._dir === 'in' ? 'in' : 'out',
amount: toSubunit(qty, asset), occurredAt: toUtcIso(`${v.occurredAt}T00:00:00`),
note: (v.note || '').trim() || null,
};
};
let tableApi = null;
const reload = async () => tableApi.refresh(await rows());
tableApi = renderTable(el.querySelector('#tbl'), {
columns, rows: await rows(),
...(await tableViewCfg('reservations', { key: 'occurredAt', dir: 'asc' })),
emptyText: t(onPage ? 'reservations.noneAll' : 'reservations.none'),
noneLabel: t('table.none'),
newLabel: t('table.new'),
newDefaults: { _dir: 'out', assetId: assets[0]?.id ?? '', note: '' },
saveLabel: t('transfers.save'), cancelLabel: t('transfers.cancel'),
onCreate: async v => { const id = await createReservation(toPayload(v)); tableApi.refresh(await rows(), { locate: id }); },
onSave: async (id, v) => { await updateReservation(id, toPayload(v)); reload(); },
onDelete: async id => {
if (!(await dialog.confirm({ message: t('reservations.confirmDelete'), danger: true }))) return;
await deleteReservation(id); reload();
},
extraActions: [{ key: 'book', label: t('reservations.book') }],
onAction: async (key, id) => {
const r = (await rows()).find(x => x.id === id);
if (key === 'book' && r && await bookReservationFlow(r)) reload();
},
});
tableApi.focusRow(takeRowFocus('reservations'));
}