aboutsummaryrefslogtreecommitdiff
path: root/packages/bank-ui/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/bank-ui/src')
-rw-r--r--packages/bank-ui/src/hooks/preferences.ts11
-rw-r--r--packages/bank-ui/src/pages/OperationState/state.ts24
-rw-r--r--packages/bank-ui/src/pages/PaytoWireTransferForm.tsx57
-rw-r--r--packages/bank-ui/src/pages/QrCodeSection.tsx8
-rw-r--r--packages/bank-ui/src/pages/WalletWithdrawForm.tsx31
-rw-r--r--packages/bank-ui/src/pages/WithdrawalConfirmationQuestion.tsx23
-rw-r--r--packages/bank-ui/src/pages/account/ShowAccountDetails.tsx41
-rw-r--r--packages/bank-ui/src/settings.json2
-rw-r--r--packages/bank-ui/src/settings.ts16
9 files changed, 151 insertions, 62 deletions
diff --git a/packages/bank-ui/src/hooks/preferences.ts b/packages/bank-ui/src/hooks/preferences.ts
index bb3dcb153..4cb5e6a95 100644
--- a/packages/bank-ui/src/hooks/preferences.ts
+++ b/packages/bank-ui/src/hooks/preferences.ts
@@ -31,8 +31,6 @@ interface Preferences {
showWithdrawalSuccess: boolean;
showDemoDescription: boolean;
showInstallWallet: boolean;
- maxWithdrawalAmount: number;
- fastWithdrawal: boolean;
showDebugInfo: boolean;
}
@@ -41,17 +39,13 @@ export const codecForPreferences = (): Codec<Preferences> =>
.property("showWithdrawalSuccess", codecForBoolean())
.property("showDemoDescription", codecForBoolean())
.property("showInstallWallet", codecForBoolean())
- .property("fastWithdrawal", codecForBoolean())
.property("showDebugInfo", codecForBoolean())
- .property("maxWithdrawalAmount", codecForNumber())
.build("Settings");
const defaultPreferences: Preferences = {
showWithdrawalSuccess: true,
showDemoDescription: true,
showInstallWallet: true,
- maxWithdrawalAmount: 25,
- fastWithdrawal: false,
showDebugInfo: false,
};
@@ -82,7 +76,6 @@ export function usePreferences(): [
export function getAllBooleanPreferences(): Array<keyof Preferences> {
return [
- "fastWithdrawal",
"showDebugInfo",
"showDemoDescription",
"showInstallWallet",
@@ -95,16 +88,12 @@ export function getLabelForPreferences(
i18n: ReturnType<typeof useTranslationContext>["i18n"],
): TranslatedString {
switch (k) {
- case "maxWithdrawalAmount":
- return i18n.str`Max withdrawal amount`;
case "showWithdrawalSuccess":
return i18n.str`Show withdrawal confirmation`;
case "showDemoDescription":
return i18n.str`Show demo description`;
case "showInstallWallet":
return i18n.str`Show install wallet first`;
- case "fastWithdrawal":
- return i18n.str`Use fast withdrawal form`;
case "showDebugInfo":
return i18n.str`Show debug info`;
}
diff --git a/packages/bank-ui/src/pages/OperationState/state.ts b/packages/bank-ui/src/pages/OperationState/state.ts
index 19c097d18..5544c4e23 100644
--- a/packages/bank-ui/src/pages/OperationState/state.ts
+++ b/packages/bank-ui/src/pages/OperationState/state.ts
@@ -18,6 +18,7 @@ import {
Amounts,
HttpStatusCode,
TalerCoreBankErrorsByMethod,
+ TalerCorebankApi,
TalerError,
assertUnreachable,
parsePaytoUri,
@@ -33,6 +34,7 @@ import { useSessionState } from "../../hooks/session.js";
import { useBankState } from "../../hooks/bank-state.js";
import { usePreferences } from "../../hooks/preferences.js";
import { Props, State } from "./index.js";
+import { useSettingsContext } from "../../context/settings.js";
export function useComponentState({
currency,
@@ -41,7 +43,8 @@ export function useComponentState({
routeHere,
onAuthorizationRequired,
}: Props): utils.RecursiveState<State> {
- const [settings] = usePreferences();
+ const [preference] = usePreferences();
+ const settings = useSettingsContext();
const [bankState, updateBankState] = useBankState();
const { state: credentials } = useSessionState();
const creds = credentials.status !== "loggedIn" ? undefined : credentials;
@@ -52,15 +55,22 @@ export function useComponentState({
const [failure, setFailure] = useState<
TalerCoreBankErrorsByMethod<"createWithdrawal"> | undefined
>();
- const amount = settings.maxWithdrawalAmount;
+ const amount = settings.defaultSuggestedAmount;
async function doSilentStart() {
// FIXME: if amount is not enough use balance
const parsedAmount = Amounts.parseOrThrow(`${currency}:${amount}`);
if (!creds) return;
- const resp = await bank.createWithdrawal(creds, {
- amount: Amounts.stringify(parsedAmount),
- });
+ const params: TalerCorebankApi.BankAccountCreateWithdrawalRequest =
+ settings.fastWithdrawalForm
+ ? {
+ suggested_amount: Amounts.stringify(parsedAmount),
+ }
+ : {
+ amount: Amounts.stringify(parsedAmount),
+ };
+
+ const resp = await bank.createWithdrawal(creds, params);
if (resp.type === "fail") {
setFailure(resp);
return;
@@ -73,7 +83,7 @@ export function useComponentState({
if (withdrawalOperationId === undefined) {
doSilentStart();
}
- }, [settings.fastWithdrawal, amount]);
+ }, [settings.fastWithdrawalForm, amount]);
if (failure) {
return {
@@ -174,7 +184,7 @@ export function useComponentState({
}
if (data.status === "confirmed") {
- if (!settings.showWithdrawalSuccess) {
+ if (!preference.showWithdrawalSuccess) {
updateBankState("currentWithdrawalOperationId", undefined);
// onClose()
}
diff --git a/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx
index 3bf891504..0fb8c0ac1 100644
--- a/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -79,6 +79,7 @@ export function PaytoWireTransferForm({
routeHere,
onAuthorizationRequired,
limit,
+ balance,
}: Props): VNode {
const [inputType, setInputType] = useState<"form" | "payto" | "qr">("form");
const isRawPayto = inputType !== "form";
@@ -111,6 +112,16 @@ export function PaytoWireTransferForm({
? ("x-taler-bank" as const)
: ("iban" as const);
+ const wireFee =
+ config.wire_transfer_fees === undefined
+ ? Amounts.zeroOfCurrency(config.currency)
+ : Amounts.parseOrThrow(config.wire_transfer_fees);
+
+ const limitWithFee =
+ Amounts.cmp(limit, wireFee) === 1
+ ? Amounts.sub(limit, wireFee).amount
+ : Amounts.zeroOfAmount(limit);
+
const errorsWire = undefinedIfEmpty({
account: !account
? i18n.str`Required`
@@ -124,7 +135,7 @@ export function PaytoWireTransferForm({
? i18n.str`Required`
: !parsedAmount
? i18n.str`Not valid`
- : validateAmount(parsedAmount, limit, i18n),
+ : validateAmount(parsedAmount, limitWithFee, i18n),
});
const parsed = !rawPaytoInput ? undefined : parsePaytoUri(rawPaytoInput);
@@ -134,7 +145,7 @@ export function PaytoWireTransferForm({
? i18n.str`Required`
: !parsed
? i18n.str`Does not follow the pattern`
- : validateRawPayto(parsed, limit, url.host, i18n, paytoType),
+ : validateRawPayto(parsed, limitWithFee, url.host, i18n, paytoType),
});
async function doSend() {
@@ -479,9 +490,9 @@ export function PaytoWireTransferForm({
e.preventDefault();
}}
>
- <div class="p-4 sm:p-8">
+ <div class="m-4">
{!isRawPayto ? (
- <div class="grid max-w-xs grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+ <div class="grid max-w-xs grid-cols-1 gap-x-6 gap-y-8 ">
{(() => {
switch (paytoType) {
case "x-taler-bank": {
@@ -622,7 +633,45 @@ export function PaytoWireTransferForm({
</div>
</div>
)}
+ {Amounts.cmp(limitWithFee, balance) > 0 ? (
+ <p class="mt-2 text-sm text-gray-900">
+ <i18n.Translate>
+ You can transfer{" "}
+ <RenderAmount
+ value={limitWithFee}
+ spec={config.currency_specification}
+ />
+ </i18n.Translate>
+ </p>
+ ) : undefined}
</div>
+ {Amounts.isZero(wireFee) ? undefined : (
+ <div class="px-4 my-4">
+ <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+ <div class="sm:col-span-6">
+ <dl class="mt-4 space-y-4">
+ <Fragment>
+ <div class="flex items-center justify-between ">
+ <dt class="flex items-center text-sm text-gray-600">
+ <span>
+ <i18n.Translate>Cost</i18n.Translate>
+ </span>
+ </dt>
+ <dd class="text-sm text-gray-900">
+ <RenderAmount
+ value={wireFee}
+ negative
+ withColor
+ spec={config.currency_specification}
+ />
+ </dd>
+ </div>
+ </Fragment>
+ </dl>
+ </div>
+ </div>
+ </div>
+ )}
<div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
{routeCancel ? (
<a
diff --git a/packages/bank-ui/src/pages/QrCodeSection.tsx b/packages/bank-ui/src/pages/QrCodeSection.tsx
index 359d4c18f..2a21295c7 100644
--- a/packages/bank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/bank-ui/src/pages/QrCodeSection.tsx
@@ -86,10 +86,10 @@ export function QrCodeSection({
<div class="mt-4 mb-4 text-sm text-gray-500">
<p>
<i18n.Translate>
- You will see the details of the operation in your wallet
- including the fees (if applies). If you still don't have one you
- can install it following instructions in
- </i18n.Translate>{" "}
+ Your wallet will display the details of the transaction
+ including the fees (if applicable). If you do not yet have a
+ wallet, please follow the instructions on
+ </i18n.Translate>
<a
class="font-semibold text-gray-500 hover:text-gray-400"
name="wallet page"
diff --git a/packages/bank-ui/src/pages/WalletWithdrawForm.tsx b/packages/bank-ui/src/pages/WalletWithdrawForm.tsx
index a9c652643..7cf2c7881 100644
--- a/packages/bank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/bank-ui/src/pages/WalletWithdrawForm.tsx
@@ -19,6 +19,7 @@ import {
AmountJson,
Amounts,
HttpStatusCode,
+ TalerCorebankApi,
TranslatedString,
assertUnreachable,
parseWithdrawUri,
@@ -45,6 +46,7 @@ import {
RenderAmount,
doAutoFocus,
} from "./PaytoWireTransferForm.js";
+import { useSettingsContext } from "../context/settings.js";
const RefAmount = forwardRef(InputAmount);
@@ -64,7 +66,7 @@ function OldWithdrawalForm({
routeCancel: RouteDefinition;
}): VNode {
const { i18n } = useTranslationContext();
- const [settings] = usePreferences();
+ const settings = useSettingsContext();
// const walletInegrationApi = useTalerWalletIntegrationAPI()
// const { navigateTo } = useNavigationContext();
@@ -79,7 +81,7 @@ function OldWithdrawalForm({
const creds = credentials.status !== "loggedIn" ? undefined : credentials;
const [amountStr, setAmountStr] = useState<string | undefined>(
- `${settings.maxWithdrawalAmount}`,
+ `${settings.defaultSuggestedAmount ?? 1}`,
);
const [notification, notify, handleError] = useLocalNotification();
@@ -141,9 +143,15 @@ function OldWithdrawalForm({
async function doStart() {
if (!parsedAmount || !creds) return;
await handleError(async () => {
- const resp = await api.createWithdrawal(creds, {
- amount: Amounts.stringify(parsedAmount),
- });
+ const params: TalerCorebankApi.BankAccountCreateWithdrawalRequest =
+ settings.fastWithdrawalForm
+ ? {
+ suggested_amount: Amounts.stringify(parsedAmount),
+ }
+ : {
+ amount: Amounts.stringify(parsedAmount),
+ };
+ const resp = await api.createWithdrawal(creds, params);
if (resp.type === "ok") {
const uri = parseWithdrawUri(resp.body.taler_withdraw_uri);
if (!uri) {
@@ -234,9 +242,9 @@ function OldWithdrawalForm({
</i18n.Translate>
</p>
{Amounts.cmp(limit, balance) > 0 ? (
- <p class="mt-2 text-sm text-gray-500">
+ <p class="mt-2 text-sm text-gray-900">
<i18n.Translate>
- Your account allows you to withdraw{" "}
+ You can withdraw{" "}
<RenderAmount
value={limit}
spec={config.currency_specification}
@@ -340,7 +348,8 @@ export function WalletWithdrawForm({
routeCancel: RouteDefinition;
}): VNode {
const { i18n } = useTranslationContext();
- const [settings, updateSettings] = usePreferences();
+ const [pref, updatePref] = usePreferences();
+ const settings = useSettingsContext();
return (
<div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-6 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
@@ -357,11 +366,11 @@ export function WalletWithdrawForm({
</div>
<div class="col-span-2">
- {settings.showInstallWallet && (
+ {pref.showInstallWallet && (
<Attention
title={i18n.str`You need a Taler wallet`}
onClose={() => {
- updateSettings("showInstallWallet", false);
+ updatePref("showInstallWallet", false);
}}
>
<i18n.Translate>
@@ -379,7 +388,7 @@ export function WalletWithdrawForm({
</Attention>
)}
- {!settings.fastWithdrawal ? (
+ {!settings.fastWithdrawalForm ? (
<OldWithdrawalForm
focus={focus}
routeOperationDetails={routeOperationDetails}
diff --git a/packages/bank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/bank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index 853dd7bae..b270c447a 100644
--- a/packages/bank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/bank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -17,6 +17,7 @@
import {
AbsoluteTime,
AmountJson,
+ Amounts,
HttpStatusCode,
PaytoUri,
PaytoUriIBAN,
@@ -79,6 +80,11 @@ export function WithdrawalConfirmationQuestion({
lib: { bank: api },
} = useBankCoreApiContext();
+ const wireFee =
+ config.wire_transfer_fees === undefined
+ ? Amounts.zeroOfCurrency(config.currency)
+ : Amounts.parseOrThrow(config.wire_transfer_fees);
+
async function doTransfer() {
await handleError(async () => {
if (!creds) return;
@@ -357,6 +363,23 @@ export function WithdrawalConfirmationQuestion({
/>
</dd>
</div>
+ {Amounts.isZero(wireFee) ? undefined : (
+ <Fragment>
+ <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
+ <dt class="text-sm font-medium leading-6 text-gray-900">
+ <i18n.Translate>Cost</i18n.Translate>
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ <RenderAmount
+ value={wireFee}
+ negative
+ withColor
+ spec={config.currency_specification}
+ />
+ </dd>
+ </div>
+ </Fragment>
+ )}
</dl>
</div>
</div>
diff --git a/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx b/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx
index 6db0e5512..0e2144d77 100644
--- a/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx
+++ b/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx
@@ -15,6 +15,7 @@
*/
import {
AbsoluteTime,
+ AccountLetter,
HttpStatusCode,
TalerCorebankApi,
TalerError,
@@ -200,28 +201,17 @@ export function ShowAccountDetails({
}
const url = bank.getRevenueAPI(account);
- url.username = account;
const baseURL = url.href;
-
+ const revenueURL = new URL(baseURL)
+ revenueURL.username = account;
+ revenueURL.password = creds?.token ?? ""
const ac = parsePaytoUri(result.body.payto_uri);
const payto = !ac?.isKnown ? undefined : ac;
- let accountLetter: string | undefined = undefined;
- if (payto) {
- switch (payto.targetType) {
- case "iban": {
- accountLetter = `account-info-url=${url.href}\naccount-type=${payto.targetType}\niban=${payto.iban}\nreceiver-name=${result.body.name}\n`;
- break;
- }
- case "x-taler-bank": {
- accountLetter = `account-info-url=${url.href}\naccount-type=${payto.targetType}\naccount=${payto.account}\nhost=${payto.host}\nreceiver-name=${result.body.name}\n`;
- break;
- }
- case "bitcoin": {
- accountLetter = `account-info-url=${url.href}\naccount-type=${payto.targetType}\naddress=${payto.address}\nreceiver-name=${result.body.name}\n`;
- break;
- }
+ const accountLetter : AccountLetter | undefined = !payto
+ ? undefined
+ : {
+ accountURI: result.body.payto_uri, infoURL: revenueURL.href
}
- }
return (
<Fragment>
@@ -327,7 +317,7 @@ export function ShowAccountDetails({
name="account-type"
id="account-type"
disabled={true}
- value={account}
+ value={payto.targetType}
autocomplete="off"
/>
</div>
@@ -372,16 +362,16 @@ export function ShowAccountDetails({
<div class="sm:col-span-5">
<label
class="block text-sm font-medium leading-6 text-gray-900"
- for="iban"
+ for="account-name"
>
- {i18n.str`IBAN`}
+ {i18n.str`Account name`}
</label>
<div class="mt-2">
<input
type="text"
class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="iban"
- id="iban"
+ name="account-name"
+ id="account-name"
disabled={true}
value={payto.account}
autocomplete="off"
@@ -389,7 +379,7 @@ export function ShowAccountDetails({
</div>
<p class="mt-2 text-sm text-gray-500">
<i18n.Translate>
- International Bank Account Number.
+ Bank account identifier for wire transfers.
</i18n.Translate>
</p>
</div>
@@ -486,7 +476,7 @@ export function ShowAccountDetails({
<i18n.Translate>Cancel</i18n.Translate>
</a>
<CopyButton
- getContent={() => accountLetter ?? ""}
+ getContent={() => !accountLetter ? "" : JSON.stringify(accountLetter)}
class="flex text-center disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
<i18n.Translate>Copy</i18n.Translate>
@@ -498,3 +488,4 @@ export function ShowAccountDetails({
</Fragment>
);
}
+
diff --git a/packages/bank-ui/src/settings.json b/packages/bank-ui/src/settings.json
index df5fe75ce..f14168e77 100644
--- a/packages/bank-ui/src/settings.json
+++ b/packages/bank-ui/src/settings.json
@@ -2,6 +2,8 @@
"backendBaseURL": "http://bank.taler.test:1180/",
"simplePasswordForRandomAccounts": true,
"allowRandomAccountCreation": true,
+ "fastWithdrawalForm": true,
+ "defaultSuggestedAmount": 11,
"bankName": "Taler DEVELOPMENT Bank",
"topNavSites": {
"Exchange": "http://Exchnage.taler.test:1180/",
diff --git a/packages/bank-ui/src/settings.ts b/packages/bank-ui/src/settings.ts
index c085c7cd8..6d8f7b850 100644
--- a/packages/bank-ui/src/settings.ts
+++ b/packages/bank-ui/src/settings.ts
@@ -20,6 +20,7 @@ import {
canonicalizeBaseUrl,
codecForBoolean,
codecForMap,
+ codecForNumber,
codecForString,
codecOptional,
} from "@gnu-taler/taler-util";
@@ -45,6 +46,17 @@ export interface UiSettings {
// - value: link target, where the user is going to be redirected
// default: empty list
topNavSites?: Record<string, string>;
+ // Use the withdrawal form which redirect the user to the wallet
+ // without asking the amount to the user.
+ // - true: on withdrawal creation the spa will use suggested_amount instead
+ // of fixed amount
+ // - false: on withdrawal creation the spa will use fixed amount
+ // default: false
+ fastWithdrawalForm?: boolean;
+ // When the withdrawal form use the suggested amount the bank
+ // will send a default value that the user can change.
+ // default: 10
+ defaultSuggestedAmount?: number;
}
/**
@@ -56,12 +68,16 @@ const defaultSettings: UiSettings = {
simplePasswordForRandomAccounts: false,
allowRandomAccountCreation: false,
topNavSites: {},
+ fastWithdrawalForm: false,
+ defaultSuggestedAmount: 10,
};
const codecForUISettings = (): Codec<UiSettings> =>
buildCodecForObject<UiSettings>()
.property("backendBaseURL", codecOptional(codecForString()))
.property("allowRandomAccountCreation", codecOptional(codecForBoolean()))
+ .property("fastWithdrawalForm", codecOptional(codecForBoolean()))
+ .property("defaultSuggestedAmount", codecOptional(codecForNumber()))
.property(
"simplePasswordForRandomAccounts",
codecOptional(codecForBoolean()),