158 lines
8.4 KiB
JavaScript
158 lines
8.4 KiB
JavaScript
// Generated, do not edit. sha256:148777e8a3d0dd6536d8b6d4b8cae6c0c4cbf2ed945fdf8bb8ed57c23e617bd8
|
|
import { listBookableAssets } from '../data/assets.js';
|
|
import { appSetting, setAppSetting } from '../app-settings.js';
|
|
import { t } from '../i18n/index.js';
|
|
import { renderNotifications } from '../actions/notifications-widget.js';
|
|
import { currentStanding } from '../wealth/data.js';
|
|
import { fmtBase, fmtBaseSigned, colouredSigns, fmtUnit, val } from '../ui/format.js';
|
|
import { esc } from '../ui/escape.js';
|
|
import { assetLabel, assetLabelHtml } from '../ui/asset-label.js';
|
|
import { originsHtml } from '../ui/price-origin.js';
|
|
import { setPageHint } from '../ui/page-hint.js';
|
|
import { CHEVRON_RIGHT } from '../ui/icons.js';
|
|
import { renderDashboard } from '../dashboard/dashboard.js';
|
|
import { renderSpotWidget, renderSpotSettings } from '../widgets/spot.js';
|
|
import { secondCurrencyOf, secondAmount, renderSecondCurrencySetting } from '../widgets/second-currency.js';
|
|
import { STANDING_GROUPINGS, standingGroupingOf, groupStanding, standingToday, standingShowsToday } from '../widgets/standing-groups.js';
|
|
import { renderMetricsWidget } from '../widgets/metrics.js';
|
|
import { renderClocksWidget, renderClocksSettings } from '../widgets/clocks.js';
|
|
import { renderReturnWidget, renderReturnSettings } from '../widgets/return.js';
|
|
import { registerWidget, availableWidgets } from '../widgets/registry.js';
|
|
import { DASHBOARD, TARGETS, targetHash } from '../../shared/config.js';
|
|
|
|
export const title = () => t('home.title');
|
|
|
|
registerWidget('notifications', { render: host => renderNotifications(host) });
|
|
registerWidget('standing', { render: (host, inst) => renderStandingWidget(host, inst), settings: renderStandingSettings });
|
|
registerWidget('spot', { render: (host, inst) => renderSpotWidget(host, inst), settings: renderSpotSettings });
|
|
registerWidget('metrics', { render: host => renderMetricsWidget(host) });
|
|
registerWidget('clocks', { render: (host, inst) => renderClocksWidget(host, inst), settings: renderClocksSettings });
|
|
registerWidget('return', { render: (host, inst) => renderReturnWidget(host, inst), settings: renderReturnSettings });
|
|
|
|
async function buildCatalog(layout) {
|
|
const present = new Set(layout.map(it => it.instId));
|
|
const persona = appSetting('persona');
|
|
const { renderers } = availableWidgets();
|
|
const items = [];
|
|
for (const [type, def] of Object.entries(DASHBOARD.WIDGETS)) {
|
|
if (def.instanceable || present.has(type) || !renderers[type]) continue;
|
|
if (def.persona && def.persona !== persona) continue;
|
|
items.push({ instId: type, type, params: null, label: t(def.label_key) });
|
|
}
|
|
let n = 1;
|
|
while (present.has(`return-${n}`)) n++;
|
|
items.push({ instId: `return-${n}`, type: 'return', params: null, label: t('dashboard.widget.return') });
|
|
for (const a of await listBookableAssets()) {
|
|
const instId = `spot-${a.id}`;
|
|
if (present.has(instId)) continue;
|
|
items.push({
|
|
instId, type: 'spot', params: { assetId: a.id },
|
|
label: `${t('dashboard.widget.spot')}: ${assetLabel(a)}`,
|
|
labelHtml: `${esc(t('dashboard.widget.spot'))}: ${assetLabelHtml(a)}`,
|
|
});
|
|
}
|
|
return items;
|
|
}
|
|
|
|
export async function render(el, opts = {}) {
|
|
if (!el.querySelector('#dashboard-root')) el.innerHTML = '<div id="dashboard-root"></div>';
|
|
|
|
const { renderers, settings } = availableWidgets();
|
|
await renderDashboard(el.querySelector('#dashboard-root'), {
|
|
renderers,
|
|
settings,
|
|
edit: !!opts.edit,
|
|
onEdit: opts.setEdit,
|
|
catalog: buildCatalog,
|
|
toolbarHost: document.getElementById('nav-tools') || document.getElementById('page-tools'),
|
|
});
|
|
|
|
setPageHint(el, { title: t('home.hintTitle'), rows: [
|
|
t('home.hintEdit'), t('home.hintDone'), t('home.hintAdd'), t('home.hintWidget'), t('home.hintProfiles'),
|
|
] });
|
|
}
|
|
|
|
function standingGroupLabel(g, by) {
|
|
const r = g.sample;
|
|
if (by === 'asset') return assetLabelHtml(r.asset);
|
|
if (by === 'assetKind') return esc(t('enums.asset_kind.' + r.asset.kind));
|
|
if (by === 'accountKind') return esc(g.key === 'none' ? t('wealth.noAccount') : t('enums.account_kind.' + g.key));
|
|
return esc(r.account.name);
|
|
}
|
|
|
|
function todayHtml(amount, cls, always = false) {
|
|
if (amount == null || (!always && Math.abs(amount) < 0.005)) return '';
|
|
const tone = amount > 0.005 ? 't-pos' : amount < -0.005 ? 't-neg' : '';
|
|
return `<span class="${[cls, tone].filter(Boolean).join(' ')}">${val(fmtBaseSigned(amount, colouredSigns()))}</span>`;
|
|
}
|
|
|
|
async function renderStandingSettings(host, instance, commit) {
|
|
host.classList.add('dash-settings--wide');
|
|
const huidig = () => standingGroupingOf(instance.params && instance.params.groupBy);
|
|
const label = { account: 'home.groupAccount', accountKind: 'home.groupAccountKind', asset: 'home.groupAsset', assetKind: 'home.groupAssetKind' };
|
|
const chips = () => STANDING_GROUPINGS.map(id => `<button type="button" class="fbar-item${huidig() === id ? ' is-on' : ''}"
|
|
data-standing-group="${id}" aria-pressed="${huidig() === id}">${esc(t(label[id]))}</button>`).join('');
|
|
const block = document.createElement('div');
|
|
block.className = 'dash-set-row';
|
|
block.innerHTML = `<span class="t-label t-label--ter">${esc(t('home.groupBy'))}</span><div class="fbar fbar--chip">${chips()}</div>`;
|
|
host.appendChild(block);
|
|
block.addEventListener('click', e => {
|
|
const chip = e.target.closest('[data-standing-group]');
|
|
if (!chip || chip.dataset.standingGroup === huidig()) return;
|
|
instance.params = { ...(instance.params || {}), groupBy: standingGroupingOf(chip.dataset.standingGroup) };
|
|
block.querySelector('.fbar').innerHTML = chips();
|
|
commit({ resize: true });
|
|
});
|
|
const today = document.createElement('div');
|
|
today.className = 'dash-set-row';
|
|
today.innerHTML = `<label class="clockw-check"><span class="toggle-switch"><input type="checkbox" data-standing-today${standingShowsToday(instance.params) ? ' checked' : ''}/><span class="toggle-track"></span></span><span class="clockw-check-name">${esc(t('home.showToday'))}</span></label>`;
|
|
host.appendChild(today);
|
|
today.addEventListener('change', e => {
|
|
const box = e.target.closest('[data-standing-today]');
|
|
if (!box) return;
|
|
instance.params = { ...(instance.params || {}), showToday: box.checked };
|
|
commit({ resize: true });
|
|
});
|
|
await renderSecondCurrencySetting(host, instance, commit);
|
|
}
|
|
|
|
async function renderStandingWidget(host, instance) {
|
|
const by = standingGroupingOf(instance && instance.params && instance.params.groupBy);
|
|
const second = secondCurrencyOf(instance);
|
|
host.innerHTML = `<div class="card standing-card">
|
|
<div class="flex-between">
|
|
<div class="t-label">${t('home.standing')}</div>
|
|
<a class="wealth-detail-btn card-go-btn" href="${targetHash(TARGETS.WEALTH)}" title="${esc(t('home.toWealth'))}" aria-label="${esc(t('home.toWealth'))}">${CHEVRON_RIGHT}</a>
|
|
</div>
|
|
<div id="standing-body" class="t-body-sm t-muted">…</div>
|
|
</div>`;
|
|
try {
|
|
const s = await currentStanding();
|
|
const body = host.querySelector('#standing-body');
|
|
if (s.rows.length === 0) { body.textContent = t('home.standingNone'); return; }
|
|
|
|
const toon = standingShowsToday(instance && instance.params);
|
|
const perGroup = toon ? standingToday(s.rows, by, s.today) : new Map();
|
|
const lines = groupStanding(s.rows, by).map(g => `<div class="dash-trow stw-acc">
|
|
<span class="tname"><span class="stw-dot" aria-hidden="true"></span>${standingGroupLabel(g, by)}</span>
|
|
<span class="stw-today-cell">${todayHtml(perGroup.get(g.key), 'stw-today-row')}</span>
|
|
<span class="n po-host">${val(fmtBase(g.value))}${originsHtml(g.origins)}</span>
|
|
</div>`).join('');
|
|
const vandaag = toon && s.today
|
|
? `<div class="stw-today">${todayHtml(s.today.total, '', true)}</div>` : '';
|
|
const tweede = second ? await secondAmount(s.total, second, { decimals: 0 }) : '';
|
|
|
|
body.className = '';
|
|
body.innerHTML = `
|
|
<div class="sigw-price po-host" style="margin:0.4rem 0 0.1rem">${val(fmtBase(s.total))}${tweede ? `<span class="amount-second">${val(tweede)}</span>` : ''}${originsHtml(s.rows.map(r => ({ origin: r.origin, asset: r.asset })))}</div>
|
|
${vandaag}
|
|
<div class="t-muted-cell" style="margin-bottom:0.6rem">${s.rows.every(r => r.asset.id === s.rows[0].asset.id) ? val(fmtUnit(s.totalSats, s.rows[0].asset)) : ''}</div>
|
|
<div class="dash-tables stw-grid">${lines}</div>`;
|
|
|
|
} catch (err) {
|
|
console.error('[standing-widget]', err);
|
|
const body = host.querySelector('#standing-body');
|
|
if (body) body.textContent = '⚠ ' + err.message;
|
|
}
|
|
}
|