mirror of
https://github.com/dancojocaru2000/foxbank.git
synced 2025-02-22 23:39:36 +02:00
Finished frontend implementation with api
This commit is contained in:
parent
e8477db7b8
commit
57d9ff8ed0
11 changed files with 158 additions and 51 deletions
13
client/package-lock.json
generated
13
client/package-lock.json
generated
|
@ -23,6 +23,7 @@
|
||||||
"rollup-plugin-svelte": "^7.0.0",
|
"rollup-plugin-svelte": "^7.0.0",
|
||||||
"rollup-plugin-terser": "^7.0.0",
|
"rollup-plugin-terser": "^7.0.0",
|
||||||
"svelte": "^3.0.0",
|
"svelte": "^3.0.0",
|
||||||
|
"svelte-keydown": "^0.4.0",
|
||||||
"svelte-preprocess": "^4.9.8"
|
"svelte-preprocess": "^4.9.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1947,6 +1948,12 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svelte-keydown": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-keydown/-/svelte-keydown-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-a0S5m+u78FE1jgpuSCZPtgh/KhwNAO9t8kkcBkK1sK9ZiVC+Iu5XOCqIu1F+PEyenRTIxGaA4yr6KAsYFB6kkQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/svelte-preprocess": {
|
"node_modules/svelte-preprocess": {
|
||||||
"version": "4.9.8",
|
"version": "4.9.8",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.9.8.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.9.8.tgz",
|
||||||
|
@ -3700,6 +3707,12 @@
|
||||||
"integrity": "sha512-4DrCEJoBvdR689efHNSxIQn2pnFwB7E7j2yLEJtHE/P8hxwZWIphCtJ8are7bjl/iVMlcEf5uh5pJ68IwR09vQ==",
|
"integrity": "sha512-4DrCEJoBvdR689efHNSxIQn2pnFwB7E7j2yLEJtHE/P8hxwZWIphCtJ8are7bjl/iVMlcEf5uh5pJ68IwR09vQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"svelte-keydown": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-keydown/-/svelte-keydown-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-a0S5m+u78FE1jgpuSCZPtgh/KhwNAO9t8kkcBkK1sK9ZiVC+Iu5XOCqIu1F+PEyenRTIxGaA4yr6KAsYFB6kkQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"svelte-preprocess": {
|
"svelte-preprocess": {
|
||||||
"version": "4.9.8",
|
"version": "4.9.8",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.9.8.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.9.8.tgz",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"rollup-plugin-svelte": "^7.0.0",
|
"rollup-plugin-svelte": "^7.0.0",
|
||||||
"rollup-plugin-terser": "^7.0.0",
|
"rollup-plugin-terser": "^7.0.0",
|
||||||
"svelte": "^3.0.0",
|
"svelte": "^3.0.0",
|
||||||
|
"svelte-keydown": "^0.4.0",
|
||||||
"svelte-preprocess": "^4.9.8"
|
"svelte-preprocess": "^4.9.8"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
export let balance="5425";
|
export let balance="5425";
|
||||||
export let iban="RONFOX62188921";
|
export let iban="RONFOX62188921";
|
||||||
export let isExpanded=false;
|
export let isExpanded=false;
|
||||||
let transactions=[];
|
export let transactions=[];
|
||||||
export let accountId;
|
export let accountId;
|
||||||
|
|
||||||
let copied = false;
|
let copied = false;
|
||||||
|
@ -55,14 +55,6 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount( () => {
|
|
||||||
gettransactions($token, accountId).then( result => {
|
|
||||||
if(result.status == "success") {
|
|
||||||
transactions = result.transactions;
|
|
||||||
transactions.sort((e1, e2) => new Date(e2.datetime) - new Date(e1.datetime));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,7 +74,7 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<DetailField class="p-1 flex-shrink">
|
<DetailField class="p-1 flex-shrink">
|
||||||
<h2 class='font-sans mt-3 mb-2 pl-2 text-4xl text-gray-50'>Balance: <span style="color: #264D59">{balance}</span>{currency}</h2>
|
<h2 class='font-sans mt-3 mb-2 pl-2 text-4xl text-gray-50'>Balance: <span style="color: #264D59">{amountToString(balance)}</span>{currency}</h2>
|
||||||
</DetailField>
|
</DetailField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -42,21 +42,39 @@
|
||||||
})
|
})
|
||||||
|
|
||||||
setContext("user", user);
|
setContext("user", user);
|
||||||
|
const refreshAccounts = writable(null);
|
||||||
|
setContext("refreshAccounts", refreshAccounts);
|
||||||
|
|
||||||
const accounts = readable(null, set => {
|
const accounts = readable(null, set => {
|
||||||
const unsubscribe = userToken.subscribe(token => {
|
function getAccounts(token){
|
||||||
if(token == null) {
|
if(token==null){
|
||||||
set(null);
|
set(null);
|
||||||
}else{
|
}else{
|
||||||
getaccountlist(token)
|
getaccountlist(token)
|
||||||
.then(result =>{
|
.then(result => {
|
||||||
set(result);
|
set(result);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let token = null;
|
||||||
|
|
||||||
|
refreshAccounts.set( () => {
|
||||||
|
getAccounts(token);
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribe = userToken.subscribe(newToken => {
|
||||||
|
token = newToken;
|
||||||
|
getAccounts(token);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
getAccounts(token);
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
|
clearInterval(intervalId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
<div class="w-full max-w-md self-start border-solid border mb-3"></div>
|
<div class="w-full max-w-md self-start border-solid border mb-3"></div>
|
||||||
|
|
||||||
<div class="flex flex-col flex-grow pl-8 pr-10 relative scroller overflow-auto overflow-x-hidden max-h-full min-h-0">
|
<div class="flex flex-col flex-grow pl-8 pr-10 relative scroller overflow-auto overflow-x-hidden max-h-full min-h-0">
|
||||||
|
{#if notifications.length > 0}
|
||||||
{#each notifications as notification,i (i)}
|
{#each notifications as notification,i (i)}
|
||||||
<div on:click={() => onNotificationClick(notification.id)} in:slide={{delay:100*i}} out:slide={{delay:50*(notifications.length-i)}}>
|
<div on:click={() => onNotificationClick(notification.id)} in:slide={{delay:100*i}} out:slide={{delay:50*(notifications.length-i)}}>
|
||||||
|
|
||||||
|
@ -68,6 +69,14 @@
|
||||||
</DetailField>
|
</DetailField>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
{:else}
|
||||||
|
<DetailField class="relative my-3 py-1 flex-shrink min-w-transaction max-w-4xl">
|
||||||
|
<div class='font-sans text-gray-50 text-2xl mt-2 mx-4 border-b-1'>
|
||||||
|
No notifications.
|
||||||
|
</div>
|
||||||
|
</DetailField>
|
||||||
|
{/if}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="m-2"></div>
|
<div class="m-2"></div>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
|
|
||||||
const token = getContext("token");
|
const token = getContext("token");
|
||||||
|
const refreshAccounts = getContext("refreshAccounts");
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
@ -32,6 +33,9 @@
|
||||||
const result = await createaccount($token, name, currency, type);
|
const result = await createaccount($token, name, currency, type);
|
||||||
if(result.status == "success") {
|
if(result.status == "success") {
|
||||||
dispatch("createPopup",{type:"create_acc_success"});
|
dispatch("createPopup",{type:"create_acc_success"});
|
||||||
|
if($refreshAccounts){
|
||||||
|
$refreshAccounts();
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
dispatch("createPopup",{type:"create_acc_failed", reason:"Failed to create account. Error:"+result.status});
|
dispatch("createPopup",{type:"create_acc_failed", reason:"Failed to create account. Error:"+result.status});
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<input type={isPassword ? "password" : "text"} placeholder={placeholder} value={value} on:input={handleInput} class="placeholder-gray-300 p-3 text-gray-50 w-full text-3xl">
|
<input on:keydown type={isPassword ? "password" : "text"} placeholder={placeholder} value={value} on:input={handleInput} class="placeholder-gray-300 p-3 text-gray-50 w-full text-3xl">
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -24,6 +24,19 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function enter(event) {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
const result = await login(username, code);
|
||||||
|
if(result.status == "success") {
|
||||||
|
dispatch("loginSuccess",{
|
||||||
|
token: result.token,
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
alert(result.code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="h-full">
|
<main class="h-full">
|
||||||
|
@ -35,7 +48,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="m-3 flex-shrink">
|
<div class="m-3 flex-shrink">
|
||||||
<InputField placeholder="Code" isPassword={true} bind:value={code}></InputField>
|
<InputField on:keydown={enter} placeholder="Code" isPassword={true} bind:value={code}></InputField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="m-3 flex-shrink">
|
<div class="m-3 flex-shrink">
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
import GreenButton from './GreenButton.svelte';
|
import GreenButton from './GreenButton.svelte';
|
||||||
import { fade, fly, slide } from 'svelte/transition';
|
import { fade, fly, slide } from 'svelte/transition';
|
||||||
import { flip } from 'svelte/animate';
|
import { flip } from 'svelte/animate';
|
||||||
import { logout, whoami, getaccountlist, getnotificationlist } from './api';
|
import { logout, whoami, getaccountlist, getnotificationlist, getForex } from './api';
|
||||||
import { amountToString } from './utils';
|
import { amountToString } from './utils';
|
||||||
|
|
||||||
const token = getContext("token");
|
const token = getContext("token");
|
||||||
const user = getContext("user");
|
const user = getContext("user");
|
||||||
|
@ -20,7 +20,7 @@ import { amountToString } from './utils';
|
||||||
$: username = $user.user.username;
|
$: username = $user.user.username;
|
||||||
$: email = $user.user.email;
|
$: email = $user.user.email;
|
||||||
$: notifications = $notificationsStore ? $notificationsStore.notifications : [];
|
$: notifications = $notificationsStore ? $notificationsStore.notifications : [];
|
||||||
let totalbalance = "2455.22";
|
let totalbalance = "0.00";
|
||||||
let maincurrency = "RON";
|
let maincurrency = "RON";
|
||||||
let expandedAccount = null;
|
let expandedAccount = null;
|
||||||
let showAllAccounts = true;
|
let showAllAccounts = true;
|
||||||
|
@ -30,12 +30,21 @@ import { amountToString } from './utils';
|
||||||
return {
|
return {
|
||||||
name: account.customName ? account.customName : `${account.accountType} Account`,
|
name: account.customName ? account.customName : `${account.accountType} Account`,
|
||||||
currency: account.currency,
|
currency: account.currency,
|
||||||
balance: amountToString(account.balance),
|
balance: account.balance,
|
||||||
iban: account.iban.replace(/(.{4})/g, "$1 "),
|
iban: account.iban.replace(/(.{4})/g, "$1 "),
|
||||||
id: account.id,
|
id: account.id,
|
||||||
|
transactions: account.transactions,
|
||||||
}
|
}
|
||||||
}) : [];
|
}) : [];
|
||||||
|
|
||||||
|
$: {
|
||||||
|
Promise.all(accounts.map(account => {
|
||||||
|
return getForex(account.currency, maincurrency, account.balance);
|
||||||
|
})).then( balances => {
|
||||||
|
const sum = balances.reduce((acc, current) => acc+current, 0);
|
||||||
|
totalbalance = amountToString(Math.round(sum));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function dispatchLogout(){
|
function dispatchLogout(){
|
||||||
if (confirm("Log out?")) {
|
if (confirm("Log out?")) {
|
||||||
|
@ -92,7 +101,7 @@ import { amountToString } from './utils';
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="self-center m-0">
|
<div class="self-center m-0">
|
||||||
{#if showAllAccounts}
|
{#if showAllAccounts && (accounts.length < 3) }
|
||||||
<div in:slide={{delay:500*accounts.length, duration:250*accounts.length}}>
|
<div in:slide={{delay:500*accounts.length, duration:250*accounts.length}}>
|
||||||
<GreenButton on:click={createAccount}><Icon icon="akar-icons:plus" color="rgba(249, 250, 251, 1)" width="26" height="26" /></GreenButton>
|
<GreenButton on:click={createAccount}><Icon icon="akar-icons:plus" color="rgba(249, 250, 251, 1)" width="26" height="26" /></GreenButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
let amount=0.00;
|
let amount=0.00;
|
||||||
let description="";
|
let description="";
|
||||||
const token = getContext("token");
|
const token = getContext("token");
|
||||||
|
const refreshAccounts = getContext("refreshAccounts");
|
||||||
|
|
||||||
|
|
||||||
async function create(){
|
async function create(){
|
||||||
|
@ -36,6 +37,9 @@
|
||||||
await createtransaction($token, receiveriban, Math.round(amount*100), account.id, description).then( result => {
|
await createtransaction($token, receiveriban, Math.round(amount*100), account.id, description).then( result => {
|
||||||
if(result.status == "success") {
|
if(result.status == "success") {
|
||||||
dispatch("createPopup",{type:"send_money_success"});
|
dispatch("createPopup",{type:"send_money_success"});
|
||||||
|
if($refreshAccounts){
|
||||||
|
$refreshAccounts();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,18 @@ export async function getaccountlist(token) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return (await result.json());
|
const data = await result.json();
|
||||||
|
|
||||||
|
if(data.status == "success"){
|
||||||
|
for(let i=0; i < data.accounts.length; i++){
|
||||||
|
const transactionsreq = await gettransactions(token, data.accounts[i].id);
|
||||||
|
if (transactionsreq.status == "success") {
|
||||||
|
data.accounts[i].transactions = transactionsreq.transactions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
status: "error",
|
status: "error",
|
||||||
|
@ -146,7 +157,12 @@ export async function getnotificationlist(token) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return (await result.json());
|
const data = await result.json();
|
||||||
|
if(data.status == "success") {
|
||||||
|
data.notifications.sort((e1, e2) => new Date(e2.datetime) - new Date(e1.datetime));
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
status: "error",
|
status: "error",
|
||||||
|
@ -233,7 +249,12 @@ export async function gettransactions(token, id) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return (await result.json());
|
const data = await result.json();
|
||||||
|
if(data.status == "success") {
|
||||||
|
data.transactions.sort((e1, e2) => new Date(e2.datetime) - new Date(e1.datetime));
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
status: "error",
|
status: "error",
|
||||||
|
@ -241,3 +262,26 @@ export async function gettransactions(token, id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export async function getForex(from, to, amount) {
|
||||||
|
try {
|
||||||
|
const result = await fetch(new URL("/forex/"+from+"/"+to, baseURL), {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await result.json();
|
||||||
|
if(data.status == "success") {
|
||||||
|
const exchanged = amount * data.rate;
|
||||||
|
return exchanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue