CI: add npm build workflow; fix shop field mismatch (server sends 'value', client used nonexistent 'buy_price') and strict-mode TS errors
All checks were successful
build / build-test (push) Successful in 34s

This commit is contained in:
Jan-Peter von Hunnius 2026-09-01 06:25:03 +02:00
parent 5142f18b1b
commit 7078e5dc54
2 changed files with 27 additions and 7 deletions

View file

@ -0,0 +1,20 @@
name: build
on: [push, workflow_dispatch]
jobs:
build-test:
runs-on: docker
container: node:20-bookworm
timeout-minutes: 15
steps:
- name: Checkout
run: git clone --depth 1 ${{ github.server_url }}/${{ github.repository }}.git .
- name: Install
run: npm ci || npm install
- name: Build
run: npm run build
- name: Test
run: npm test --if-present

View file

@ -16,7 +16,7 @@ interface Character {
} }
interface ShopItem { interface ShopItem {
id: string; name: string; type: string; quality: string; slot: string id: string; name: string; type: string; quality: string; slot: string
stats: ItemStat; attack_type?: string; price: number stats: ItemStat; attack_type?: string; value: number
} }
interface ItemStat { [key: string]: number } interface ItemStat { [key: string]: number }
interface Item { interface Item {
@ -149,8 +149,8 @@ function xpForLevel(level: number): number {
const MAX_LEVEL = 100 const MAX_LEVEL = 100
function nextLevel() { function nextLevel(): number {
if (!selectedChar.value) return if (!selectedChar.value) return 1
const current = selectedChar.value.level const current = selectedChar.value.level
return current + 1 return current + 1
} }
@ -452,7 +452,7 @@ async function sellItem(itemId: string) {
body: JSON.stringify({ action: 'sell', item_id: itemId }), body: JSON.stringify({ action: 'sell', item_id: itemId }),
}) })
if (!res.ok) { error.value = (await res.text()) || 'Cannot sell'; selling.value = null; return } if (!res.ok) { error.value = (await res.text()) || 'Cannot sell'; selling.value = null; return }
const result = await res.json() await res.json()
const charsRes = await apiFetch(`/api/users/${selectedUser.value!.id}/characters`) const charsRes = await apiFetch(`/api/users/${selectedUser.value!.id}/characters`)
characters.value = await charsRes.json() characters.value = await charsRes.json()
selectedChar.value = characters.value.find(c => c.id === selectedChar.value!.id) ?? null selectedChar.value = characters.value.find(c => c.id === selectedChar.value!.id) ?? null
@ -644,7 +644,7 @@ async function activateSpell(spellId: string | null) {
<div v-else-if="selectedChar" class="home-summary"> <div v-else-if="selectedChar" class="home-summary">
<div class="summary-header"> <div class="summary-header">
<h2>{{ selectedChar.name }}</h2> <h2>{{ selectedChar.name }}</h2>
<span class="summary-sub">{{ races.find(r => r.race === selectedChar.race)?.name }} {{ classes.find(c => c.class === selectedChar.class)?.name }}</span> <span class="summary-sub">{{ races.find(r => r.race === selectedChar?.race)?.name }} {{ classes.find(c => c.class === selectedChar?.class)?.name }}</span>
<span class="summary-sub">Level {{ selectedChar.level }} &middot; {{ selectedChar.xp }} XP &middot; <span class="gold-text">{{ selectedChar.gold }} Gold</span></span> <span class="summary-sub">Level {{ selectedChar.level }} &middot; {{ selectedChar.xp }} XP &middot; <span class="gold-text">{{ selectedChar.gold }} Gold</span></span>
</div> </div>
<div class="summary-stats"> <div class="summary-stats">
@ -733,8 +733,8 @@ async function activateSpell(spellId: string | null) {
<div class="item-meta"><span class="item-type">{{ item.type }}</span><span class="item-slot">{{ item.slot.replace('_',' ') }}</span><span v-if="item.attack_type" class="item-atype">{{ item.attack_type }}</span></div> <div class="item-meta"><span class="item-type">{{ item.type }}</span><span class="item-slot">{{ item.slot.replace('_',' ') }}</span><span v-if="item.attack_type" class="item-atype">{{ item.attack_type }}</span></div>
<div class="item-stats"><span v-for="(val, stat) in item.stats" :key="stat" class="item-stat">+{{ val }} {{ statLabel(stat) }}</span></div> <div class="item-stats"><span v-for="(val, stat) in item.stats" :key="stat" class="item-stat">+{{ val }} {{ statLabel(stat) }}</span></div>
<div class="shop-buy-row"> <div class="shop-buy-row">
<span class="shop-price">{{ item.buy_price }} Gold</span> <span class="shop-price">{{ item.value }} Gold</span>
<button class="btn-buy" :disabled="buying === item.id || (shopInfo?.gold ?? 0) < item.buy_price" @click="buyItem(item.id)"> <button class="btn-buy" :disabled="buying === item.id || (shopInfo?.gold ?? 0) < item.value" @click="buyItem(item.id)">
{{ buying === item.id ? 'Buying...' : 'Buy' }} {{ buying === item.id ? 'Buying...' : 'Buy' }}
</button> </button>
</div> </div>