// Generated, do not edit. sha256:7498a98f290e59aca597eb4a57338d467525bf6c2933429d69f5e8474930a0ac import { aiProposalsStatus, acceptAiProposal, acceptAiProposals, rejectAiProposal, rejectAiProposals } from '../data/ai-proposals.js'; import { CONFIG, PREFERENCES } from '../../shared/config.js'; import { appSetting, timerSetting, loadAppSettings } from '../app-settings.js'; import { t } from '../i18n/index.js'; import { assetLabelHtml } from '../ui/asset-label.js'; import * as dialog from '../ui/dialog.js'; import { esc } from '../ui/escape.js'; import { fmtBase, fmtBasePrice, fmtUnit, priceFromBase, val, currencyName } from '../ui/format.js'; import { taskCardHtml, nothingPendingHtml } from '../ui/task-card.js'; import { priceUnitLabel } from '../../shared/units.js'; import { localDateNl } from '../../shared/time.js'; const ONOMKEERBAAR = new Set(CONFIG.AI.DESTRUCTIVE_PROPOSAL_KINDS); const assetVan = p => ({ asset_name: p.asset_name, asset_symbol: p.asset_symbol }); function detailTekst(detail) { if (typeof detail !== 'string' || !detail.startsWith('{')) return detail; try { const o = JSON.parse(detail); return o && o.key ? t(o.key, o.params) : detail; } catch { return detail; } } const meta = p => ({ kind: p.asset_kind, decimals: p.asset_decimals, unit: p.asset_unit, subunit: p.asset_subunit, displayUnit: p.asset_display_unit, }); const eenheidIsSymbool = p => { const asset = meta(p); const getoond = asset.displayUnit === 'subunit' && asset.subunit ? asset.subunit : asset.unit; return String(getoond || '').toUpperCase() === String(p.asset_symbol || '').toUpperCase(); }; const koers = p => { const asset = meta(p); return `${fmtBasePrice(priceFromBase(p.fiatPrice, asset))} ${t('aiProposals.perUnit')} ${priceUnitLabel(asset)}`; }; const leesbareWaarde = (p, json) => { try { return toon(p, JSON.parse(json)); } catch { return String(json); } }; const toon = (p, v) => { const spec = PREFERENCES[p.scope]?.[p.key]; if (spec?.type === 'bool') return t(Number(v) ? 'aiProposals.preferenceOn' : 'aiProposals.preferenceOff'); return String(v); }; const huidigeVoorkeur = (p) => { try { const v = p.scope === 'timers' ? timerSetting(p.key) : appSetting(p.key); return v == null ? null : toon(p, v); } catch { return null; } }; const OMSCHRIJVING = { price_point: p => `${t('aiProposals.kindPricePoint')}: ${assetLabelHtml(assetVan(p))} · ${val(koers(p))} ` + `${esc(localDateNl(p.observedAt))}`, account: p => `${t('aiProposals.kindAccount')}: ${esc(p.name)} ` + `${esc(t('enums.account_kind.' + p.accountKind))} · ` + `${esc(p.isOwnCustody ? t('aiProposals.ownCustody') : t('aiProposals.thirdParty'))}`, account_delete: p => `${t('aiProposals.kindAccountDelete')}: ${esc(p.account_name)} ` + `${esc(t('enums.account_kind.' + p.account_kind))}`, portfolio: p => `${t('aiProposals.kindPortfolio')}: ${esc(p.name)} ` + `${esc(t('aiProposals.portfolioIn'))} ${esc(currencyName(p.currency))} (${esc(p.currency)})
` + `${esc(t('aiProposals.portfolioNote'))}`, preference: p => { const nu = huidigeVoorkeur(p); return `${t('aiProposals.kindPreference')}: ${esc(p.scope)}.${esc(p.key)} ` + `→ ${val(leesbareWaarde(p, p.value))}` + (nu === null ? '' : ` (${esc(t('aiProposals.preferenceCurrent'))} ${esc(nu)})`) + `
${esc(t('aiProposals.preferenceNote'))}`; }, source_choice: p => `${t('aiProposals.kindSourceChoice')}: ${assetLabelHtml(assetVan(p))} · ` + `${esc(t('integrations.sourceCap.' + p.scope))} · ${esc(p.provider_name || p.key)} ` + `${esc(t(p.value === 'first' ? 'aiProposals.sourceFirst' : 'aiProposals.sourceRemove'))}
` + `${esc(t('aiProposals.sourceChoiceNote'))}`, valuation: p => { const asset = meta(p); const grootboek = p.ledger_balance == null ? null : Number(p.ledger_balance); const verschil = grootboek == null ? null : Number(p.balance) - grootboek; const naast = grootboek == null ? '' : ` · ${t('aiProposals.ledger')} ${val(fmtUnit(grootboek, asset))}` + (verschil === 0 ? '' : ` · ${t('aiProposals.diff')} ${verschil > 0 ? '+' : '−'}${val(fmtUnit(Math.abs(verschil), asset))}`); return `${t('aiProposals.kindValuation')}: ${val(fmtUnit(p.balance, asset))} ` + `${assetLabelHtml(assetVan(p), { withoutSymbol: eenheidIsSymbool(p) })} ` + `· ${esc(p.account_name || '')}${naast}
` + `${esc(localDateNl(p.observedAt))}`; }, event: p => { const waar = [p.from_name, p.to_name].filter(Boolean).map(esc).join(' → '); const prijs = p.fiatPrice == null ? '' : ` · ${val(koers(p))}`; const bedragen = [ p.fiatFee == null ? null : val(fmtBase(p.fiatFee)), p.unitFee == null ? null : val(fmtUnit(p.unitFee, meta(p))), ].filter(Boolean); const kosten = bedragen.length ? ` · ${t('aiProposals.fee')} ${bedragen.join(' + ')}` : ''; return `${esc(t('enums.event_type.' + p.eventType))}: ${val(fmtUnit(p.amount, meta(p)))} ` + `${assetLabelHtml(assetVan(p), { withoutSymbol: eenheidIsSymbool(p) })}${prijs}${kosten}
` + `${waar} · ${esc(localDateNl(p.observedAt))}`; }, }; function regel(p) { const maak = OMSCHRIJVING[p.kind]; const tekst = maak ? maak(p) : esc(t('aiProposals.unknownKind') + ` (${p.kind})`); const notitie = p.note ? `
${esc(p.note)}
` : ''; const mislukt = p.detail ? `
${esc(t('aiProposals.failed'))}: ${esc(detailTekst(p.detail))}
` : ''; const weg = ONOMKEERBAAR.has(p.kind); const waarschuwing = weg ? `
${esc(t('aiProposals.irreversible'))}
` : ''; const opening = p.kind === 'valuation' && p.events_before === 0 ? `
${esc(t('aiProposals.openingBalance'))}
` : ''; return `
${tekst}${notitie}${waarschuwing}${opening}${mislukt}
`; } function redenTekst(u) { if (!u.key) return u.reden; try { return t(u.key, u.params); } catch { return u.reden; } } async function meldMislukt(uitkomsten) { const stuk = uitkomsten.filter(u => !u.ok); if (!stuk.length) return; await dialog.alert({ title: t('aiProposals.partialTitle'), message: `${t('aiProposals.partialBody')}\n\n${stuk.map(u => `- ${redenTekst(u)}`).join('\n')}`, }); } let live = null; let liveGebonden = false; function bindLive() { if (liveGebonden) return; liveGebonden = true; document.addEventListener('ai-proposals-changed', async () => { if (!live || !live.host || !live.host.isConnected) { live = null; return; } try { await renderAiProposals(live.host, live.refresh); } catch { } }); } export async function renderAiProposals(host, refresh) { live = { host, refresh }; bindLive(); const { enabled, pending } = await aiProposalsStatus(); if (!enabled) { host.innerHTML = ''; return null; } if (!pending.length) { host.innerHTML = taskCardHtml({ title: t('aiProposals.title'), body: nothingPendingHtml() }); return 0; } const teVerwijderen = pending.filter(p => ONOMKEERBAAR.has(p.kind)).length; const bulkUitleg = teVerwijderen ? `
${esc(t('aiProposals.bulkSkips').replace('{n}', teVerwijderen))}
` : ''; host.innerHTML = taskCardHtml({ title: t('aiProposals.title'), actions: `
`, body: `
${t('aiProposals.intro')}
${bulkUitleg} ${pending.map(regel).join('')}`, }); const ids = pending.filter(p => !ONOMKEERBAAR.has(p.kind)).map(p => p.id); const alleIds = pending.map(p => p.id); host.querySelector('#aiprop-accept-all').onclick = async () => { if (!ids.length) { await dialog.alert({ title: t('aiProposals.title'), message: t('aiProposals.bulkNothing') }); return; } if (!(await dialog.confirm({ message: t('aiProposals.acceptAllConfirm').replace('{n}', ids.length) }))) return; await meldMislukt(await acceptAiProposals(ids)); if (pending.some(p => ids.includes(p.id) && p.kind === 'preference')) { await loadAppSettings(); location.reload(); return; } refresh(); }; host.querySelector('#aiprop-reject-all').onclick = async () => { if (!(await dialog.confirm({ message: t('aiProposals.rejectAllConfirm').replace('{n}', alleIds.length), danger: true }))) return; await meldMislukt(await rejectAiProposals(alleIds, t('aiProposals.rejectedByUser'))); refresh(); }; host.querySelectorAll('[data-aiprop]').forEach(rij => { const id = Number(rij.dataset.aiprop); rij.querySelectorAll('[data-aiact]').forEach(knop => knop.onclick = async () => { try { if (knop.dataset.aiact === 'accept') { if (rij.dataset.aidanger === '1' && !(await dialog.confirm({ message: t('aiProposals.acceptDeleteConfirm'), danger: true }))) return; await acceptAiProposal(id); if (rij.dataset.aikind === 'preference') { await loadAppSettings(); location.reload(); return; } } else await rejectAiProposal(id, t('aiProposals.rejectedByUser')); } catch (e) { await dialog.alert({ title: t('dialog.error'), message: e.message }); } refresh(); }); }); return pending.length; }