[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-merchant-backoffice] branch master updated: copying pybank's look
From: |
gnunet |
Subject: |
[taler-merchant-backoffice] branch master updated: copying pybank's look. WIP |
Date: |
Mon, 04 Apr 2022 17:55:03 +0200 |
This is an automated email from the git hooks/post-receive script.
ms pushed a commit to branch master
in repository merchant-backoffice.
The following commit(s) were added to refs/heads/master by this push:
new 1e8fbfc copying pybank's look. WIP
1e8fbfc is described below
commit 1e8fbfc481346f39cc05a7532dcfb92a8240a7d4
Author: ms <ms@taler.net>
AuthorDate: Mon Apr 4 17:52:49 2022 +0200
copying pybank's look. WIP
---
packages/bank/src/pages/home/index.tsx | 303 ++++++++++++++++++++++-----------
1 file changed, 199 insertions(+), 104 deletions(-)
diff --git a/packages/bank/src/pages/home/index.tsx
b/packages/bank/src/pages/home/index.tsx
index 070d091..03f0661 100644
--- a/packages/bank/src/pages/home/index.tsx
+++ b/packages/bank/src/pages/home/index.tsx
@@ -80,7 +80,9 @@ interface Amount {
*/
interface PageStateType {
isLoggedIn: boolean;
+ isRawPayto: boolean;
tryRegister: boolean;
+ tryManualTransfer: boolean;
showPublicHistories: boolean;
hasError: boolean;
withdrawalInProgress: boolean;
@@ -119,7 +121,7 @@ function goPublicAccounts(pageStateSetter:
StateUpdater<PageStateType>) {
* replace comma with a dot. Returns 'false' whenever
* the input is invalid, the valid amount otherwise.
*/
-function validateAmount(maybeAmount: string): string {
+function validateAmount(maybeAmount: string): any {
const amountRegex = "^[0-9]+(\.[0-9]+)?$";
if (typeof maybeAmount !== "undefined" || maybeAmount !== "") {
console.log("Maybe valid amount", maybeAmount);
@@ -278,7 +280,9 @@ function useAccountState(
function usePageState(
state: PageStateType = {
isLoggedIn: false,
+ isRawPayto: false,
tryRegister: false,
+ tryManualTransfer: false,
showPublicHistories: false,
hasError: false,
withdrawalInProgress: false,
@@ -668,14 +672,17 @@ async function registrationCall(
* Functional components. *
*************************/
-function ErrorBanner(Props: any): VNode {
+function ErrorBanner(Props: any): VNode | null {
const [pageState, pageStateSetter] = Props.pageState;
const i18n = useTranslator();
- if (!pageState.hasError) return;
+ if (!pageState.hasError) return null;
return (
<p class="informational informational-fail">{pageState.error}
<a href="#" onClick={() => {
- pageStateSetter((prevState: PageStateType) =>({...prevState, hasError:
false}))}}>
+ pageStateSetter(function (prevState: PageStateType) {
+ delete prevState.error; // delete error message
+ return {...prevState, hasError: false} // delete error state
+ })}}>
{i18n`Clear`}
</a>
</p>);
@@ -698,7 +705,7 @@ function BankFrame(Props: any): VNode {
This part of the demo shows how a bank that supports
Taler directly would work. In addition to using your own
bank account, you can also see the transaction history of
- some <a href="#" onclick={goPublicAccounts(pageStateSetter)}>Public
Accounts</a>.
+ some <a href="#" onClick={goPublicAccounts(pageStateSetter)}>Public
Accounts</a>.
</Translate></p>
</div>
<a href="https://taler.net/">
@@ -737,51 +744,120 @@ function BankFrame(Props: any): VNode {
}
function PaytoWireTransfer(Props: any): VNode {
- const {backendState, pageStateSetter} = Props;
const currency = useContext(CurrencyContext);
+ const [pageState, pageStateSetter] = useContext(PageContext); // NOTE: used
for go-back button?
const i18n = useTranslator();
const amountRegex = "^[0-9]+(\.[0-9]+)?$";
+ const ibanRegex = "^[A-Z][A-Z][0-9]+$";
var amountInput = "";
+ var receiverInput = "";
+ var subjectInput = "";
var transactionData: TransactionRequestType;
-
- return <div>
- <input
- type="text"
- placeholder="amount"
- pattern={amountRegex}
- onInput={(e): void => {
- amountInput = e.currentTarget.value;
- }} />
- <label>{currency}</label>
- <input
- type="text"
- placeholder="payto address" // changing this breaks tests.
- required
- onInput={(e): void => {
- transactionData = {
- ...transactionData,
- paytoUri: e.currentTarget.value,
- };
- }} />
- <button onClick={() => {
- amountInput = validateAmount(amountInput);
- /**
- * By invalid amounts, the validator prints error messages
- * on the console, and the browser colourizes the amount input
- * box to indicate a error.
- */
- if (!amountInput) return;
- transactionData = {
- ...transactionData,
- amount: `${currency}:${amountInput}`
- };
- createTransactionCall(
- transactionData,
- backendState,
- pageStateSetter
- );
- }}>{i18n`Create wire transfer`}</button>
- </div>
+
+ console.log("wire form page state", pageState);
+ const goBackForm = <a href="#" onClick={
+ () => pageStateSetter((prevState: PageStateType) => ({...prevState,
tryManualTransfer: false}))
+ }>{i18n`Go back`}</a>;
+ const goBackRawPayto = <a href="#" onClick={
+ () => pageStateSetter((prevState: PageStateType) => ({...prevState,
isRawPayto: false}))
+ }>{i18n`Go back`}</a>;
+ if (!pageState.isRawPayto) {
+ console.log("wire transfer form");
+ return (<article>
+ <div>
+ <h2>{i18n`Wire transfer`}</h2>
+ <p>{i18n`Transfer money to another account of this bank:`}<br /><br
/></p>
+ <div name="wire-transfer-form">
+ <input
+ type="text"
+ placeholder="receiver iban"
+ required
+ pattern={ibanRegex}
+ onInput={(e): void => {
+ receiverInput = e.currentTarget.value;
+ }} /><br /><br />
+ <input
+ type="text"
+ placeholder="subject"
+ onInput={(e): void => {
+ subjectInput = e.currentTarget.value;
+ }} /><br /><br />
+ <input
+ type="text"
+ placeholder="amount"
+ pattern={amountRegex}
+ onInput={(e): void => {
+ amountInput = e.currentTarget.value;
+ }} /> <label>{`${currency}:X.Y`}</label><br /><br />
+ <input
+ type="submit"
+ onClick={() => {
+ amountInput = validateAmount(amountInput);
+ /**
+ * By invalid amounts, the validator prints error messages
+ * on the console, and the browser colourizes the amount input
+ * box to indicate a error.
+ */
+ if (!amountInput) return;
+ if (!RegExp(ibanRegex).test(receiverInput)) return;
+ transactionData = {
+ paytoUri:
`payto://iban/${receiverInput}?message=${subjectInput}`,
+ amount: `${currency}:${amountInput}`
+ };
+ createTransactionCall(
+ transactionData,
+ Props.backendState,
+ pageStateSetter
+ );
+ }} />
+ </div>
+ <p><a
+ href="#"
+ onClick={() => {
+ console.log("switch to raw payto form");
+ pageStateSetter((prevState) => ({...prevState, isRawPayto: true}));
+ }}>{i18n`Want to try the raw payto://-format?`}
+ </a></p>
+ </div>
+ {goBackForm}
+ </article>);
+ }
+ console.log("rendering raw payto form");
+ return (<article>
+ <div>
+ <h2>{i18n`Wire transfer`}</h2>
+ <p>{i18n`Transfer money via the Payto system:`}<br /><br />
+ Address pattern: <code style="font-size: 15px">
+ payto://iban/[receiver-iban]?message=[subject]&amount=[{currency}:X.Y]
+ </code>
+ </p>
+ <div name="payto-form">
+ <input name="address"
+ size={90}
+ required
+ placeholder={i18n`payto address`}
+
pattern="payto://x-taler-bank/[a-z\.]+(:[0-9]+)?/[0-9a-zA-Z]+\?message=[a-zA-Z0-9
]+&amount={currency}:[0-9]+(\.[0-9]+)?"
+ onInput={(e): void => {
+ transactionData = {
+ ...transactionData,
+ paytoUri: e.currentTarget.value,
+ };
+ }} />
+ <input class="pure-button pure-button-primary"
+ type="submit"
+ value={i18n`Confirm`}
+ onClick={() => {
+ if (typeof transactionData.paytoUri === "undefined" ||
+ transactionData.paytoUri.length === 0) return;
+ createTransactionCall(
+ transactionData,
+ Props.backendState,
+ pageStateSetter);
+ }} />
+ </div>
+ </div>
+ <p>{goBackRawPayto}</p>
+ </article>);
}
/**
@@ -794,7 +870,11 @@ function TalerWithdrawal(Props: any): VNode {
var submitAmount = ""; // without currency.
const amountRegex = "^[0-9]+(\.[0-9]+)?$";
- var submitButton = <button
+ var submitButton = <input
+ id="select-exchange"
+ class="pure-button pure-button-primary"
+ type="submit"
+ value={i18n`Start withdrawal`}
onClick={() => {
submitAmount = validateAmount(submitAmount);
/**
@@ -807,17 +887,15 @@ function TalerWithdrawal(Props: any): VNode {
`${currency}:${submitAmount}`,
backendState,
pageStateSetter
- )}}>{i18n`Charge Taler wallet`}
- </button>;
+ )}} />;
return (<article>
<div>
<h2>{i18n`Withdraw Money into a Taler wallet`}</h2>
<div id="reserve-form"
class="pure-form"
- method="post"
name="tform">
- {i18n`Amount to withdraw`}
+ {i18n`Amount to withdraw`}:
<select id="reserve-amount" name="withdraw-amount" class="amount"
autofocus>
<option value="5.00">5.00</option>
<option value="10.00">10.00</option>
@@ -828,9 +906,9 @@ function TalerWithdrawal(Props: any): VNode {
type="text"
readonly
class="currency-indicator"
- size={balance.currency.length}
- tabindex="-1" value="{{ currency }}" />
- {submitButton}
+ size={currency.length}
+ tabIndex={-1} value={currency} />
+ {submitButton}
</div>
</div>
</article>);
@@ -885,7 +963,7 @@ function LoginForm(Props: any): VNode {
*/
function RegistrationForm(Props: any): VNode {
const [pageState, pageStateSetter] = useContext(PageContext);
- var submitData: CredentialsRequestType = {};
+ var submitData: CredentialsRequestType;
const i18n = useTranslator();
return (
@@ -924,8 +1002,10 @@ function RegistrationForm(Props: any): VNode {
<button
class="pure-button pure-button-primary"
onClick={() => {
+ console.log("maybe submitting the registration..");
if (!("password" in submitData) || !("username" in
submitData)) return;
if (submitData.password.length === 0 ||
submitData.username.length === 0) return;
+ console.log("submitting the registration..");
registrationCall(
submitData,
Props.backendStateSetter, // will store BE URL, if OK.
@@ -1000,15 +1080,41 @@ function Transactions(Props: any): VNode {
}
/**
- * Show only the account's balance.
+ * Show only the account's balance. NOTE: the backend state
+ * is mostly needed to provide the user's credentials to POST
+ * to the bank.
*/
function Account(Props: any): VNode {
+ const { accountLabel, backendState } = Props;
+ const [pageState, pageStateSetter] = useContext(PageContext);
const {
+ withdrawalInProgress,
+ tryManualTransfer,
withdrawalOutcome,
transferOutcome,
- talerWithdrawUri,
- accountLabel } = Props;
- const pageState = useContext(PageContext);
+ talerWithdrawUri } = pageState;
+ const i18n = useTranslator();
+ const logOut = (
+ <a
+ href="#"
+ class="pure-button logout-button"
+ onClick={() => {
+ setTxPageNumber(0);
+ pageStateSetter((prevState: PageStateType) => {
+ const {
+ talerWithdrawUri,
+ withdrawalOutcome,
+ withdrawalId, ...rest } = prevState;
+ return {
+ ...rest,
+ isLoggedIn: false,
+ withdrawalInProgress: false,
+ isRawPayto: false,
+ tryManualTransfer: false,
+ };
+ });
+ }}>[{i18n`Logout`}]</a>);
+
/**
* This part shows a list of transactions: with 5 elements by
* default and offers a "load more" button.
@@ -1018,30 +1124,28 @@ function Account(Props: any): VNode {
for (let i = 0; i <= txPageNumber; i++) {
txsPages.push(<Transactions accountLabel={accountLabel} pageNumber={i} />)
}
- const i18n = useTranslator();
/**
* Getting the bank account balance.
*/
const { data, error } = useSWR(`access-api/accounts/${accountLabel}`);
if (typeof error !== "undefined") {
console.log("account error", error);
-
/**
* FIXME: try only one invocation of pageStateSetter,
* after having decided the error message in the case-branch.
*/
switch(error.status) {
case 404: {
- pageState[1]((prevState) => ({
+ pageStateSetter((prevState: PageStateType) => ({
...prevState,
hasError: true,
isLoggedIn: false,
- error: i18n`Username or account label not found.`
+ error: i18n`Username or account label '${accountLabel}' not found.
Won't login.`
}));
return <p>Profile not found...</p>;
}
case 401: {
- pageState[1]((prevState) => ({
+ pageStateSetter((prevState: PageStateType) => ({
...prevState,
hasError: true,
isLoggedIn: false,
@@ -1050,7 +1154,7 @@ function Account(Props: any): VNode {
return <p>Wrong credentials...</p>;
}
default: {
- pageState[1]((prevState) => ({
+ pageStateSetter((prevState: PageStateType) => ({
...prevState,
hasError: true,
isLoggedIn: false,
@@ -1107,28 +1211,43 @@ function Account(Props: any): VNode {
</Fragment>);
}
const balance = parseAmount(data.balance.amount)
+ if (tryManualTransfer) {
+ return (
+ <BankFrame>
+ {logOut}<br />
+ <CurrencyContext.Provider value={balance.currency}>
+ <PaytoWireTransfer backendState={backendState} />
+ </CurrencyContext.Provider></BankFrame>);
+ }
return (<BankFrame>
<div>
<h1 class="nav">
<Translate>Welcome, {accountLabel}
({getIbanFromPayto(data.paytoUri)})!</Translate>
</h1>
- <a
- href="#"
- class="pure-button logout-button"
- onClick={() => {
- setTxPageNumber(0);
- pageStateSetter((prevState) => {
- const {
- talerWithdrawUri,
- withdrawalOutcome,
- withdrawalId, ...rest } = prevState;
- return {...rest, isLoggedIn: false, withdrawalInProgress: false};
- })
- }}>[{i18n`Logout`}]</a><br />
+ {logOut}<br />
</div>
<section id="menu">
<p>{i18n`Bank account balance:`} <br /> <b>{`${balance.value}
${balance.currency}`}</b></p>
</section>
+ <CurrencyContext.Provider value={balance.currency}>
+ {Props.children}
+ </CurrencyContext.Provider>
+ {
+ withdrawalInProgress && !transferOutcome &&
+ <TalerWithdrawal
+ backendState={backendState}
+ pageStateSetter={pageStateSetter} />
+ }
+ <section id="main">
+ <article>
+ <h2>{i18n`Latest transactions:`}</h2>
+ <Transactions pageNumber="0" accountLabel={accountLabel} />
+ <p><a href="#" onClick={() =>
+ pageStateSetter((prevState: PageStateType) =>
+ ({...prevState, tryManualTransfer: true}))
+ }>{i18n`Transfer money manually`}</a></p>
+ </article>
+ </section>
</BankFrame>);
}
@@ -1191,23 +1310,21 @@ function PublicHistories(Props: any): VNode {
switch(error.status) {
case 404: {
console.log("public accounts: 404", error);
- Props.pageStateSetter((prevState) => ({
+ Props.pageStateSetter((prevState: PageStateType) => ({
...prevState,
hasError: true,
showPublicHistories: false,
error: i18n`List of public accounts was not found.`
}));
- return;
}
default: {
console.log("public accounts: non-404 error", error);
- Props.pageStateSetter((prevState) => ({
+ Props.pageStateSetter((prevState: PageStateType) => ({
...prevState,
hasError: true,
showPublicHistories: false,
error: i18n`List of public accounts could not be retrieved.`
}));
- return;
}
}
}
@@ -1298,7 +1415,7 @@ export function BankHome(): VNode {
password={backendState.password}
backendUrl={backendState.url}>
<PageContext.Provider value={[pageState, pageStateSetter]}>
- <Account>
+ <Account accountLabel={backendState.username}
backendState={backendState}>
{ /**
* No action is currently being performed (page is 'pristine'):
@@ -1349,15 +1466,6 @@ export function BankHome(): VNode {
pageStateSetter);}}>{i18n`Abort withdrawal`}</button>
</div>
}
-
- { /**
- * Profile page is pristine: offer the wire transfer form.
- */
- !pageState.withdrawalInProgress &&
- !pageState.transferOutcome &&
- <PaytoWireTransfer pageStateSetter={pageStateSetter}
- backendState={backendState} />
- }
</Account>
</PageContext.Provider>
</SWRWithCredentials>
@@ -1388,16 +1496,3 @@ export function BankHome(): VNode {
</PageContext.Provider>
);
}
-
-/*
-<SWRWithoutCredentials baseUrl={getRootPath()}>
- <LoginForm
- pageStateSetter={pageStateSetter}
- backendStateSetter={backendStateSetter} />
- <p>Not a customer yet? <a onClick={() => {
- pageStateSetter((prevState) => ({...prevState, tryRegister:
true}))}}>Register!</a></p>
- <p>See our public <a onClick={() => {
- pageStateSetter((prevState) => (
- {...prevState, showPublicHistories: true}))}}>transactions</a></p>
- </SWRWithoutCredentials>
-*/
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.