diff options
Diffstat (limited to 'packages/demobank-ui/src/pages')
| -rw-r--r-- | packages/demobank-ui/src/pages/AdminPage.tsx | 5 | ||||
| -rw-r--r-- | packages/demobank-ui/src/pages/BusinessAccount.tsx | 111 | ||||
| -rw-r--r-- | packages/demobank-ui/src/pages/LoginForm.tsx | 48 | 
3 files changed, 116 insertions, 48 deletions
| diff --git a/packages/demobank-ui/src/pages/AdminPage.tsx b/packages/demobank-ui/src/pages/AdminPage.tsx index f565455bb..ac0457e9b 100644 --- a/packages/demobank-ui/src/pages/AdminPage.tsx +++ b/packages/demobank-ui/src/pages/AdminPage.tsx @@ -36,9 +36,9 @@ import {  } from "../context/pageState.js";  import { useAccountDetails } from "../hooks/access.js";  import { +  useAdminAccountAPI,    useBusinessAccountDetails,    useBusinessAccounts, -  useAdminAccountAPI,  } from "../hooks/circuit.js";  import {    buildRequestErrorMessage, @@ -50,7 +50,6 @@ import {  } from "../utils.js";  import { ErrorBannerFloat } from "./BankFrame.js";  import { ShowCashoutDetails } from "./BusinessAccount.js"; -import { PaymentOptions } from "./PaymentOptions.js";  import { PaytoWireTransferForm } from "./PaytoWireTransferForm.js";  import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js"; @@ -581,7 +580,6 @@ function CreateNewAccount({            template={undefined}            purpose="create"            onChange={(a) => { -            console.log(a);              setSubmitAccount(a);            }}          /> @@ -831,6 +829,7 @@ function RemoveAccount({              title: i18n.str`Can't delete the account`,              description: i18n.str`Balance is not empty`,            }} +          onClear={() => saveError(undefined)}          />        )}        {error && ( diff --git a/packages/demobank-ui/src/pages/BusinessAccount.tsx b/packages/demobank-ui/src/pages/BusinessAccount.tsx index 128b47114..258802e58 100644 --- a/packages/demobank-ui/src/pages/BusinessAccount.tsx +++ b/packages/demobank-ui/src/pages/BusinessAccount.tsx @@ -237,6 +237,7 @@ function CreateCashout({    if (!result.ok) return onLoadNotOk(result);    if (!ratiosResult.ok) return onLoadNotOk(ratiosResult);    const config = ratiosResult.data; +    const balance = Amounts.parseOrThrow(result.data.balance.amount);    const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);    const zero = Amounts.zeroOfCurrency(balance.currency); @@ -254,23 +255,14 @@ function CreateCashout({    if (!sellRate || sellRate < 0) return <div>error rate</div>;    const amount = Amounts.parse(`${balance.currency}:${form.amount}`); -  const amount_debit = !amount -    ? zero -    : form.isDebit -    ? amount -    : truncate(Amounts.divide(Amounts.add(amount, sellFee).amount, sellRate)); -  const credit_before_fee = !amount -    ? zero -    : form.isDebit -    ? truncate(Amounts.divide(amount, 1 / sellRate)) -    : Amounts.add(amount, sellFee).amount; -  const __amount_credit = Amounts.sub(credit_before_fee, sellFee).amount; -  const amount_credit = Amounts.parseOrThrow( -    `${fiatCurrency}:${Amounts.stringifyValue(__amount_credit)}`, -  ); +  const calc = !amount +    ? { debit: zero, credit: zero, beforeFee: zero } +    : !form.isDebit +    ? calculateFromCredit(amount, sellFee, sellRate) +    : calculateFromDebit(amount, sellFee, sellRate); -  const balanceAfter = Amounts.sub(balance, amount_debit).amount; +  const balanceAfter = Amounts.sub(balance, calc.debit).amount;    function updateForm(newForm: typeof form): void {      setForm(newForm); @@ -280,11 +272,11 @@ function CreateCashout({        ? i18n.str`required`        : !amount        ? i18n.str`could not be parsed` -      : Amounts.cmp(limit, amount_debit) === -1 +      : Amounts.cmp(limit, calc.debit) === -1        ? i18n.str`balance is not enough` -      : Amounts.cmp(credit_before_fee, sellFee) === -1 +      : Amounts.cmp(calc.beforeFee, sellFee) === -1        ? i18n.str`the total amount to transfer does not cover the fees` -      : Amounts.isZero(amount_credit) +      : Amounts.isZero(calc.credit)        ? i18n.str`the total transfer at destination will be zero`        : undefined,      channel: !form.channel ? i18n.str`required` : undefined, @@ -408,7 +400,7 @@ function CreateCashout({                id="withdraw-amount"                disabled                name="withdraw-amount" -              value={amount_debit ? Amounts.stringifyValue(amount_debit) : ""} +              value={Amounts.stringifyValue(calc.debit)}              />            </div>          </fieldset> @@ -454,7 +446,7 @@ function CreateCashout({                    // type="number"                    style={{ color: "black" }}                    disabled -                  value={Amounts.stringifyValue(credit_before_fee)} +                  value={Amounts.stringifyValue(calc.beforeFee)}                  />                </div>              </fieldset> @@ -503,7 +495,7 @@ function CreateCashout({                id="withdraw-amount"                disabled                name="withdraw-amount" -              value={amount_credit ? Amounts.stringifyValue(amount_credit) : ""} +              value={Amounts.stringifyValue(calc.credit)}              />            </div>          </fieldset> @@ -584,8 +576,10 @@ function CreateCashout({                if (errors) return;                try {                  const res = await createCashout({ -                  amount_credit: Amounts.stringify(amount_credit), -                  amount_debit: Amounts.stringify(amount_debit), +                  amount_credit: `${fiatCurrency}:${Amounts.stringifyValue( +                    calc.credit, +                  )}`, +                  amount_debit: Amounts.stringify(calc.debit),                    subject: form.subject,                    tan_channel: form.channel,                  }); @@ -630,25 +624,6 @@ function CreateCashout({    );  } -const MAX_AMOUNT_DIGIT = 2; -/** - * Truncate the amount of digits to display - * in the form based on the fee calculations - * - * Backend must have the same truncation - * @param a - * @returns - */ -function truncate(a: AmountJson): AmountJson { -  const str = Amounts.stringify(a); -  const idx = str.indexOf("."); -  if (idx === -1) { -    return a; -  } -  const truncated = str.substring(0, idx + 1 + MAX_AMOUNT_DIGIT); -  return Amounts.parseOrThrow(truncated); -} -  interface ShowCashoutProps {    id: string;    onCancel: () => void; @@ -836,6 +811,58 @@ export function ShowCashoutDetails({    );  } +const MAX_AMOUNT_DIGIT = 2; +/** + * Truncate the amount of digits to display + * in the form based on the fee calculations + * + * Backend must have the same truncation + * @param a + * @returns + */ +function truncate(a: AmountJson): AmountJson { +  const str = Amounts.stringify(a); +  const idx = str.indexOf("."); +  if (idx === -1) { +    return a; +  } +  const truncated = str.substring(0, idx + 1 + MAX_AMOUNT_DIGIT); +  return Amounts.parseOrThrow(truncated); +} + +type TransferCalculation = { +  debit: AmountJson; +  credit: AmountJson; +  beforeFee: AmountJson; +}; + +function calculateFromDebit( +  amount: AmountJson, +  sellFee: AmountJson, +  sellRate: number, +): TransferCalculation { +  const debit = amount; + +  const beforeFee = truncate(Amounts.divide(debit, 1 / sellRate)); + +  const credit = Amounts.sub(beforeFee, sellFee).amount; +  return { debit, credit, beforeFee }; +} + +function calculateFromCredit( +  amount: AmountJson, +  sellFee: AmountJson, +  sellRate: number, +): TransferCalculation { +  const credit = amount; + +  const beforeFee = Amounts.add(credit, sellFee).amount; + +  const debit = truncate(Amounts.divide(beforeFee, sellRate)); + +  return { debit, credit, beforeFee }; +} +  export function assertUnreachable(x: never): never {    throw new Error("Didn't expect to get here");  } diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx index 2452745a5..16d2373da 100644 --- a/packages/demobank-ui/src/pages/LoginForm.tsx +++ b/packages/demobank-ui/src/pages/LoginForm.tsx @@ -14,12 +14,18 @@   GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>   */ -import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; +import { +  ErrorType, +  useTranslationContext, +} from "@gnu-taler/web-util/lib/index.browser";  import { Fragment, h, VNode } from "preact";  import { useEffect, useRef, useState } from "preact/hooks";  import { useBackendContext } from "../context/backend.js"; +import { ErrorMessage } from "../context/pageState.js"; +import { useCredentialsChecker } from "../hooks/backend.js";  import { bankUiSettings } from "../settings.js";  import { undefinedIfEmpty } from "../utils.js"; +import { ErrorBannerFloat } from "./BankFrame.js";  import { USERNAME_REGEX } from "./RegistrationPage.js";  import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js"; @@ -31,6 +37,8 @@ export function LoginForm({ onRegister }: { onRegister: () => void }): VNode {    const [username, setUsername] = useState<string | undefined>();    const [password, setPassword] = useState<string | undefined>();    const { i18n } = useTranslationContext(); +  const testLogin = useCredentialsChecker(); +  const [error, saveError] = useState<ErrorMessage | undefined>();    const ref = useRef<HTMLInputElement>(null);    useEffect(function focusInput() {      ref.current?.focus(); @@ -48,6 +56,9 @@ export function LoginForm({ onRegister }: { onRegister: () => void }): VNode {    return (      <Fragment>        <h1 class="nav">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h1> +      {error && ( +        <ErrorBannerFloat error={error} onClear={() => saveError(undefined)} /> +      )}        <div class="login-div">          <form            class="login-form" @@ -105,10 +116,41 @@ export function LoginForm({ onRegister }: { onRegister: () => void }): VNode {                type="submit"                class="pure-button pure-button-primary"                disabled={!!errors} -              onClick={(e) => { +              onClick={async (e) => {                  e.preventDefault();                  if (!username || !password) return; -                backend.logIn({ username, password }); +                const { valid, cause } = await testLogin(username, password); +                if (valid) { +                  backend.logIn({ username, password }); +                } else { +                  switch (cause) { +                    case ErrorType.CLIENT: { +                      saveError({ +                        title: i18n.str`Wrong credentials or username`, +                      }); +                      break; +                    } +                    case ErrorType.SERVER: { +                      saveError({ +                        title: i18n.str`Server had a problem, try again later or report.`, +                      }); +                      break; +                    } +                    case ErrorType.TIMEOUT: { +                      saveError({ +                        title: i18n.str`Could not reach the server, please report.`, +                      }); +                      break; +                    } +                    default: { +                      saveError({ +                        title: i18n.str`Unexpected error, please report.`, +                      }); +                      break; +                    } +                  } +                  backend.logOut(); +                }                  setUsername(undefined);                  setPassword(undefined);                }} | 
