diff --git a/web-app/src/api/account.ts b/web-app/src/api/account.ts index 2dc22c6..aa83918 100644 --- a/web-app/src/api/account.ts +++ b/web-app/src/api/account.ts @@ -3,6 +3,7 @@ import { ApiClient } from './base' import type { Currency } from './data' import type { Page, PageRequest } from './pagination' import { getSelectedProfile } from './profile' +import type { Attachment } from './attachment' export interface AccountType { id: string @@ -87,6 +88,7 @@ export interface AccountValueRecord { type: AccountValueRecordType value: number currency: Currency + attachments: Attachment[] } export interface AccountValueRecordCreationPayload { @@ -103,6 +105,14 @@ export enum AccountHistoryItemType { JOURNAL_ENTRY = 'JOURNAL_ENTRY', } +export function accountHistoryItemTypeToDisplayName(type: AccountHistoryItemType) { + if (type === AccountHistoryItemType.TEXT) return 'Text' + if (type === AccountHistoryItemType.PROPERTY_CHANGE) return 'Property Change' + if (type === AccountHistoryItemType.VALUE_RECORD) return 'Value Record' + if (type === AccountHistoryItemType.JOURNAL_ENTRY) return 'Journal Entry' + return 'Unknown' +} + export interface AccountHistoryItem { timestamp: string type: AccountHistoryItemType diff --git a/web-app/src/api/attachment.ts b/web-app/src/api/attachment.ts index 4e3e4f1..3fd6927 100644 --- a/web-app/src/api/attachment.ts +++ b/web-app/src/api/attachment.ts @@ -2,6 +2,14 @@ import type { RouteLocation } from 'vue-router' import { ApiClient } from './base' import { getSelectedProfile } from './profile' +export interface Attachment { + id: number + uploadedAt: string + filename: string + contentType: string + size: number +} + export class AttachmentApiClient extends ApiClient { readonly profileName: string diff --git a/web-app/src/api/transaction.ts b/web-app/src/api/transaction.ts index 363826b..748d4ff 100644 --- a/web-app/src/api/transaction.ts +++ b/web-app/src/api/transaction.ts @@ -1,3 +1,4 @@ +import type { Attachment } from './attachment' import { ApiClient } from './base' import type { Currency } from './data' import { type Page, type PageRequest } from './pagination' @@ -86,7 +87,7 @@ export interface TransactionDetail { debitedAccount: TransactionDetailAccount | null tags: string[] lineItems: TransactionDetailLineItem[] - attachments: TransactionDetailAttachment[] + attachments: Attachment[] } export interface TransactionDetailAccount { @@ -104,14 +105,6 @@ export interface TransactionDetailLineItem { category: TransactionCategory | null } -export interface TransactionDetailAttachment { - id: number - uploadedAt: string - filename: string - contentType: string - size: number -} - export interface AddTransactionPayload { timestamp: string amount: number diff --git a/web-app/src/assets/main.css b/web-app/src/assets/main.css index 55e615e..8317281 100644 --- a/web-app/src/assets/main.css +++ b/web-app/src/assets/main.css @@ -1,5 +1,6 @@ @import url('@/assets/styles/fonts.css'); @import url('@/assets/styles/text.css'); +@import url('@/assets/styles/spacing.css'); :root { --theme-primary: #113188; @@ -12,6 +13,7 @@ --text: rgb(247, 247, 247); --text-muted: gray; + --text-link: #6c8eff; --positive: rgb(59, 219, 44); --negative: rgb(253, 52, 52); --warning: rgb(255, 187, 0); @@ -26,11 +28,11 @@ body { } a { - color: var(--theme-primary); + color: var(--text-link); text-decoration: none; } a:hover { - color: var(--theme-tertiary); + color: var(--text-tertiary); text-decoration: underline; } diff --git a/web-app/src/assets/styles/spacing.css b/web-app/src/assets/styles/spacing.css new file mode 100644 index 0000000..d99b1c9 --- /dev/null +++ b/web-app/src/assets/styles/spacing.css @@ -0,0 +1,11 @@ +.p0 { + padding: 0; +} + +.m0 { + margin: 0; +} + +.mt-1 { + margin-top: 0.5rem; +} diff --git a/web-app/src/assets/styles/text.css b/web-app/src/assets/styles/text.css index 48695f4..64daa09 100644 --- a/web-app/src/assets/styles/text.css +++ b/web-app/src/assets/styles/text.css @@ -41,3 +41,7 @@ .align-left { text-align: left; } + +.align-center { + text-align: center; +} diff --git a/web-app/src/components/AddValueRecordModal.vue b/web-app/src/components/AddValueRecordModal.vue index 9a94abd..6347cca 100644 --- a/web-app/src/components/AddValueRecordModal.vue +++ b/web-app/src/components/AddValueRecordModal.vue @@ -9,6 +9,7 @@ import { AccountApiClient, AccountValueRecordType, type Account, type AccountVal import { datetimeLocalToISO, getDatetimeLocalValueForNow } from '@/util/time'; import FileSelector from '@/components/common/FileSelector.vue'; import { useRoute } from 'vue-router'; +import { showConfirm } from '@/util/alert'; const route = useRoute() const props = defineProps<{ account: Account }>() @@ -23,7 +24,11 @@ const attachments: Ref = ref([]) async function show(): Promise { if (!modal.value) return Promise.resolve(undefined) timestamp.value = getDatetimeLocalValueForNow() - amount.value = props.account.currentBalance ?? 0 + if (props.account.currentBalance !== null) { + amount.value = props.account.currentBalance / Math.pow(10, props.account.currency.fractionalDigits) + } else { + amount.value = 0 + } savedValueRecord.value = undefined const result = await modal.value.show() if (result === 'saved') { @@ -38,6 +43,11 @@ async function addValueRecord() { type: AccountValueRecordType.BALANCE, value: Math.round(amount.value * Math.pow(10, props.account.currency.fractionalDigits)) } + // Check and confirm with the user if the value they entered doesn't match the expected balance of the account. + if (props.account.currentBalance !== null && payload.value !== props.account.currentBalance) { + const result = await showConfirm("The balance you entered doesn't match the expected current balance for this account. Proceeding to add this value record will result in an incomplete account history and possible reconciliation errors. Are you sure you want to proceed?") + if (!result) return + } const api = new AccountApiClient(route) try { savedValueRecord.value = await api.createValueRecord(props.account.id, payload, attachments.value) diff --git a/web-app/src/components/common/AttachmentRow.vue b/web-app/src/components/common/AttachmentRow.vue new file mode 100644 index 0000000..b716b85 --- /dev/null +++ b/web-app/src/components/common/AttachmentRow.vue @@ -0,0 +1,49 @@ + + + diff --git a/web-app/src/components/common/FileSelector.vue b/web-app/src/components/common/FileSelector.vue index d555886..8ac36fd 100644 --- a/web-app/src/components/common/FileSelector.vue +++ b/web-app/src/components/common/FileSelector.vue @@ -1,9 +1,7 @@ diff --git a/web-app/src/components/history/ValueRecordHistoryItem.vue b/web-app/src/components/history/ValueRecordHistoryItem.vue index da15387..663a563 100644 --- a/web-app/src/components/history/ValueRecordHistoryItem.vue +++ b/web-app/src/components/history/ValueRecordHistoryItem.vue @@ -1,35 +1,26 @@ diff --git a/web-app/src/pages/AccountPage.vue b/web-app/src/pages/AccountPage.vue index 84033d6..a467d15 100644 --- a/web-app/src/pages/AccountPage.vue +++ b/web-app/src/pages/AccountPage.vue @@ -16,6 +16,7 @@ const route = useRoute() const router = useRouter() const addValueRecordModal = useTemplateRef("addValueRecordModal") +const history = useTemplateRef('history') const account: Ref = ref(null) onMounted(async () => { @@ -45,7 +46,7 @@ async function deleteAccount() { async function addValueRecord() { const result = await addValueRecordModal.value?.show() if (result) { - console.info('Value record added', result) + history.value?.reload() } } @@ -97,7 +98,7 @@ async function addValueRecord() { Delete - + diff --git a/web-app/src/pages/TransactionPage.vue b/web-app/src/pages/TransactionPage.vue index 33e24b0..686527c 100644 --- a/web-app/src/pages/TransactionPage.vue +++ b/web-app/src/pages/TransactionPage.vue @@ -11,6 +11,7 @@ import TagLabel from '@/components/TagLabel.vue'; import { showAlert, showConfirm } from '@/util/alert'; import { onMounted, ref, type Ref } from 'vue'; import { useRoute, useRouter } from 'vue-router'; +import AttachmentRow from '@/components/common/AttachmentRow.vue'; const route = useRoute() const router = useRouter() @@ -117,6 +118,11 @@ async function deleteTransaction() { + +
+

Attachments

+ +
diff --git a/web-app/src/pages/ValueRecordPage.vue b/web-app/src/pages/ValueRecordPage.vue new file mode 100644 index 0000000..181ddfe --- /dev/null +++ b/web-app/src/pages/ValueRecordPage.vue @@ -0,0 +1,69 @@ + + diff --git a/web-app/src/pages/home/AccountsModule.vue b/web-app/src/pages/home/AccountsModule.vue index 190c4f2..de68294 100644 --- a/web-app/src/pages/home/AccountsModule.vue +++ b/web-app/src/pages/home/AccountsModule.vue @@ -3,6 +3,7 @@ import { AccountApiClient, type Account, type CurrencyBalance } from '@/api/acco import { formatMoney } from '@/api/data' import { getSelectedProfile } from '@/api/profile' import AccountCard from '@/components/AccountCard.vue' +import AppBadge from '@/components/common/AppBadge.vue' import AppButton from '@/components/common/AppButton.vue' import HomeModule from '@/components/HomeModule.vue' import { onMounted, ref, type Ref } from 'vue' @@ -33,13 +34,9 @@ onMounted(async () => {

-
    -
  • - Total in {{ bal.currency.code }} - = - {{ formatMoney(bal.balance, bal.currency) }} -
  • -
+ + Total {{ bal.currency.code }}: {{ formatMoney(bal.balance, bal.currency) }} +