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

View File

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

View File

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

View File

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