forked from Kispi/Core
feat(webapp): add backend to bank view
This commit is contained in:
parent
069da4fc5e
commit
4915be99b8
6 changed files with 130 additions and 90 deletions
|
@ -11,7 +11,7 @@
|
||||||
<td>
|
<td>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div>
|
<div>
|
||||||
<div class="font-bold">{{ transaction.type }}</div>
|
<div class="font-bold">{{ transaction.label }}</div>
|
||||||
<div>{{ DateService.toString(transaction.date) }}</div>
|
<div>{{ DateService.toString(transaction.date) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
54
src/components/services/bank.service.ts
Normal file
54
src/components/services/bank.service.ts
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import { AppwriteService } from './appwrite.service';
|
||||||
|
import { IAccount } from '../../interfaces/account.interface';
|
||||||
|
import { Databases, Models, Query } from 'appwrite';
|
||||||
|
import { ITransaction } from '../../interfaces/transaction.interface';
|
||||||
|
|
||||||
|
const DATABASE_ID = '62dfd5787755e7ed9fcd';
|
||||||
|
const ACCOUNTS_COLLECTION_ID = '62dfd6ca06197f9baa86';
|
||||||
|
const TRANSACTIONS_COLLECTION_ID = '62dfd81b7671727b27cd';
|
||||||
|
const DEPOSIT_LABEL = 'Bargeldeinzahlung';
|
||||||
|
const SALARY_LABEL = 'Gehalt';
|
||||||
|
const WITHDRAW_LABEL = 'Bargeldauszahlung';
|
||||||
|
const sdk = AppwriteService.getSDK();
|
||||||
|
|
||||||
|
export const BankService = {
|
||||||
|
async getAccountDetails(accountNumber: string): Promise<IAccount> {
|
||||||
|
const database = new Databases(sdk, DATABASE_ID);
|
||||||
|
const accountDocument = await database.listDocuments<IAccount & Models.Document>(
|
||||||
|
ACCOUNTS_COLLECTION_ID, [Query.equal('accountNumber', accountNumber)], 1);
|
||||||
|
const transactionDocuments = await database.listDocuments<ITransaction & Models.Document>(
|
||||||
|
TRANSACTIONS_COLLECTION_ID, [Query.equal('accountNumber', accountNumber)], 100, 0, undefined, undefined, ['$createdAt'], ['DESC']);
|
||||||
|
const account: IAccount = {
|
||||||
|
accountNumber: accountDocument.documents[0].accountNumber,
|
||||||
|
firstName: accountDocument.documents[0].firstName,
|
||||||
|
lastName: accountDocument.documents[0].lastName,
|
||||||
|
balance: accountDocument.documents[0].balance,
|
||||||
|
transactions: transactionDocuments.documents.map(transaction => ({
|
||||||
|
label: transaction.label,
|
||||||
|
amount: transaction.amount,
|
||||||
|
date: new Date(transaction.$createdAt * 1000),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
return account;
|
||||||
|
},
|
||||||
|
async addTransaction(accountNumber: string, amount: number, type: TransactionType): Promise<ITransaction> {
|
||||||
|
const database = new Databases(sdk, DATABASE_ID);
|
||||||
|
const accountDocument = await database.listDocuments<IAccount & Models.Document>(
|
||||||
|
ACCOUNTS_COLLECTION_ID, [Query.equal('accountNumber', accountNumber)], 1);
|
||||||
|
const transactionDocument = await database.createDocument<ITransaction & Models.Document>(TRANSACTIONS_COLLECTION_ID, 'unique()', {
|
||||||
|
accountNumber: accountNumber,
|
||||||
|
label: type === TransactionType.DEPOSIT ? DEPOSIT_LABEL : (type === TransactionType.SALARY ? SALARY_LABEL : WITHDRAW_LABEL),
|
||||||
|
amount: (type === TransactionType.WITHDRAW ? -amount : amount),
|
||||||
|
});
|
||||||
|
await database.updateDocument<IAccount & Models.Document>(ACCOUNTS_COLLECTION_ID, accountDocument.documents[0].$id, {
|
||||||
|
balance: accountDocument.documents[0].balance + (type === TransactionType.WITHDRAW ? -amount : amount),
|
||||||
|
});
|
||||||
|
return { label: transactionDocument.label, amount: transactionDocument.amount, date: new Date(transactionDocument.$createdAt * 1000) };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum TransactionType {
|
||||||
|
DEPOSIT,
|
||||||
|
SALARY,
|
||||||
|
WITHDRAW,
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ export class DateService {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="hero-content flex-col text-center lg:p-6 pt-0">
|
<div
|
||||||
<h1 class="text-8xl mt-5">{{ CurrencyService.toString(balance) }}</h1>
|
v-if="accountDetails"
|
||||||
<h3 class="text-5xl font-bold mt-10">Kontoinhaber: John Doe</h3>
|
class="hero-content flex-col text-center lg:p-6 pt-0"
|
||||||
<h4 class="text-4xl mb-10">Kontonummer: 0000123456</h4>
|
>
|
||||||
|
<h1 class="text-8xl mt-5">{{ CurrencyService.toString(accountDetails.balance) }}</h1>
|
||||||
|
<h3 class="text-5xl font-bold mt-10">Kontoinhaber: {{ `${accountDetails.firstName} ${accountDetails.lastName}` }}</h3>
|
||||||
|
<h4 class="text-4xl mb-10">Kontonummer: {{ accountDetails.accountNumber }}</h4>
|
||||||
<MoleculeTransactionTable
|
<MoleculeTransactionTable
|
||||||
class="w-[42rem]"
|
class="w-[42rem] mb-24"
|
||||||
:balance="balance"
|
:balance="accountDetails.balance"
|
||||||
:transactions="transactions"
|
:transactions="accountDetails.transactions"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="sticky flex place-content-center lg:gap-32 gap-16 bg-base-100 bottom-0 w-100 p-5">
|
<div class="fixed flex place-content-center lg:gap-32 gap-16 bg-base-100 bottom-0 lg:w-[calc(100%-16rem)] w-full p-5">
|
||||||
<MoleculeInputModal
|
<MoleculeInputModal
|
||||||
:id="depositModalId"
|
:id="depositModalId"
|
||||||
title="Einzahlung"
|
title="Einzahlung"
|
||||||
|
@ -18,6 +21,7 @@
|
||||||
@submit="handleDeposit"
|
@submit="handleDeposit"
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
|
ref="depositModalLabel"
|
||||||
class="btn gap-2"
|
class="btn gap-2"
|
||||||
:for="depositModalId"
|
:for="depositModalId"
|
||||||
>
|
>
|
||||||
|
@ -32,6 +36,7 @@
|
||||||
@submit="handleSalary"
|
@submit="handleSalary"
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
|
ref="salaryModalLabel"
|
||||||
class="btn gap-2"
|
class="btn gap-2"
|
||||||
:for="salaryModalId"
|
:for="salaryModalId"
|
||||||
>
|
>
|
||||||
|
@ -46,6 +51,7 @@
|
||||||
@submit="handleWithdraw"
|
@submit="handleWithdraw"
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
|
ref="withdrawModalLabel"
|
||||||
class="btn gap-2"
|
class="btn gap-2"
|
||||||
:for="withdrawModalId"
|
:for="withdrawModalId"
|
||||||
>
|
>
|
||||||
|
@ -62,97 +68,67 @@ import { CurrencyDollarIcon, ChevronDownIcon, ChevronUpIcon } from '@heroicons/v
|
||||||
import MoleculeTransactionTable from '../molecules/MoleculeTransactionTable.vue';
|
import MoleculeTransactionTable from '../molecules/MoleculeTransactionTable.vue';
|
||||||
import MoleculeInputModal from '../molecules/MoleculeInputModal.vue';
|
import MoleculeInputModal from '../molecules/MoleculeInputModal.vue';
|
||||||
import { onKeyStroke } from '@vueuse/core';
|
import { onKeyStroke } from '@vueuse/core';
|
||||||
|
import { BankService, TransactionType } from '../services/bank.service';
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { IAccount } from '../../interfaces/account.interface';
|
||||||
|
|
||||||
|
const DEMO_ACCOUNT_ID = '0000123456';
|
||||||
|
|
||||||
|
const accountDetails = ref<IAccount>();
|
||||||
|
const depositModalLabel = ref<HTMLLabelElement>();
|
||||||
|
const salaryModalLabel = ref<HTMLLabelElement>();
|
||||||
|
const withdrawModalLabel = ref<HTMLLabelElement>();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
// TODO: Remove this demo account id
|
||||||
|
accountDetails.value = await BankService.getAccountDetails(DEMO_ACCOUNT_ID);
|
||||||
|
});
|
||||||
|
|
||||||
const depositModalId = 'deposit-modal';
|
const depositModalId = 'deposit-modal';
|
||||||
const salaryModalId = 'salary-modal';
|
const salaryModalId = 'salary-modal';
|
||||||
const withdrawModalId = 'withdraw-modal';
|
const withdrawModalId = 'withdraw-modal';
|
||||||
const balance = 135;
|
|
||||||
const transactions: ITransaction[] = [
|
|
||||||
{
|
|
||||||
type: 'Kontoeröffnung',
|
|
||||||
date: new Date(2022, 7, 12, 17, 30, 0),
|
|
||||||
amount: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Bargeldeinzahlung',
|
|
||||||
date: new Date(2022, 7, 12, 17, 30, 10),
|
|
||||||
amount: 100,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Gehalt',
|
|
||||||
date: new Date(2022, 7, 12, 17, 34, 10),
|
|
||||||
amount: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Gehalt',
|
|
||||||
date: new Date(2022, 7, 12, 17, 34, 10),
|
|
||||||
amount: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Gehalt',
|
|
||||||
date: new Date(2022, 7, 12, 17, 34, 10),
|
|
||||||
amount: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Gehalt',
|
|
||||||
date: new Date(2022, 7, 12, 17, 34, 10),
|
|
||||||
amount: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Gehalt',
|
|
||||||
date: new Date(2022, 7, 12, 17, 34, 10),
|
|
||||||
amount: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Gehalt',
|
|
||||||
date: new Date(2022, 7, 12, 17, 34, 10),
|
|
||||||
amount: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Gehalt',
|
|
||||||
date: new Date(2022, 7, 12, 17, 34, 10),
|
|
||||||
amount: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Gehalt',
|
|
||||||
date: new Date(2022, 7, 12, 17, 34, 10),
|
|
||||||
amount: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Gehalt',
|
|
||||||
date: new Date(2022, 7, 12, 17, 34, 10),
|
|
||||||
amount: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Gehalt',
|
|
||||||
date: new Date(2022, 7, 12, 17, 34, 10),
|
|
||||||
amount: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Bargeldauszahlung',
|
|
||||||
date: new Date(2022, 7, 15, 18, 26, 0),
|
|
||||||
amount: -20,
|
|
||||||
},
|
|
||||||
].sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
||||||
|
|
||||||
|
onKeyStroke(['e', 'g', 'a'], (e) => {
|
||||||
|
|
||||||
onKeyStroke('e', (e) => {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log('e pressed');
|
switch (e.key) {
|
||||||
// TODO: open deposit modal
|
case 'e':
|
||||||
|
depositModalLabel.value?.click();
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
salaryModalLabel.value?.click();
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
withdrawModalLabel.value?.click();
|
||||||
|
break;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleDeposit(amount: number) {
|
async function handleDeposit(amount: string) {
|
||||||
console.log('Deposit:', amount);
|
const amountNumber = parseInt(amount);
|
||||||
|
if(!isNaN(amountNumber)) {
|
||||||
|
updateTransactions(await BankService.addTransaction(DEMO_ACCOUNT_ID, amountNumber, TransactionType.DEPOSIT));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSalary(amount: number) {
|
async function handleSalary(amount: string) {
|
||||||
console.log('Salary:', amount);
|
const amountNumber = parseInt(amount);
|
||||||
|
if(!isNaN(amountNumber)) {
|
||||||
|
updateTransactions(await BankService.addTransaction(DEMO_ACCOUNT_ID, amountNumber, TransactionType.SALARY));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleWithdraw(amount: number) {
|
async function handleWithdraw(amount: string) {
|
||||||
console.log('Withdraw:', amount);
|
const amountNumber = parseInt(amount);
|
||||||
|
if(!isNaN(amountNumber)) {
|
||||||
|
updateTransactions(await BankService.addTransaction(DEMO_ACCOUNT_ID, amountNumber, TransactionType.WITHDRAW));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTransactions(transaction: ITransaction) {
|
||||||
|
if(accountDetails.value) {
|
||||||
|
accountDetails.value.balance += transaction.amount;
|
||||||
|
accountDetails.value.transactions.unshift(transaction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
9
src/interfaces/account.interface.ts
Normal file
9
src/interfaces/account.interface.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { ITransaction } from './transaction.interface';
|
||||||
|
|
||||||
|
export interface IAccount {
|
||||||
|
accountNumber: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
balance: number;
|
||||||
|
transactions: ITransaction[];
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
export interface ITransaction {
|
export interface ITransaction {
|
||||||
type: string;
|
label: string;
|
||||||
date: Date;
|
|
||||||
amount: number;
|
amount: number;
|
||||||
|
date: Date;
|
||||||
}
|
}
|
Loading…
Reference in a new issue