Files
AtreidesAssetsFree/server/data/onboarding.js
T

83 lines
3.7 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:329990ff910102e84cb67048624a913db4948fa6ef673de3eb40c65c633864ba
import * as db from '../db.js';
import { ASSET_DEFINITIONS, DASHBOARD } from '../../shared/config.js';
import { toSearchName } from '../../shared/search-name.js';
import { getPref, setPref } from './preferences.js';
import { createAsset } from './assets.js';
import { createAccount } from './accounts.js';
import { activeProfile, addWidget, listWidgets } from './dashboard.js';
import { currentCount, capacityReachedFor } from './_limits.js';
export function setupStatus() {
const done = Number(getPref('app', 'setup_done')) === 1;
const empty = currentCount('assets') === 0;
const own = db.one(`SELECT (SELECT COUNT(*) FROM Account) + (SELECT COUNT(*) FROM Event)
+ (SELECT COUNT(*) FROM Valuation) AS n`).n;
return { needed: !done && empty, done, empty, persona: getPref('app', 'persona'),
currencyOpen: !done && empty && own === 0 };
}
function assertKnownAsset(symbol) {
if (!ASSET_DEFINITIONS[symbol]) {
throw new Error(`Unknown asset '${symbol}'. Allowed: ${Object.keys(ASSET_DEFINITIONS).join(', ')}.`);
}
}
async function ensureAsset(symbol) {
const def = ASSET_DEFINITIONS[symbol];
const existing = db.one(`SELECT id FROM Asset WHERE symbol = ?`, [def.symbol]);
if (existing) return existing.id;
return capacityReachedFor('assets') === null ? createAsset(def) : null;
}
async function ensureAccount({ name, kind }) {
const existing = db.one(`SELECT id FROM Account WHERE searchName = ?`, [toSearchName(name)]);
if (existing) return existing.id;
if (kind === 'bank' && capacityReachedFor('fiatAccounts') !== null) return null;
const own = kind === 'wallet' || kind === 'vault';
return createAccount({ name, kind, isOwnCustody: own, scanEnabled: false, taxCategory: null });
}
function placeInFreeSpace(rows, items) {
const cols = DASHBOARD.GRID_COLUMNS;
const heightOf = (type, h) => (h > 0 ? h : DASHBOARD.WIDGETS[type]?.defaultH || 1);
const bottom = new Array(cols).fill(0);
for (const r of rows) {
const end = r.gridY + heightOf(r.type, r.spanHeight);
for (let c = r.gridX; c < Math.min(cols, r.gridX + r.spanWidth); c++) bottom[c] = Math.max(bottom[c], end);
}
return items.map(({ prefer, ...w }) => {
const span = DASHBOARD.WIDGETS[w.type].defaultSpan;
let bestX = 0, bestY = Infinity;
for (let x = 0; x + span <= cols; x++) {
const y = Math.max(...bottom.slice(x, x + span));
if (y < bestY || (y === bestY && x === prefer)) { bestY = y; bestX = x; }
}
for (let c = bestX; c < bestX + span; c++) bottom[c] = bestY + heightOf(w.type, null);
return { ...w, gridX: bestX, gridY: bestY };
});
}
export async function completeSetup({ assets = [], persona = 'broad', accounts = [], widgets = [] } = {}) {
assets.forEach(assertKnownAsset);
await setPref('app', 'persona', persona);
const assetIds = [];
for (const s of assets) {
const id = await ensureAsset(s);
if (id != null) assetIds.push(id);
}
let accountCount = 0;
for (const a of accounts) if ((await ensureAccount(a)) != null) accountCount++;
const profileId = activeProfile().id;
const add = [];
if (widgets.includes('standing')) add.push({ type: 'standing', prefer: 4 });
if (widgets.includes('signal') && persona === 'bitcoin') add.push({ type: 'signal', prefer: 4 });
if (widgets.includes('spot')) assetIds.forEach(id => add.push({ type: 'spot', instanceKey: `spot-${id}`, assetId: id, prefer: 0, settings: { momentum: false } }));
let widgetCount = 0;
for (const w of placeInFreeSpace(listWidgets(profileId), add)) if (await addWidget(profileId, w)) widgetCount++;
await setPref('app', 'setup_done', 1);
return { persona, assetIds, accounts: accountCount, widgets: widgetCount };
}