aboutsummaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx')
-rw-r--r--packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx221
1 files changed, 151 insertions, 70 deletions
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index 1107360bd..5e0624cbf 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -19,17 +19,21 @@ import {
Amounts,
HttpStatusCode,
Logger,
- parsePaytoUri
+ TranslatedString,
+ buildPayto,
+ parsePaytoUri,
+ stringifyPaytoUri
} from "@gnu-taler/taler-util";
import {
RequestError,
+ notify,
+ notifyError,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
-import { h, VNode, Fragment } from "preact";
+import { h, VNode, Fragment, Ref } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
import { useAccessAPI } from "../hooks/access.js";
-import { notifyError } from "../hooks/notification.js";
import {
buildRequestErrorMessage,
undefinedIfEmpty,
@@ -41,10 +45,12 @@ const logger = new Logger("PaytoWireTransferForm");
export function PaytoWireTransferForm({
focus,
onSuccess,
+ onCancel,
limit,
}: {
focus?: boolean;
onSuccess: () => void;
+ onCancel: (() => void) | undefined;
limit: AmountJson;
}): VNode {
const [isRawPayto, setIsRawPayto] = useState(false);
@@ -105,7 +111,51 @@ export function PaytoWireTransferForm({
? i18n.str`IBAN should have just uppercased letters and numbers`
: validateIBAN(parsed.iban, i18n),
});
- // if (!isRawPayto) {
+
+ async function doSend() {
+ let paytoUri: string | undefined;
+
+ if (rawPaytoInput) {
+ paytoUri = rawPaytoInput
+ } else {
+ if (!iban || !subject) return;
+ const ibanPayto = buildPayto("iban", iban, undefined);
+ ibanPayto.params.message = encodeURIComponent(subject);
+ paytoUri = stringifyPaytoUri(ibanPayto);
+ }
+
+ try {
+ await createTransaction({
+ paytoUri,
+ amount: `${limit.currency}:${amount}`,
+ });
+ onSuccess();
+ setAmount(undefined);
+ setIban(undefined);
+ setSubject(undefined);
+ rawPaytoInputSetter(undefined)
+ } catch (error) {
+ if (error instanceof RequestError) {
+ notify(
+ buildRequestErrorMessage(i18n, error.cause, {
+ onClientError: (status) =>
+ status === HttpStatusCode.BadRequest
+ ? i18n.str`The request was invalid or the payto://-URI used unacceptable features.`
+ : undefined,
+ }),
+ );
+ } else {
+ notifyError(
+ i18n.str`Operation failed, please report`,
+ (error instanceof Error
+ ? error.message
+ : JSON.stringify(error)) as TranslatedString
+ )
+}
+ }
+
+ }
+
return (<div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
<div class="px-4 sm:px-0">
<h2 class="text-base font-semibold leading-7 text-gray-900"><i18n.Translate>Transfer details</i18n.Translate></h2>
@@ -118,7 +168,7 @@ export function PaytoWireTransferForm({
}} />
<span class="flex flex-1">
<span class="flex flex-col">
- <span id="project-type-0-label" class="block text-sm font-medium text-gray-900">
+ <span class="block text-sm font-medium text-gray-900">
<i18n.Translate>form</i18n.Translate>
</span>
</span>
@@ -133,7 +183,7 @@ export function PaytoWireTransferForm({
}} />
<span class="flex flex-1">
<span class="flex flex-col">
- <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
+ <span class="block text-sm font-medium text-gray-900">
<i18n.Translate>payto://</i18n.Translate>
</span>
</span>
@@ -143,23 +193,31 @@ export function PaytoWireTransferForm({
</div>
</div>
- <form class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2">
+ <form
+ class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
+ autoCapitalize="none"
+ autoCorrect="off"
+ onSubmit={e => {
+ e.preventDefault()
+ }}
+ >
<div class="px-4 py-6 sm:p-8">
<div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
{!isRawPayto ?
<Fragment>
- <div class="sm:col-span-3">
- <label for="first-name" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Account number`}</label>
+ <div class="sm:col-span-5">
+ <label for="iban" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Account number`}</label>
<div class="mt-2">
<input
ref={ref}
type="text"
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- id="iban"
name="iban"
+ id="iban"
value={iban ?? ""}
placeholder="CC0123456789"
+ autocomplete="off"
required
pattern={ibanRegex}
onInput={(e): void => {
@@ -171,21 +229,18 @@ export function PaytoWireTransferForm({
isDirty={iban !== undefined}
/>
</div>
- <p class="mt-2 text-sm text-gray-500" id="email-description">the receiver of the money</p>
- </div>
-
- <div class="sm:col-span-3">
+ <p class="mt-2 text-sm text-gray-500" >the receiver of the money</p>
</div>
- <div class="sm:col-span-3">
- <label for="first-name" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Transfer subject`}</label>
+ <div class="sm:col-span-5">
+ <label for="subject" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Transfer subject`}</label>
<div class="mt-2">
-
<input
type="text"
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
name="subject"
id="subject"
+ autocomplete="off"
placeholder="subject"
value={subject ?? ""}
required
@@ -198,37 +253,40 @@ export function PaytoWireTransferForm({
isDirty={subject !== undefined}
/>
</div>
- <p class="mt-2 text-sm text-gray-500" id="email-description">some text to identify the transfer</p>
-
- </div>
-
- <div class="sm:col-span-3">
+ <p class="mt-2 text-sm text-gray-500" >some text to identify the transfer</p>
</div>
- <div class="sm:col-span-3">
- <label for="first-name" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Amount`}</label>
- <div class="mt-2">
- <input type="text" name="first-name" id="first-name" autocomplete="given-name" class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" />
- </div>
+ <div class="sm:col-span-5">
+ <label for="amount" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Amount`}</label>
+ <Amount
+ name="amount"
+ currency={limit.currency}
+ value={trimmedAmountStr}
+ onChange={(d) => {
+ setAmount(d)
+ }}
+ />
+ <ShowInputErrorLabel
+ message={errorsWire?.subject}
+ isDirty={subject !== undefined}
+ />
+ <p class="mt-2 text-sm text-gray-500" >amount to transfer</p>
</div>
- <div class="sm:col-span-3">
- </div>
</Fragment> :
<Fragment>
<div class="sm:col-span-6">
- <label for="first-name" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`payto URI:`}</label>
+ <label for="address" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`payto URI:`}</label>
<div class="mt-2">
<input
name="address"
+ id="address"
type="text"
size={50}
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" ref={ref}
- id="address"
value={rawPaytoInput ?? ""}
required
placeholder={i18n.str`payto://iban/[receiver-iban]?message=[subject]&amount=[${limit.currency}:X.Y]`}
- // pattern={`payto://iban/[A-Z][A-Z][0-9]+?message=[a-zA-Z0-9 ]+&amount=${currency}:[0-9]+(.[0-9]+)?`}
onInput={(e): void => {
rawPaytoInputSetter(e.currentTarget.value);
}}
@@ -244,9 +302,23 @@ export function PaytoWireTransferForm({
}
</div>
</div>
- <div class="flex items-center justify-end gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- <button type="button" class="text-sm font-semibold leading-6 text-gray-900">Cancel</button>
- <button type="submit" class="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">
+ <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
+ {onCancel ?
+ <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
+ onClick={onCancel}
+ >
+ <i18n.Translate>Cancel</i18n.Translate>
+ </button>
+ : <div />
+ }
+ <button type="submit"
+ class="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"
+ disabled={isRawPayto ? !!errorsPayto : !!errorsWire}
+ onClick={(e) => {
+ e.preventDefault()
+ doSend()
+ }}
+ >
<i18n.Translate>Send</i18n.Translate>
</button>
</div>
@@ -262,8 +334,6 @@ export function PaytoWireTransferForm({
// onSubmit={(e) => {
// e.preventDefault();
// }}
- // autoCapitalize="none"
- // autoCorrect="off"
// >
// <label for="iban">{i18n.str`Receiver IBAN:`}</label>&nbsp;
@@ -318,39 +388,7 @@ export function PaytoWireTransferForm({
// if (!(iban && subject && amount)) {
// return;
// }
- // const ibanPayto = buildPayto("iban", iban, undefined);
- // ibanPayto.params.message = encodeURIComponent(subject);
- // const paytoUri = stringifyPaytoUri(ibanPayto);
-
- // try {
- // await createTransaction({
- // paytoUri,
- // amount: `${limit.currency}:${amount}`,
- // });
- // onSuccess();
- // setAmount(undefined);
- // setIban(undefined);
- // setSubject(undefined);
- // } catch (error) {
- // if (error instanceof RequestError) {
- // notifyError(
- // buildRequestErrorMessage(i18n, error.cause, {
- // onClientError: (status) =>
- // status === HttpStatusCode.BadRequest
- // ? i18n.str`The request was invalid or the payto://-URI used unacceptable features.`
- // : undefined,
- // }),
- // );
- // } else {
- // notifyError({
- // title: i18n.str`Operation failed, please report`,
- // description:
- // error instanceof Error
- // ? error.message
- // : JSON.stringify(error),
- // });
- // }
- // }
+
// }}
// />
// <input
@@ -389,3 +427,46 @@ export function PaytoWireTransferForm({
// </div>
// );
}
+export function Amount(
+ {
+ currency,
+ name,
+ value,
+ error,
+ onChange,
+ }: {
+ error?: string;
+ currency: string;
+ name: string;
+ value: string | undefined;
+ onChange?: (s: string) => void;
+ },
+ ref: Ref<HTMLInputElement>,
+): VNode {
+ return (
+ <div class="mt-2">
+ <div class="relative rounded-md shadow-sm">
+ <div class="pointer-events-none absolute inset-y-0 flex items-center pl-3">
+ <span class="text-gray-500 sm:text-sm">{currency}</span>
+ </div>
+ <input
+ type="number"
+ class="text-right block w-full rounded-md border-0 py-1.5 pl-16 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+ placeholder="0.00" aria-describedby="price-currency"
+ ref={ref}
+ name={name}
+ id={name}
+ autocomplete="off"
+ value={value ?? ""}
+ disabled={!onChange}
+ onInput={(e): void => {
+ if (onChange) {
+ onChange(e.currentTarget.value);
+ }
+ }}
+ />
+ </div>
+ <ShowInputErrorLabel message={error} isDirty={value !== undefined} />
+ </div>
+ );
+}