Versie 0.3.3
This commit is contained in:
1 parent
1cf6eeed29
commit
6e741a51fe
57 files changed
+1362
-2135
No files matched your search
@@ -0,0 +1,420 @@
|
||||
// Generated, do not edit. sha256:86d4e38921922eb14e5463ba3c4d6855cd2e5276d5295930d9126848cf48e05c
|
||||
import { t, localeTag } from '../i18n/index.js';
|
||||
import { serverFoutTekst } from '../data/_rpc.js';
|
||||
import { esc } from '../ui/escape.js';
|
||||
import * as dialog from '../ui/dialog.js';
|
||||
import { setPageHint } from '../ui/page-hint.js';
|
||||
import { getSettings, updateSettings } from '../data/preferences.js';
|
||||
import { firstPositionDate } from '../data/positions.js';
|
||||
import { switchPortfolio, reloadWhenBack, refreshActivePortfolio } from '../portfolio.js';
|
||||
import { backupDb, exportDb, restoreDb, startFresh } from './db-file.js';
|
||||
import { openPopover, closePopover } from '../ui/popover.js';
|
||||
import { expanders } from '../ui/expand.js';
|
||||
import { PLUS, CHEVRON } from '../ui/icons.js';
|
||||
import { currencyName } from '../ui/format.js';
|
||||
import { CONFIG, ENUMS } from '../../shared/config.js';
|
||||
import { counterText, limitFor, locked } from '../license.js';
|
||||
import { counterHtml } from '../ui/lock.js';
|
||||
import { baseCurrency } from '../app-settings.js';
|
||||
import { openSetup } from '../onboarding/setup.js';
|
||||
|
||||
async function api(pad, body) {
|
||||
const opties = body
|
||||
? { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }
|
||||
: {};
|
||||
const r = await fetch(pad, opties);
|
||||
if (r.status === 401) {
|
||||
location.replace(CONFIG.AUTH.LOGIN_PATH);
|
||||
throw new Error(t('common.sessionExpired'));
|
||||
}
|
||||
const uit = await r.json().catch(() => null);
|
||||
if (!r.ok) throw new Error(serverFoutTekst(uit) || `HTTP ${r.status}`);
|
||||
return uit;
|
||||
}
|
||||
|
||||
const datum = (iso) => (iso ? new Date(iso).toLocaleDateString(localeTag()) : null);
|
||||
|
||||
const DAG_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
const backupTeOud = (iso) => !iso || Date.now() - new Date(iso).getTime() > CONFIG.BACKUP.REMIND_AFTER_DAYS * DAG_MS;
|
||||
|
||||
function menuItems(p, { full, beheer }) {
|
||||
if (!beheer) return [];
|
||||
const kopie = p.heeftDatabase && !full ? [['kopieer', t('portfolios.copy')]] : [];
|
||||
return p.actief
|
||||
? [...kopie, ['exporteer', t('dbfile.export')], ['terugzetten', t('dbfile.import')], ['inrichten', t('setup.rerun')],
|
||||
['leeg', t('dbfile.fresh')]]
|
||||
: [...kopie, ['hernoem', t('portfolios.rename')], ['verwijder', t('portfolios.remove')]];
|
||||
}
|
||||
|
||||
function feitHtml(label, waarde, { late = false } = {}) {
|
||||
return `<div class="pf-fact${late ? ' is-late' : ''}"><dt>${label}</dt><dd>${waarde}</dd></div>`;
|
||||
}
|
||||
|
||||
const soortenVan = (telling, enumNaam) => ENUMS[enumNaam.toUpperCase()].filter(k => telling?.[k]);
|
||||
|
||||
function perSoortHtml(label, telling, enumNaam, regels) {
|
||||
const getal = (n) => esc(Number(n).toLocaleString(localeTag()));
|
||||
const soorten = soortenVan(telling, enumNaam);
|
||||
const totaal = soorten.reduce((s, k) => s + telling[k], 0);
|
||||
const lijst = soorten.map(k => `<li><span>${esc(t(`enums.${enumNaam}.${k}`))}</span><span>${getal(telling[k])}</span></li>`).join('');
|
||||
return `<div class="pf-fact"><dt>${label}</dt><dd>${getal(totaal)}</dd>
|
||||
<ul class="pf-kinds" style="--pf-lines:${Number(regels) || 1}">${lijst}</ul></div>`;
|
||||
}
|
||||
|
||||
function kaartHtml(p, { full, beheer, regels }) {
|
||||
const k = p.kerncijfers;
|
||||
const getal = (n) => esc(Number(n).toLocaleString(localeTag()));
|
||||
const sub = !p.heeftDatabase ? t('portfolios.noDatabase')
|
||||
: k && k.munt ? esc(k.eersteJaar ? t('portfolios.cardSince', { munt: k.munt, jaar: k.eersteJaar }) : k.munt)
|
||||
: '';
|
||||
const feiten = !k ? '' : `<dl class="pf-facts">
|
||||
${perSoortHtml(t('portfolios.factAssets'), k.assets, 'asset_kind', regels)}
|
||||
${perSoortHtml(t('portfolios.factAccounts'), k.accounts, 'account_kind', regels)}
|
||||
${feitHtml(t('portfolios.factEvents'), getal(k.boekingen))}
|
||||
${feitHtml(t('portfolios.factValuations'), getal(k.balansen))}
|
||||
${feitHtml(t('portfolios.factLastUsed'), esc(datum(p.laatstGebruikt) || t('portfolios.never')))}
|
||||
${beheer ? feitHtml(t('portfolios.factBackup'), esc(datum(k.laatsteBackup) || t('portfolios.backupNever')),
|
||||
{ late: backupTeOud(k.laatsteBackup) }) : '<div class="pf-fact"></div>'}
|
||||
</dl>`;
|
||||
const backupKlasse = k && backupTeOud(k.laatsteBackup) ? 'btn-primary' : 'btn-secondary';
|
||||
const hoofd = !p.actief
|
||||
? `<button class="btn btn-secondary btn-sm" data-actie="open" data-naam="${esc(p.naam)}">${t('portfolios.activate')}</button>`
|
||||
: `<button class="btn btn-secondary btn-sm" data-actie="instellingen" data-naam="${esc(p.naam)}">${t('portfolios.settingsButton')}</button>
|
||||
${beheer ? `<button class="btn ${backupKlasse} btn-sm" data-actie="backup">${t('dbfile.backup')}</button>` : ''}`;
|
||||
const menu = menuItems(p, { full, beheer }).length
|
||||
? `<button class="btn btn-ghost btn-sm pf-more" data-menu="${esc(p.naam)}" title="${esc(t('portfolios.more'))}" aria-label="${esc(t('portfolios.more'))}">…</button>`
|
||||
: '';
|
||||
return `<div class="card pf-card${p.actief ? ' is-open' : ''}">
|
||||
<div class="pf-head">
|
||||
<div class="pf-name">${esc(p.naam)}</div>
|
||||
${p.demo ? `<span class="badge">${t('portfolios.demoBadge')}</span>` : ''}
|
||||
${p.actief ? `<span class="badge badge-accent pf-head-end">${t('portfolios.openNow')}</span>` : ''}
|
||||
</div>
|
||||
<div class="pf-sub t-body-sm t-muted">${sub}</div>
|
||||
${feiten}
|
||||
<div class="pf-actions">${hoofd}${menu}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function archiefRijHtml(a) {
|
||||
return `<div class="flex gap-sm" style="align-items:center; flex-wrap:wrap">
|
||||
<div style="flex:1 1 12rem">
|
||||
<div class="t-body">${esc(a.naam)}</div>
|
||||
<div class="t-body-sm t-muted">${esc(datum(a.gearchiveerd) || '')}</div>
|
||||
</div>
|
||||
<button class="btn btn-danger btn-sm" data-actie="wis" data-naam="${esc(a.naam)}">${t('portfolios.purge')}</button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function authHtml(auth) {
|
||||
const stand = auth.bron === 'omgeving' ? t('portfolios.authEnv')
|
||||
: auth.ingesteld ? t('portfolios.authSet')
|
||||
: t('portfolios.authNone');
|
||||
const knoppen = auth.wijzigbaar
|
||||
? `<button class="btn btn-secondary btn-sm" data-actie="auth-zet">${t('portfolios.authChange')}</button>
|
||||
${auth.ingesteld ? `<button class="btn btn-ghost btn-sm" data-actie="auth-leeg">${t('portfolios.authClear')}</button>` : ''}`
|
||||
: '';
|
||||
const uitloggen = auth.ingesteld
|
||||
? `<button class="btn btn-ghost btn-sm" data-actie="auth-uit">${t('portfolios.authLogout')}</button>`
|
||||
: '';
|
||||
return `<div class="pf-blok">
|
||||
<div class="t-h4">${t('portfolios.authTitle')}</div>
|
||||
<div class="t-body-sm mt-sm">${stand}</div>
|
||||
<div class="flex gap-sm mt-md" style="flex-wrap:wrap">${knoppen}${uitloggen}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function startJaarHtml({ gekozen, eersteJaar, huidigJaar }) {
|
||||
const AUTO = CONFIG.START_YEAR.AUTO;
|
||||
let inhoud;
|
||||
if (eersteJaar == null) {
|
||||
inhoud = `<div class="t-body-sm mt-sm">${t('portfolios.startYearEmpty')}</div>`;
|
||||
} else {
|
||||
const eersteKolom = Math.min(Math.max(eersteJaar + 1, gekozen), huidigJaar);
|
||||
const stand = gekozen === AUTO
|
||||
? t('portfolios.startYearAutoState', { jaar: eersteKolom })
|
||||
: t('portfolios.startYearState', { jaar: eersteKolom, eerste: eersteJaar });
|
||||
const jaren = [];
|
||||
for (let y = eersteJaar; y <= huidigJaar; y++) jaren.push(y);
|
||||
if (gekozen !== AUTO && !jaren.includes(gekozen)) jaren.unshift(gekozen);
|
||||
const opties = [`<option value="${AUTO}"${gekozen === AUTO ? ' selected' : ''}>${t('portfolios.startYearAuto')}</option>`]
|
||||
.concat(jaren.map(y => `<option value="${y}"${y === gekozen ? ' selected' : ''}>${y}</option>`));
|
||||
inhoud = `<div class="t-body-sm mt-sm">${stand}</div>
|
||||
<div class="form-group mt-md" style="margin-bottom:0">
|
||||
<label class="form-label" for="startjaar">${t('portfolios.startYearLabel')}</label>
|
||||
<select class="input" id="startjaar" style="max-width:320px">${opties.join('')}</select>
|
||||
</div>`;
|
||||
}
|
||||
return `<div class="pf-blok">
|
||||
<div class="t-h4">${t('portfolios.startYearTitle')}</div>
|
||||
${inhoud}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function rendementHtml({ gekozen }) {
|
||||
const opties = ENUMS.MWR_METHOD
|
||||
.map(m => `<option value="${m}"${m === gekozen ? ' selected' : ''}>${t(`portfolios.mwr_${m}`)}</option>`)
|
||||
.join('');
|
||||
return `<div class="pf-blok">
|
||||
<div class="t-h4">${t('portfolios.mwrTitle')}</div>
|
||||
<div class="form-group mt-sm" style="margin-bottom:0">
|
||||
<label class="form-label" for="mwrmethode">${t('portfolios.mwrLabel')}</label>
|
||||
<select class="input" id="mwrmethode" style="max-width:320px">${opties}</select>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
async function instellingenHtml() {
|
||||
const [auth, instellingen, eerstePositie] = await Promise.all([
|
||||
api('/api/auth/status'), getSettings(), firstPositionDate(),
|
||||
]);
|
||||
return authHtml(auth) + startJaarHtml({
|
||||
gekozen: Number(instellingen.start_year) || CONFIG.START_YEAR.AUTO,
|
||||
eersteJaar: eerstePositie ? Number(eerstePositie.slice(0, 4)) : null,
|
||||
huidigJaar: new Date().getFullYear(),
|
||||
}) + rendementHtml({ gekozen: String(instellingen.mwr_method || 'dietz') });
|
||||
}
|
||||
|
||||
async function openInstellingen(naam) {
|
||||
const html = await instellingenHtml();
|
||||
const klaar = dialog.detail({ title: t('portfolios.settingsOf', { naam }), html: `<div data-pf-settings>${html}</div>` });
|
||||
const host = document.querySelector('[data-pf-settings]');
|
||||
host?.closest('.dialog')?.classList.add('pf-settings-dialog');
|
||||
const ververs = async () => { if (host?.isConnected) host.innerHTML = await instellingenHtml(); };
|
||||
const fout = (e) => dialog.alert({ title: t('dialog.error'), message: e.message });
|
||||
if (host) {
|
||||
host.onclick = async (ev) => {
|
||||
const knop = ev.target.closest('[data-actie]');
|
||||
if (!knop) return;
|
||||
try {
|
||||
if (knop.dataset.actie === 'auth-zet' && await authZetten()) await ververs();
|
||||
if (knop.dataset.actie === 'auth-leeg' && await authLeegmaken()) await ververs();
|
||||
if (knop.dataset.actie === 'auth-uit') await uitloggen();
|
||||
} catch (e) { fout(e); }
|
||||
};
|
||||
host.onchange = async (ev) => {
|
||||
const veld = ev.target.matches('#mwrmethode') ? { mwr_method: ev.target.value }
|
||||
: ev.target.matches('#startjaar') ? { start_year: Number(ev.target.value) } : null;
|
||||
if (!veld) return;
|
||||
try {
|
||||
await updateSettings(veld);
|
||||
document.dispatchEvent(new Event('settings-changed'));
|
||||
await ververs();
|
||||
} catch (e) { fout(e); }
|
||||
};
|
||||
}
|
||||
await klaar;
|
||||
}
|
||||
|
||||
export async function render(el) {
|
||||
const [overzicht, expand] = await Promise.all([
|
||||
api('/api/portfolios'), expanders('portfolios', { archive: false }),
|
||||
]);
|
||||
refreshActivePortfolio();
|
||||
const beheer = !locked('portfolios');
|
||||
const max = limitFor('portfolios');
|
||||
const eigen = overzicht.portefeuilles.filter(p => !p.demo).length;
|
||||
const full = max !== null && eigen >= max;
|
||||
|
||||
const aangemaakt = p => p.kerncijfers?.aangemaakt || '';
|
||||
const lijst = [...overzicht.portefeuilles]
|
||||
.sort((a, b) => aangemaakt(a).localeCompare(aangemaakt(b)) || a.naam.localeCompare(b.naam, localeTag()));
|
||||
const regels = Math.max(1, ...lijst.map(p => Math.max(
|
||||
soortenVan(p.kerncijfers?.assets, 'asset_kind').length, soortenVan(p.kerncijfers?.accounts, 'account_kind').length)));
|
||||
const nieuwTegel = full
|
||||
? `<div class="pf-new is-full">${counterHtml(counterText('portfolios', eigen))}</div>`
|
||||
: `<div class="pf-new"><button class="btn btn-new pf-new-btn" data-actie="nieuw" title="${esc(t('portfolios.create'))}"
|
||||
aria-label="${esc(t('portfolios.create'))}">${PLUS}</button></div>`;
|
||||
|
||||
const archief = beheer && overzicht.archief.length
|
||||
? `<details class="card pf-card pf-archive" ${expand.attr('archive')}>
|
||||
<summary><span class="chev-btn">${CHEVRON}</span><span>${t('portfolios.archiveTitle')}</span>
|
||||
<span class="badge pf-head-end">${overzicht.archief.length}</span></summary>
|
||||
<div class="pf-archive-list">${overzicht.archief.map(archiefRijHtml).join('')}</div>
|
||||
</details>`
|
||||
: '';
|
||||
|
||||
el.innerHTML = `
|
||||
<div class="pf-grid${beheer ? '' : ' is-stacked'}">
|
||||
${lijst.map(p => kaartHtml(p, { full, beheer, regels })).join('')}
|
||||
${nieuwTegel}
|
||||
</div>
|
||||
${archief ? `<div class="pf-grid pf-grid-archive">${archief}</div>` : ''}`;
|
||||
expand.bind(el);
|
||||
|
||||
const instellingenUitleg = [
|
||||
{ heading: t('portfolios.authTitle') }, t('portfolios.authHint'),
|
||||
{ heading: t('portfolios.startYearTitle') }, t('portfolios.startYearHint'),
|
||||
{ heading: t('portfolios.mwrTitle') },
|
||||
t('portfolios.mwrHint'), t('portfolios.mwrState_dietz'), t('portfolios.mwrState_irr'),
|
||||
];
|
||||
setPageHint(el, {
|
||||
title: t('portfolios.hintTitle'),
|
||||
rows: beheer ? [
|
||||
{ heading: t('portfolios.title') },
|
||||
t('portfolios.intro'), t('portfolios.createHint'), t('portfolios.createCurrencyHint'),
|
||||
{ heading: t('portfolios.backupTitle') }, t('dbfile.hint'), t('portfolios.backupLateHint', { dagen: CONFIG.BACKUP.REMIND_AFTER_DAYS }),
|
||||
...instellingenUitleg,
|
||||
{ heading: t('portfolios.archiveTitle') }, t('portfolios.archiveIntro'),
|
||||
] : [
|
||||
{ heading: t('portfolios.title') }, t('portfolios.intro'), t('portfolios.demoHint'),
|
||||
...instellingenUitleg,
|
||||
],
|
||||
});
|
||||
|
||||
const opnieuw = () => render(el);
|
||||
const fout = (e) => dialog.alert({ title: t('dialog.error'), message: e.message });
|
||||
|
||||
async function doe(actie, naam) {
|
||||
try {
|
||||
switch (actie) {
|
||||
case 'open': await open(naam); break;
|
||||
case 'nieuw': if (await nieuw()) await opnieuw(); break;
|
||||
case 'kopieer': if (await kopieer(naam)) await opnieuw(); break;
|
||||
case 'instellingen': await openInstellingen(naam); break;
|
||||
case 'backup': if (await backupDb()) await opnieuw(); break;
|
||||
case 'exporteer': await exportDb(); await opnieuw(); break;
|
||||
case 'terugzetten': restoreDb(opnieuw); break;
|
||||
case 'leeg': await startFresh(opnieuw); break;
|
||||
case 'inrichten': await openSetup({ rerun: true }); await opnieuw(); break;
|
||||
case 'hernoem': if (await hernoem(naam)) await opnieuw(); break;
|
||||
case 'verwijder': if (await verwijder(naam)) await opnieuw(); break;
|
||||
case 'wis': if (await wis(naam)) await opnieuw(); break;
|
||||
}
|
||||
} catch (e) { fout(e); }
|
||||
}
|
||||
|
||||
function openMenu(knop) {
|
||||
const p = lijst.find(x => x.naam === knop.dataset.menu);
|
||||
if (!p) return;
|
||||
const { el: pop, position } = openPopover(knop, { className: 'popover new-menu' });
|
||||
pop.innerHTML = menuItems(p, { full, beheer }).map(([actie, label]) =>
|
||||
`<button class="btn btn-ghost btn-sm new-menu-item" data-actie="${actie}">${esc(label)}</button>`).join('');
|
||||
position();
|
||||
pop.onclick = (ev) => {
|
||||
const item = ev.target.closest('[data-actie]');
|
||||
if (!item) return;
|
||||
closePopover();
|
||||
doe(item.dataset.actie, p.naam);
|
||||
};
|
||||
}
|
||||
|
||||
el.onclick = (ev) => {
|
||||
const menuKnop = ev.target.closest('[data-menu]');
|
||||
if (menuKnop) { openMenu(menuKnop); return; }
|
||||
const knop = ev.target.closest('[data-actie]');
|
||||
if (knop) doe(knop.dataset.actie, knop.dataset.naam);
|
||||
};
|
||||
}
|
||||
|
||||
async function open(naam) {
|
||||
if (!await dialog.confirm({ message: t('portfolios.activateConfirm', { naam }) })) return;
|
||||
const uit = await switchPortfolio(naam);
|
||||
if (!uit.supervised) {
|
||||
await dialog.alert({ message: t('portfolios.activateNoSupervisor', { naam }) });
|
||||
return;
|
||||
}
|
||||
dialog.notice({ message: t('portfolios.activateWaiting') });
|
||||
await reloadWhenBack();
|
||||
}
|
||||
|
||||
async function nieuw() {
|
||||
const res = await dialog.form({
|
||||
title: t('portfolios.create'),
|
||||
fields: [
|
||||
{ key: 'naam', label: t('portfolios.createName'), placeholder: t('portfolios.createNameHint') },
|
||||
{
|
||||
key: 'munt', label: t('portfolios.createCurrency'), type: 'select',
|
||||
options: CONFIG.PORTFOLIO.BASE_CURRENCIES.map(code => ({ value: code, label: `${currencyName(code)} (${code})` })),
|
||||
value: baseCurrency(),
|
||||
},
|
||||
],
|
||||
confirmLabel: t('portfolios.createConfirm'),
|
||||
});
|
||||
if (!res) return false;
|
||||
const uit = await api('/api/portfolios/create', { naam: res.naam, kopieVan: null, baseCurrency: res.munt });
|
||||
await dialog.alert({ message: t('portfolios.createDone', { naam: uit.naam }) });
|
||||
return true;
|
||||
}
|
||||
|
||||
async function kopieer(bron) {
|
||||
const res = await dialog.form({
|
||||
title: t('portfolios.copyTitle', { naam: bron }),
|
||||
fields: [{ key: 'naam', label: t('portfolios.copyName'), placeholder: t('portfolios.createNameHint') }],
|
||||
confirmLabel: t('portfolios.copyConfirm'),
|
||||
});
|
||||
if (!res) return false;
|
||||
const uit = await api('/api/portfolios/create', { naam: res.naam, kopieVan: bron, baseCurrency: null });
|
||||
await dialog.alert({ message: t('portfolios.copyDone', { naam: uit.naam, bron }) });
|
||||
return true;
|
||||
}
|
||||
|
||||
async function hernoem(naam) {
|
||||
const res = await dialog.form({
|
||||
title: t('portfolios.rename'),
|
||||
fields: [{ key: 'naar', label: t('portfolios.renameTo'), value: naam }],
|
||||
confirmLabel: t('portfolios.rename'),
|
||||
});
|
||||
if (!res) return false;
|
||||
const uit = await api('/api/portfolios/rename', { van: naam, naar: res.naar });
|
||||
await dialog.alert({ message: t('portfolios.renameDone', { naam: uit.naar }) });
|
||||
return true;
|
||||
}
|
||||
|
||||
async function verwijder(naam) {
|
||||
const res = await dialog.form({
|
||||
title: t('portfolios.remove'),
|
||||
fields: [{ key: 'bevestiging', label: t('portfolios.removeConfirm', { naam }) }],
|
||||
confirmLabel: t('portfolios.remove'),
|
||||
});
|
||||
if (!res) return false;
|
||||
if (res.bevestiging.trim() !== naam) {
|
||||
await dialog.alert({ title: t('dialog.error'), message: t('portfolios.removeMismatch') });
|
||||
return false;
|
||||
}
|
||||
await api('/api/portfolios/archive', { naam });
|
||||
await dialog.alert({ message: t('portfolios.removeDone', { naam }) });
|
||||
return true;
|
||||
}
|
||||
|
||||
async function wis(naam) {
|
||||
const res = await dialog.form({
|
||||
title: t('portfolios.purge'),
|
||||
fields: [{ key: 'bevestiging', label: t('portfolios.purgeConfirm', { naam }) }],
|
||||
confirmLabel: t('portfolios.purge'),
|
||||
});
|
||||
if (!res) return false;
|
||||
if (res.bevestiging.trim() !== naam) {
|
||||
await dialog.alert({ title: t('dialog.error'), message: t('portfolios.removeMismatch') });
|
||||
return false;
|
||||
}
|
||||
await api('/api/portfolios/purge', { naam });
|
||||
await dialog.alert({ message: t('portfolios.purgeDone', { naam }) });
|
||||
return true;
|
||||
}
|
||||
|
||||
async function authZetten() {
|
||||
const res = await dialog.form({
|
||||
title: t('portfolios.authChange'),
|
||||
fields: [{ key: 'password', label: t('portfolios.authPassword'), type: 'password' }],
|
||||
confirmLabel: t('portfolios.authSave'),
|
||||
});
|
||||
if (!res) return false;
|
||||
await api('/api/auth/set', { password: res.password });
|
||||
await dialog.alert({ message: t('portfolios.authDone') });
|
||||
return true;
|
||||
}
|
||||
|
||||
async function uitloggen() {
|
||||
if (!await dialog.confirm({ message: t('portfolios.authLogoutConfirm') })) return;
|
||||
await api('/api/logout', {});
|
||||
location.replace(CONFIG.AUTH.LOGIN_PATH);
|
||||
}
|
||||
|
||||
async function authLeegmaken() {
|
||||
if (!await dialog.confirm({ message: t('portfolios.authClearConfirm'), danger: true })) return false;
|
||||
await api('/api/auth/clear', {});
|
||||
await dialog.alert({ message: t('portfolios.authClearedDone') });
|
||||
return true;
|
||||
}
|
||||
Reference in new issue
Block a user