Clean up account page, fix onCancel for EditAccountPage.
Build and Deploy Web App / build-and-deploy (push) Successful in 20s Details

This commit is contained in:
andrewlalis 2025-09-24 21:44:34 -04:00
parent 9763fd7abe
commit c1193d96fd
4 changed files with 58 additions and 117 deletions

View File

@ -9,6 +9,7 @@ export interface AccountType {
id: string
name: string
debitsPositive: boolean
emoji: string
}
export abstract class AccountTypes {
@ -16,21 +17,25 @@ export abstract class AccountTypes {
id: 'CHECKING',
name: 'Checking',
debitsPositive: true,
emoji: '💵',
}
public static readonly SAVINGS: AccountType = {
id: 'SAVINGS',
name: 'Savings',
debitsPositive: true,
emoji: '💰',
}
public static readonly CREDIT_CARD: AccountType = {
id: 'CREDIT_CARD',
name: 'Credit Card',
debitsPositive: false,
emoji: '💳',
}
public static readonly BROKERAGE: AccountType = {
id: 'BROKERAGE',
name: 'Brokerage',
debitsPositive: true,
emoji: '📈',
}
public static of(id: string): AccountType {

View File

@ -34,28 +34,18 @@ function goToAccount() {
}
</script>
<template>
<div
class="account-card"
@click="goToAccount()"
>
<div class="account-card" @click="goToAccount()">
<!-- A top row for the name on the left, and balance on the right. -->
<div class="account-card-top-row">
<div>
<span
class="font-bold"
style="margin-right: 0.5rem"
>{{ account.name }}</span
>
<span class="font-bold" style="margin-right: 0.5rem">{{ account.name }}</span>
<span class="font-mono font-size-xsmall">#{{ account.numberSuffix }}</span>
</div>
<div
class="font-mono font-size-small"
:class="{
<div class="font-mono font-size-small" :class="{
'text-positive': isBalancePositive,
'text-negative': isBalanceNegative,
}"
>
}">
<span v-if="account.currentBalance !== null">
{{ formatMoney(account.currentBalance, account.currency) }}
</span>
@ -65,7 +55,7 @@ function goToAccount() {
</div>
<!-- A bottom row for other attributes. -->
<div>
<AppBadge size="sm">{{ accountType.name }}</AppBadge>
<AppBadge size="sm">{{ accountType.emoji }} {{ accountType.name }}</AppBadge>
</div>
</div>
</template>

View File

@ -1,15 +1,16 @@
<script setup lang="ts">
import { AccountApiClient, type Account } from '@/api/account'
import { AccountApiClient, AccountTypes, type Account } from '@/api/account'
import { formatMoney } from '@/api/data'
import { getSelectedProfile } from '@/api/profile'
import AddValueRecordModal from '@/components/AddValueRecordModal.vue'
import AppBadge from '@/components/common/AppBadge.vue'
import AppButton from '@/components/common/AppButton.vue'
import AppPage from '@/components/common/AppPage.vue'
import ButtonBar from '@/components/common/ButtonBar.vue'
import AccountHistory from '@/components/history/AccountHistory.vue'
import PropertiesTable from '@/components/PropertiesTable.vue'
import { showConfirm } from '@/util/alert'
import { onMounted, ref, useTemplateRef, type Ref } from 'vue'
import { computed, onMounted, ref, useTemplateRef, type Ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
@ -18,6 +19,10 @@ const router = useRouter()
const addValueRecordModal = useTemplateRef('addValueRecordModal')
const history = useTemplateRef('history')
const account: Ref<Account | null> = ref(null)
const accountType = computed(() => {
if (account.value === null) return null
return AccountTypes.of(account.value.type)
})
onMounted(async () => {
const accountId = parseInt(route.params.id as string)
@ -55,70 +60,36 @@ async function addValueRecord() {
}
</script>
<template>
<AppPage :title="account?.name ?? ''">
<PropertiesTable v-if="account">
<tr>
<th>ID</th>
<td>{{ account.id }}</td>
</tr>
<AppPage :title="account?.name ?? ''" v-if="account">
<div>
<AppBadge size="lg" v-if="account.currentBalance !== null" class="font-mono">
{{ account.currency.code }} {{ formatMoney(account.currentBalance, account.currency) }}
</AppBadge>
<AppBadge size="lg" class="font-mono">#{{ account.numberSuffix }}</AppBadge>
<AppBadge size="lg" v-if="accountType" class="font-mono">
{{ accountType.emoji }} {{ accountType.name }}
</AppBadge>
</div>
<p>{{ account.description }}</p>
<PropertiesTable>
<tr>
<th>Created at</th>
<td>{{ new Date(account.createdAt).toLocaleString() }}</td>
</tr>
<tr>
<th>Archived</th>
<td>{{ account.archived }}</td>
</tr>
<tr>
<th>Type</th>
<td>{{ account.type }}</td>
</tr>
<tr>
<th>Name</th>
<td>{{ account.name }}</td>
</tr>
<tr>
<th>Number Suffix</th>
<td>{{ account.numberSuffix }}</td>
</tr>
<tr>
<th>Currency</th>
<td>{{ account.currency.code }}</td>
</tr>
<tr>
<th>Description</th>
<td>{{ account.description }}</td>
</tr>
<tr v-if="account.currentBalance !== null">
<th>Current Balance</th>
<td>{{ formatMoney(account.currentBalance, account.currency) }}</td>
</tr>
</PropertiesTable>
<ButtonBar>
<AppButton @click="addValueRecord()">Record Value</AppButton>
<AppButton
icon="wrench"
@click="router.push(`/profiles/${getSelectedProfile(route)}/accounts/${account?.id}/edit`)"
>
Edit</AppButton
>
<AppButton
icon="trash"
@click="deleteAccount()"
>Delete</AppButton
>
<AppButton @click="addValueRecord()" :disabled="account.archived">Record Value</AppButton>
<AppButton icon="wrench" :disabled="account.archived"
@click="router.push(`/profiles/${getSelectedProfile(route)}/accounts/${account?.id}/edit`)">
Edit</AppButton>
<AppButton icon="trash" @click="deleteAccount()" :disabled="account.archived">Delete</AppButton>
</ButtonBar>
<AccountHistory
:account="account"
v-if="account"
ref="history"
/>
<AccountHistory :account="account" v-if="account" ref="history" />
<AddValueRecordModal
v-if="account"
:account="account"
ref="addValueRecordModal"
/>
<AddValueRecordModal v-if="account" :account="account" ref="addValueRecordModal" />
</AppPage>
</template>

View File

@ -67,27 +67,24 @@ async function doSubmit() {
loading.value = false
}
}
function onCancel() {
if (editing.value) {
router.replace(`/profiles/${getSelectedProfile(route)}/accounts/${existingAccount.value?.id}`)
} else {
router.replace(`/profiles/${getSelectedProfile(route)}`)
}
}
</script>
<template>
<AppPage :title="editing ? 'Edit Account' : 'Add Account'">
<AppForm @submit="doSubmit()">
<FormGroup>
<FormControl
label="Account Name"
style="max-width: 200px"
>
<input
v-model="accountName"
:disabled="loading"
/>
<FormControl label="Account Name" style="max-width: 200px">
<input v-model="accountName" :disabled="loading" />
</FormControl>
<FormControl label="Account Type">
<select
id="account-type-select"
v-model="accountType"
:disabled="loading"
required
>
<select id="account-type-select" v-model="accountType" :disabled="loading" required>
<option :value="AccountTypes.CHECKING">{{ AccountTypes.CHECKING.name }}</option>
<option :value="AccountTypes.SAVINGS">{{ AccountTypes.SAVINGS.name }}</option>
<option :value="AccountTypes.CREDIT_CARD">{{ AccountTypes.CREDIT_CARD.name }}</option>
@ -95,46 +92,24 @@ async function doSubmit() {
</select>
</FormControl>
<FormControl label="Currency">
<select
id="currency-select"
v-model="currency"
:disabled="loading"
required
>
<select id="currency-select" v-model="currency" :disabled="loading" required>
<option value="USD">USD</option>
<option value="EUR">EUR</option>
<option value="GBP">GBP</option>
</select>
</FormControl>
<FormControl
label="Account Number Suffix"
style="max-width: 200px"
>
<input
id="account-number-suffix-input"
v-model="accountNumberSuffix"
minlength="4"
maxlength="4"
:disabled="loading"
required
/>
<FormControl label="Account Number Suffix" style="max-width: 200px">
<input id="account-number-suffix-input" v-model="accountNumberSuffix" minlength="4" maxlength="4"
:disabled="loading" required />
</FormControl>
</FormGroup>
<FormGroup>
<FormControl label="Description">
<textarea
id="description-textarea"
v-model="description"
:disabled="loading"
></textarea>
<textarea id="description-textarea" v-model="description" :disabled="loading"></textarea>
</FormControl>
</FormGroup>
<FormActions
@cancel="router.replace(`/profiles/${getSelectedProfile(route)}`)"
:disabled="loading"
:submit-text="editing ? 'Save' : 'Add'"
/>
<FormActions @cancel="onCancel" :disabled="loading" :submit-text="editing ? 'Save' : 'Add'" />
</AppForm>
</AppPage>
</template>