diff --git a/character-builder/frontend/src/App.vue b/character-builder/frontend/src/App.vue index 1fc43f6..7cc1e5e 100644 --- a/character-builder/frontend/src/App.vue +++ b/character-builder/frontend/src/App.vue @@ -65,27 +65,46 @@ watch( { immediate: true }, ); +const bootError = ref(''); + +async function safeLoad(label: string, fn: () => Promise): Promise { + try { + return await fn(); + } catch (e: any) { + const msg = `[${label}] ${e?.message || e}`; + // eslint-disable-next-line no-console + console.error(msg, e); + if (!bootError.value) bootError.value = msg; + return null; + } +} + onMounted(async () => { - await Promise.all([ - loadCharacterXp().then((d) => (charXp.value = d)), + // Each load is isolated — a single failed fetch can't bring down the rest + // of the page (or leave us looking at a blank background). + await Promise.allSettled([ + safeLoad('character-xp', loadCharacterXp).then( + (d) => d && (charXp.value = d), + ), ...SPECS.map((s) => - loadSpec(s).then((d) => (specTables.value = { ...specTables.value, [s]: d })), + safeLoad(`spec-${s}`, () => loadSpec(s)).then( + (d) => d && (specTables.value = { ...specTables.value, [s]: d }), + ), ), - loadFaction('atreides').then( - (d) => (factionTables.value = { ...factionTables.value, atreides: d }), + safeLoad('faction-atreides', () => loadFaction('atreides')).then( + (d) => d && (factionTables.value = { ...factionTables.value, atreides: d }), ), - loadFaction('harkonnen').then( - (d) => (factionTables.value = { ...factionTables.value, harkonnen: d }), + safeLoad('faction-harkonnen', () => loadFaction('harkonnen')).then( + (d) => d && (factionTables.value = { ...factionTables.value, harkonnen: d }), ), ]); - // Load all skill trees in background so class switching is instant + for (const c of CLASSES) { - loadSkillTree(c.id).then( - (d) => (skillTrees.value = { ...skillTrees.value, [c.id]: d }), + safeLoad(`skills-${c.id}`, () => loadSkillTree(c.id)).then( + (d) => d && (skillTrees.value = { ...skillTrees.value, [c.id]: d }), ); } - // If URL has ?b=, load that build. const params = new URLSearchParams(window.location.search); const code = params.get('b'); if (code) { @@ -236,6 +255,12 @@ const specMeta: Record = {
{{ shareStatus }} + Boot warning: {{ bootError }}