Browse Source

Implemented api stuff

pull/9/head
DariusTFox24 3 years ago
parent
commit
e8477db7b8
  1. 15
      client/.vscode/launch.json
  2. 60
      client/src/AccountCard.svelte
  3. 43
      client/src/App.svelte
  4. 37
      client/src/CheckNotifications.svelte
  5. 7
      client/src/CreateAccount.svelte
  6. 52
      client/src/MainPage.svelte
  7. 21
      client/src/SendMoney.svelte
  8. 63
      client/src/api.js
  9. 4
      client/src/utils.js
  10. 1
      server/.vscode/launch.json

15
client/.vscode/launch.json vendored

@ -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"
}
]
}

60
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));
}
});
})
</script>
@ -80,18 +94,27 @@
<DetailField class="my-3 py-1 flex-shrink min-w-transaction">
<div class='font-sans text-gray-50 mt-2 mx-4 border-b-1'>
<h3 class="inline mr-3">{transaction.title}: </h3>
<span class="text-4xl {transaction.type == "send" ? "text-red-c" : "text-lime-c"}">{transaction.amount}</span>
<span class="text-4xl">{currency}</span>
{#if transaction.transactionType == "send_transfer"}
<h3 class="inline mr-3">Sent money: </h3>
{:else if transaction.transactionType == "receive_transfer"}
<h3 class="inline mr-3">Received money: </h3>
{:else if transaction.transactionType == "card_payment"}
<h3 class="inline mr-3">{transaction.otherParty.store}: </h3>
{:else if transaction.transactionType == "fee"}
<h3 class="inline mr-3">Fees: </h3>
{/if}
<span class="text-4xl {transaction.transactionType == "receive_transfer" ? "text-lime-c" : "text-red-c"}">{amountToString(transaction.extra.amount)}</span>
<span class="text-4xl">{transaction.extra.currency}</span>
</div>
<div class='font-sans text-2xl text-gray-100 mt-2 mx-6 border-b-1'>
<p class="inline">at {transaction.time} </p>
<p class="inline">at {new Date(transaction.datetime).toLocaleString()} </p>
{#if transaction.status == "PROCESSED"}
{#if transaction.status == "processed"}
<span>
<Icon class="inline mb-1" icon="akar-icons:circle-check" color="#6DE25ACC"/>
</span>
{:else if transaction.status == "PENDING"}
{:else if transaction.status == "pending"}
<span>
<Icon class="inline mb-1" icon="akar-icons:arrow-cycle" color="#F6AF43"/>
</span>
@ -114,18 +137,27 @@
{:else if transactions.length > 0}
<DetailField class="my-3 py-2 flex-shrink min-w-transaction">
<div class='font-sans text-gray-50 mt-2 mx-4 border-b-1'>
<h3 class="inline mr-3">{transactions[0].title}: </h3>
<span class="text-4xl {transactions[0].type == "send" ? "text-red-c" : "text-lime-c"}">{transactions[0].amount}</span>
<span class="text-4xl">{currency}</span>
{#if transactions[0].transactionType == "send_transfer"}
<h3 class="inline mr-3">Sent money: </h3>
{:else if transactions[0].transactionType == "receive_transfer"}
<h3 class="inline mr-3">Received money: </h3>
{:else if transactions[0].transactionType == "card_payment"}
<h3 class="inline mr-3">{transactions[0].otherParty.store}: </h3>
{:else if transactions[0].transactionType == "fee"}
<h3 class="inline mr-3">Fees: </h3>
{/if}
<span class="text-4xl {transactions[0].transactionType == "receive_transfer" ? "text-lime-c" : "text-red-c"}">{amountToString(transactions[0].extra.amount)}</span>
<span class="text-4xl">{transactions[0].extra.currency}</span>
</div>
<div class='font-sans text-2xl text-gray-100 mt-2 mx-6 border-b-1'>
<p class="inline">at {transactions[0].time} </p>
<p class="inline">at {new Date(transactions[0].datetime).toLocaleString()} </p>
{#if transactions[0].status == "PROCESSED"}
{#if transactions[0].status == "processed"}
<span>
<Icon class="inline mb-1" icon="akar-icons:circle-check" color="#6DE25ACC"/>
</span>
{:else if transactions[0].status == "PENDING"}
{:else if transactions[0].status == "pending"}
<span>
<Icon class="inline mb-1" icon="akar-icons:arrow-cycle" color="#F6AF43"/>
</span>

43
client/src/App.svelte

@ -1,7 +1,7 @@
<script>
import { onMount, setContext } from "svelte";
import { writable, readable } from "svelte/store";
import { whoami, createnotification, getaccountlist } from "./api";
import { whoami, createnotification, getaccountlist, getnotificationlist } from "./api";
import BottomBorder from "./BottomBorder.svelte";
import CheckNotifications from "./CheckNotifications.svelte";
@ -62,6 +62,44 @@
setContext("accounts", accounts);
const refreshNotifications = writable(null);
setContext("refreshNotifications", refreshNotifications);
const notifications = readable(null, set=> {
function getNotifications(token){
if(token==null){
set(null);
}else{
getnotificationlist(token)
.then(result => {
set(result);
})
}
}
let token = null;
refreshNotifications.set( () => {
getNotifications(token);
});
const unsubscribe = userToken.subscribe(newToken => {
token = newToken;
getNotifications(token);
})
const intervalId = setInterval(() => {
getNotifications(token);
}, 10000);
return () => {
unsubscribe();
clearInterval(intervalId);
}
})
setContext("notifications", notifications);
let isCreatingAccount = false;
let isCheckingNotifications = false;
let isSendingMoney = false;
@ -116,7 +154,6 @@
break;
case "check_notifications":
notifications = event.detail.notifications;
isCheckingNotifications = true;
break;
@ -146,7 +183,7 @@
{:else if isCheckingNotifications}
<Overlay>
<div class="flex items-center justify-center h-full">
<CheckNotifications on:createPopup={onCreatePopup} notifications={notifications}></CheckNotifications>
<CheckNotifications on:createPopup={onCreatePopup}></CheckNotifications>
</div>
</Overlay>
{:else if isSendingMoney}

37
client/src/CheckNotifications.svelte

@ -1,21 +1,32 @@
<script>
import CardBG from "./CardBG.svelte";
import {createEventDispatcher} from 'svelte';
import {createEventDispatcher, getContext} from 'svelte';
import Icon from "@iconify/svelte";
import { fade, fly, slide } from 'svelte/transition';
import { flip } from 'svelte/animate';
import DetailField from "./DetailField.svelte";
import { marknotificationread } from "./api";
const dispatch = createEventDispatcher();
const notificationsStore = getContext("notifications");
const refreshNotifications = getContext("refreshNotifications");
const token = getContext("token");
export let notifications = [{time: "15:38 27/11/2021", text: "A notification's text."}];
$: notifications = $notificationsStore ? $notificationsStore.notifications : [];
function cancelCheckNotifications(){
dispatch("createPopup",{type:"check_notifications_cancelled"});
}
async function onNotificationClick(id){
await marknotificationread($token, id);
if($refreshNotifications){
$refreshNotifications();
}
}
</script>
<div class="h-full self-center">
@ -29,18 +40,30 @@
<div class="flex flex-col flex-grow pl-8 pr-10 relative scroller overflow-auto overflow-x-hidden max-h-full min-h-0">
{#each notifications as notification,i (i)}
<div 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)}}>
<DetailField class="my-3 py-1 flex-shrink min-w-transaction max-w-4xl">
<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'>
{notification.text}
{notification.body}
</div>
<div class="flex flex-row">
<div class="flex flex-row">
<div class='inline font-sans ml-auto mr-4 text-xl text-gray-100 mt-2 mx-6 border-b-1'>
<span> at {notification.time} </span>
<span> at {new Date(notification.datetime).toLocaleString()} </span>
</div>
</div>
<div class='fixed font-sans text-6xl text-gray-100 left-2 bottom-2'>
{#if !notification.read}
<svg class="fill-current text-gray-100" viewBox="0 0 16 16" width="16" height="16">
<circle cx="8" cy="8" r="8"></circle>
</svg>
{:else}
<svg class="fill-current text-gray-100" viewBox="0 0 24 24" width="16" height="16">
<path d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/>
</svg>
{/if}
</div>
</DetailField>
</div>

7
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});
}

52
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 @@
<main class="h-full flex flex-col items-stretch md:flex-row">
<div class="flex flex-col items-stretch max-h-full">
{#if expandedAccount || expandedAccount === 0}
<AccountCard name={accounts[expandedAccount].name} currency={accounts[expandedAccount].currency} balance={accounts[expandedAccount].balance} iban={accounts[expandedAccount].iban} transactions={accounts[expandedAccount].transactions} isExpanded={true} on:expanded={() => expanded(null)}></AccountCard>
<AccountCard accountId={accounts[expandedAccount].id} name={accounts[expandedAccount].name} currency={accounts[expandedAccount].currency} balance={accounts[expandedAccount].balance} iban={accounts[expandedAccount].iban} transactions={accounts[expandedAccount].transactions} isExpanded={true} on:expanded={() => expanded(null)}></AccountCard>
{:else}
{#if showAllAccounts}
{#each accounts as account,i}
<div in:slide={{delay:500*i, duration:250*(i==0 ? 1 : i) }}>
<AccountCard name={account.name} currency={account.currency} balance={account.balance} iban={account.iban} transactions={account.transactions} isExpanded={false} on:expanded={() => expanded(i)} on:createPopup></AccountCard>
<AccountCard accountId={account.id} name={account.name} currency={account.currency} balance={account.balance} iban={account.iban} transactions={account.transactions} isExpanded={false} on:expanded={() => expanded(i)} on:createPopup></AccountCard>
</div>
{/each}
{/if}
@ -137,7 +107,7 @@
<CardBG class="flex-shrink flex flex-col min-w-transaction items-stretch md:self-start p-6">
<div class="flex flex-row">
<h1 class='font-sans flex-grow text-5xl text-gray-50 m-6 border-b-2'>{fullname}</h1>
<button on:click={checkNotifications} style=" filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));"> <Icon icon="akar-icons:envelope" color="#FB6666" width="36" height="36" /></button>
<button on:click={checkNotifications} style=" filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));"> <Icon icon={notifications_unread==0 ? "akar-icons:envelope" : "akar-icons:open-envelope"} color="#FB6666" width="36" height="36" /></button>
</div>
<div class="m-3 flex-shrink">

21
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 @@
<div class="mx-1 flex-shrink">
<h2 class='font-sans text-2xl text-gray-50 mb-2 '>IBAN:</h2>
<InputField placeholder={account.currency +"-0000-0000-0000-0000"} isPassword={false} bind:value={receiveriban}></InputField>
<InputField placeholder={"RO00 FOXB 0"+account.currency +" 0000 0000 0000"} isPassword={false} bind:value={receiveriban}></InputField>
</div>
<div class="mx-1 flex-shrink">

63
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"
}
}
}
}
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"
}
}
}

4
client/src/utils.js

@ -0,0 +1,4 @@
export function amountToString(amount) {
amount = amount.toString().padStart(3, "0");
return amount.replace(/(.{2})$/, ".$1");
}

1
server/.vscode/launch.json vendored

@ -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",

Loading…
Cancel
Save