diff --git a/client/.vscode/launch.json b/client/.vscode/launch.json new file mode 100644 index 0000000..b70a0dc --- /dev/null +++ b/client/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-msedge", + "request": "launch", + "name": "Launch Edge against localhost", + "url": "http://localhost:5000", + "webRoot": "${workspaceFolder}/public" + } + ] +} \ No newline at end of file diff --git a/client/src/AccountCard.svelte b/client/src/AccountCard.svelte index 14c9eae..7220394 100644 --- a/client/src/AccountCard.svelte +++ b/client/src/AccountCard.svelte @@ -5,12 +5,15 @@ import CardBG from "./CardBG.svelte"; import DetailField from './DetailField.svelte'; import GreenButton from './GreenButton.svelte'; - import {createEventDispatcher} from 'svelte'; + import {createEventDispatcher, onMount, getContext} from 'svelte'; import { fade, fly, slide } from 'svelte/transition'; import { flip } from 'svelte/animate'; + import { gettransactions } from './api'; + import { amountToString } from './utils'; const dispatch = createEventDispatcher(); + const token = getContext("token"); export let name="RON Account"; @@ -18,7 +21,8 @@ export let balance="5425"; export let iban="RONFOX62188921"; export let isExpanded=false; - export let transactions=[]; + let transactions=[]; + export let accountId; let copied = false; @@ -42,6 +46,7 @@ dispatch("createPopup",{ type: 'send_money', account: { + id: accountId, type: name, currency, balance, @@ -49,6 +54,15 @@ } }); } + + 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)); + } + }); + }) @@ -80,18 +94,27 @@
-

{transaction.title}:

- {transaction.amount} - {currency} + {#if transaction.transactionType == "send_transfer"} +

Sent money:

+ {:else if transaction.transactionType == "receive_transfer"} +

Received money:

+ {:else if transaction.transactionType == "card_payment"} +

{transaction.otherParty.store}:

+ {:else if transaction.transactionType == "fee"} +

Fees:

+ {/if} + + {amountToString(transaction.extra.amount)} + {transaction.extra.currency}
-

at {transaction.time}

+

at {new Date(transaction.datetime).toLocaleString()}

- {#if transaction.status == "PROCESSED"} + {#if transaction.status == "processed"} - {:else if transaction.status == "PENDING"} + {:else if transaction.status == "pending"} @@ -114,18 +137,27 @@ {:else if transactions.length > 0}
-

{transactions[0].title}:

- {transactions[0].amount} - {currency} + {#if transactions[0].transactionType == "send_transfer"} +

Sent money:

+ {:else if transactions[0].transactionType == "receive_transfer"} +

Received money:

+ {:else if transactions[0].transactionType == "card_payment"} +

{transactions[0].otherParty.store}:

+ {:else if transactions[0].transactionType == "fee"} +

Fees:

+ {/if} + + {amountToString(transactions[0].extra.amount)} + {transactions[0].extra.currency}
-

at {transactions[0].time}

+

at {new Date(transactions[0].datetime).toLocaleString()}

- {#if transactions[0].status == "PROCESSED"} + {#if transactions[0].status == "processed"} - {:else if transactions[0].status == "PENDING"} + {:else if transactions[0].status == "pending"} diff --git a/client/src/App.svelte b/client/src/App.svelte index 536eced..ed8e7f4 100644 --- a/client/src/App.svelte +++ b/client/src/App.svelte @@ -1,7 +1,7 @@
@@ -29,18 +40,30 @@
{#each notifications as notification,i (i)} -
+
onNotificationClick(notification.id)} in:slide={{delay:100*i}} out:slide={{delay:50*(notifications.length-i)}}> - +
- {notification.text} + {notification.body}
-
+
- at {notification.time} + at {new Date(notification.datetime).toLocaleString()}
+ +
+ {#if !notification.read} + + + + {:else} + + + + {/if} +
diff --git a/client/src/CreateAccount.svelte b/client/src/CreateAccount.svelte index c715c48..acbc6ba 100644 --- a/client/src/CreateAccount.svelte +++ b/client/src/CreateAccount.svelte @@ -22,10 +22,7 @@ $: placeholder = type==null ? "Checking Account" : `${type} Account`; async function create(){ - if(name == "" || name == null) { - alert("Account Name field can not be empty!"); - console.debug(`account name: ${type}`) - }else if(type == null){ + if(type == null){ alert("Type is not selected!"); }else if(currency == null){ alert("Currency is not selected!"); @@ -34,7 +31,7 @@ }else{ const result = await createaccount($token, name, currency, type); if(result.status == "success") { - dispatch("createPopup",{type:"create_acc_success", account:{type:type, currency:currency, transactions:[]}}); + dispatch("createPopup",{type:"create_acc_success"}); }else{ dispatch("createPopup",{type:"create_acc_failed", reason:"Failed to create account. Error:"+result.status}); } diff --git a/client/src/MainPage.svelte b/client/src/MainPage.svelte index 2214e33..ba89f3f 100644 --- a/client/src/MainPage.svelte +++ b/client/src/MainPage.svelte @@ -6,74 +6,44 @@ import GreenButton from './GreenButton.svelte'; import { fade, fly, slide } from 'svelte/transition'; import { flip } from 'svelte/animate'; - import { logout, whoami, getaccountlist } from './api'; + import { logout, whoami, getaccountlist, getnotificationlist } from './api'; +import { amountToString } from './utils'; const token = getContext("token"); const user = getContext("user"); const accountsStore = getContext("accounts"); + const notificationsStore = getContext("notifications"); const dispatch = createEventDispatcher(); $: fullname = $user.user.fullname; $: username = $user.user.username; $: email = $user.user.email; + $: notifications = $notificationsStore ? $notificationsStore.notifications : []; let totalbalance = "2455.22"; let maincurrency = "RON"; let expandedAccount = null; let showAllAccounts = true; - let notifications = [ - {time: "15:38 27/11/2021", text: "A notification's text."}, - {time: "15:38 27/11/2021", text: "A notification's text but longer aaaaaaaaaaaa asddagagfabsdhubaiufbau bdauhsbabsdbayub badysabdyba ybbdbasbd bbdabsdb aybdbaysbdya bybdabs bdabsdbadbua."}, - {time: "15:38 27/11/2021", text: "A notification's text but way longer absdb aybdbaysbdya bybdabs bdabsd absdb aybdbaysbdya bybdabs bdabsd absdb aybdbaysbdya bybdabs bdabsd absdb aybdbaysbdya bybdabs bdabsd absdb aybdbaysbdya bybdabs bdabsd absdb aybdbaysbdya bybdabs bdabsd absdb aybdbaysbdya bybdabs bdabsd absdb aybdbaysbdya bybdabs bdabsd absdb aybdbaysbdya bybdabs bdabsd."}, - {time: "15:38 27/11/2021", text: "A notification's text."}, - {time: "15:38 27/11/2021", text: "A notification's text."}, - - ]; + $: notifications_unread = notifications.filter(n => !n.read).length; $: accounts = $accountsStore ? $accountsStore.accounts.map(account => { return { name: account.customName ? account.customName : `${account.accountType} Account`, currency: account.currency, - balance: "123.12", + balance: amountToString(account.balance), iban: account.iban.replace(/(.{4})/g, "$1 "), - transactions: [ - {title:"Transaction Name#1", status:"PROCESSED", amount:"-45.09", time:"15:38 27/11/2021", type:"send"}, - {title:"Transaction Name#2", status:"PENDING", amount:"+25.00", time:"15:38 27/11/2021", type:"received"}, - ], + id: account.id, } }) : []; - // let accounts = [ - // {type:"RON Account", currency:"RON", balance:"420.42", iban:"RONFOX62188921", - // transactions: [ - // {title:"Transaction Name#1", status:"PROCESSED", amount:"-45.09", time:"15:38 27/11/2021", type:"send"}, - // {title:"Transaction Name#2", status:"PENDING", amount:"+25.00", time:"15:38 27/11/2021", type:"received"}, - // {title:"Transaction Name#3", status:"CANCELLED", amount:"-469.09", time:"15:38 27/11/2021", type:"send"}, - // {title:"Transaction Name#1", status:"PROCESSED", amount:"-45.09", time:"15:38 27/11/2021", type:"send"}, - // {title:"Transaction Name#2", status:"PENDING", amount:"+25.00", time:"15:38 27/11/2021", type:"received"}, - // {title:"Transaction Name#3", status:"CANCELLED", amount:"-469.09", time:"15:38 27/11/2021", type:"send"}, - // {title:"Transaction Name#1", status:"PROCESSED", amount:"-45.09", time:"15:38 27/11/2021", type:"send"}, - // {title:"Transaction Name#2", status:"PENDING", amount:"+25.00", time:"15:38 27/11/2021", type:"received"}, - // {title:"Transaction Name#3", status:"CANCELLED", amount:"-469.09", time:"15:38 27/11/2021", type:"send"}, - // ] - // }, - // {type:"EUR Account", currency:"EUR", balance:"620,42", iban:"EURFOX62188921", - // transactions: [ - // {title:"Transaction Name#2", status:"PENDING", amount:"+25.00", time:"15:38 27/11/2021", type:"received"}, - // {title:"Transaction Name#1", status:"PROCESSED", amount:"-45.09", time:"15:38 27/11/2021", type:"send"}, - // {title:"Transaction Name#3", status:"CANCELLED", amount:"-469.09", time:"15:38 27/11/2021", type:"send"}, - // ] - // }, - // ]; + function dispatchLogout(){ - //todo: CHeck here if (confirm("Log out?")) { logout($token); dispatch("logOut",null); } } - function expanded(index) { if (!expandedAccount && expandedAccount !== 0) { expandedAccount = index; @@ -110,12 +80,12 @@
{#if expandedAccount || expandedAccount === 0} - expanded(null)}> + expanded(null)}> {:else} {#if showAllAccounts} {#each accounts as account,i}
- expanded(i)} on:createPopup> + expanded(i)} on:createPopup>
{/each} {/if} @@ -137,7 +107,7 @@

{fullname}

- +
diff --git a/client/src/SendMoney.svelte b/client/src/SendMoney.svelte index c912bad..d3cdfc8 100644 --- a/client/src/SendMoney.svelte +++ b/client/src/SendMoney.svelte @@ -3,37 +3,42 @@ import CardBG from "./CardBG.svelte"; import InputField from "./InputField.svelte"; - import {createEventDispatcher} from 'svelte'; + import {createEventDispatcher, getContext} from 'svelte'; import Icon from "@iconify/svelte"; import Overlay from "./Overlay.svelte"; import TextareaField from "./TextareaField.svelte"; import { fade, fly, slide } from 'svelte/transition'; import { flip } from 'svelte/animate'; + import { createnotification, createtransaction } from "./api"; const dispatch = createEventDispatcher(); - export let account={type: "", currency:"", balance:0}; + export let account={id: -1, type: "", currency:"", balance:0}; let receivername=""; let receiveriban=""; let amount=0.00; let description=""; + const token = getContext("token"); - let send_details={receivername:"", receiveriban:"", amount:0, description:""}; - function create(){ + async function create(){ if(receivername == "" || receivername == null) { alert("Receiver's name field can not be empty!"); }else if(receiveriban == "" || receiveriban == null){ alert("Receiver's iBan field can not be empty!"); - }else if (amount > parseFloat(account.balance) ){ + }else if (parseFloat(amount) > parseFloat(account.balance) ){ alert("Not enough money in your account!"); }else if (amount <= 0.00 ){ alert("Insert a valid amount!"); }else{ //TODO Create account with provided details on the server - send_details={receivername:receivername, receiveriban:receiveriban, amount:amount, description:description} - dispatch("createPopup",{type:"send_money_success", send_details:{send_details}}); + await createtransaction($token, receiveriban, Math.round(amount*100), account.id, description).then( result => { + if(result.status == "success") { + dispatch("createPopup",{type:"send_money_success"}); + } + }); + } } @@ -63,7 +68,7 @@

IBAN:

- +
diff --git a/client/src/api.js b/client/src/api.js index ccae9f4..1c4e917 100644 --- a/client/src/api.js +++ b/client/src/api.js @@ -112,11 +112,15 @@ export async function getaccounttypes() { } } - -export async function getnotificationlist(token) { +export async function createaccount(token, name, currency, type) { try { - const result = await fetch(new URL("/notifications", baseURL), { - method: "GET", + const result = await fetch(new URL("/accounts/", baseURL), { + method: "POST", + body: JSON.stringify({ + customName: name, + currency: currency, + accountType: type, + }), headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token, @@ -132,9 +136,9 @@ export async function getnotificationlist(token) { } } -export async function gettransactions(token, id) { +export async function getnotificationlist(token) { try { - const result = await fetch(new URL("/transactions?accountId="+id, baseURL), { + const result = await fetch(new URL("/notifications/", baseURL), { method: "GET", headers: { "Content-Type": "application/json", @@ -151,14 +155,13 @@ export async function gettransactions(token, id) { } } -export async function createaccount(token, name, currency, type) { +export async function createnotification(token, body, read) { try { - const result = await fetch(new URL("/accounts/", baseURL), { + const result = await fetch(new URL("/notifications/", baseURL), { method: "POST", body: JSON.stringify({ - customName: name, - currency: currency, - accountType: type, + body: body, + read: read, }), headers: { "Content-Type": "application/json", @@ -175,13 +178,10 @@ export async function createaccount(token, name, currency, type) { } } -export async function createnotification(token, body, datetime) { +export async function marknotificationread(token, id) { try { - const result = await fetch(new URL("/notification/create", baseURL), { + const result = await fetch(new URL("/notifications/"+id+"/mark_read", baseURL), { method: "POST", - body: JSON.stringify({ - body, datetime, - }), headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token, @@ -197,12 +197,15 @@ export async function createnotification(token, body, datetime) { } } -export async function createtransaction(token, otherparty, amount, type, ) { +export async function createtransaction(token, otherparty, amount, accountId, description) { try { - const result = await fetch(new URL("/transaction/create", baseURL), { + const result = await fetch(new URL("/transactions/", baseURL), { method: "POST", body: JSON.stringify({ - otherparty, amount, type, + description: description, + account_id: accountId, + destination_iban: otherparty, + amount: amount, }), headers: { "Content-Type": "application/json", @@ -217,4 +220,24 @@ export async function createtransaction(token, otherparty, amount, type, ) { code: "request/failure" } } -} \ No newline at end of file +} + + +export async function gettransactions(token, id) { + try { + const result = await fetch(new URL("/transactions/?account_id="+id, baseURL), { + method: "GET", + headers: { + "Content-Type": "application/json", + "Authorization": "Bearer " + token, + }, + }); + + return (await result.json()); + } catch (error) { + return { + status: "error", + code: "request/failure" + } + } +} diff --git a/client/src/utils.js b/client/src/utils.js new file mode 100644 index 0000000..3b59876 --- /dev/null +++ b/client/src/utils.js @@ -0,0 +1,4 @@ +export function amountToString(amount) { + amount = amount.toString().padStart(3, "0"); + return amount.replace(/(.{2})$/, ".$1"); +} \ No newline at end of file diff --git a/server/.vscode/launch.json b/server/.vscode/launch.json index 195ea05..f35221f 100644 --- a/server/.vscode/launch.json +++ b/server/.vscode/launch.json @@ -4,6 +4,7 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { "name": "Python: Flask", "type": "python",