diff options
Diffstat (limited to 'packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx')
-rw-r--r-- | packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx | 581 |
1 files changed, 304 insertions, 277 deletions
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx index d16dc70f8..1107360bd 100644 --- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx +++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx @@ -17,26 +17,24 @@ import { AmountJson, Amounts, - buildPayto, HttpStatusCode, Logger, - parsePaytoUri, - stringifyPaytoUri, + parsePaytoUri } from "@gnu-taler/taler-util"; import { RequestError, useTranslationContext, } from "@gnu-taler/web-util/browser"; -import { h, VNode } from "preact"; +import { h, VNode, Fragment } from "preact"; import { useEffect, useRef, useState } from "preact/hooks"; -import { notifyError } from "../hooks/notification.js"; +import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; import { useAccessAPI } from "../hooks/access.js"; +import { notifyError } from "../hooks/notification.js"; import { buildRequestErrorMessage, undefinedIfEmpty, validateIBAN, } from "../utils.js"; -import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; const logger = new Logger("PaytoWireTransferForm"); @@ -72,293 +70,322 @@ export function PaytoWireTransferForm({ iban: !iban ? i18n.str`Missing IBAN` : !IBAN_REGEX.test(iban) - ? i18n.str`IBAN should have just uppercased letters and numbers` - : validateIBAN(iban, i18n), + ? i18n.str`IBAN should have just uppercased letters and numbers` + : validateIBAN(iban, i18n), subject: !subject ? i18n.str`Missing subject` : undefined, amount: !trimmedAmountStr ? i18n.str`Missing amount` : !parsedAmount - ? i18n.str`Amount is not valid` - : Amounts.isZero(parsedAmount) - ? i18n.str`Should be greater than 0` - : Amounts.cmp(limit, parsedAmount) === -1 - ? i18n.str`balance is not enough` - : undefined, + ? i18n.str`Amount is not valid` + : Amounts.isZero(parsedAmount) + ? i18n.str`Should be greater than 0` + : Amounts.cmp(limit, parsedAmount) === -1 + ? i18n.str`balance is not enough` + : undefined, }); const { createTransaction } = useAccessAPI(); - if (!isRawPayto) - return ( - <div> - <form - class="pure-form" - name="wire-transfer-form" - onSubmit={(e) => { - e.preventDefault(); - }} - autoCapitalize="none" - autoCorrect="off" - > - <label for="iban">{i18n.str`Receiver IBAN:`}</label> - <input - ref={ref} - type="text" - id="iban" - name="iban" - value={iban ?? ""} - placeholder="CC0123456789" - required - pattern={ibanRegex} - onInput={(e): void => { - setIban(e.currentTarget.value); - }} - /> - <ShowInputErrorLabel - message={errorsWire?.iban} - isDirty={iban !== undefined} - /> - <label for="subject">{i18n.str`Transfer subject:`}</label> - <input - type="text" - name="subject" - id="subject" - placeholder="subject" - value={subject ?? ""} - required - onInput={(e): void => { - setSubject(e.currentTarget.value); - }} - /> - <ShowInputErrorLabel - message={errorsWire?.subject} - isDirty={subject !== undefined} - /> - <label for="amount">{i18n.str`Amount:`}</label> - <div style={{ width: "max-content", display: "flex" }}> - <input - type="text" - readonly - class="currency-indicator" - size={limit.currency.length} - maxLength={limit.currency.length} - tabIndex={-1} - style={{ - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - borderRight: 0, - }} - value={limit.currency} - /> - <input - type="number" - name="amount" - id="amount" - placeholder="amount" - required - style={{ - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - borderLeft: 0, - width: 150, - }} - value={amount ?? ""} - onInput={(e): void => { - setAmount(e.currentTarget.value); - }} - /> - </div> - <ShowInputErrorLabel - message={errorsWire?.amount} - isDirty={amount !== undefined} - /> - <p style={{ display: "flex", justifyContent: "space-between" }}> - <input - type="submit" - class="pure-button pure-button-primary" - disabled={!!errorsWire} - value="Send" - onClick={async (e) => { - e.preventDefault(); - 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 - type="button" - class="pure-button" - value="Clear" - onClick={async (e) => { - e.preventDefault(); - setAmount(undefined); - setIban(undefined); - setSubject(undefined); - }} - /> - </p> - </form> - <p> - <a - href="#" - onClick={(e) => { - setIsRawPayto(true); - e.preventDefault(); - }} - > - {i18n.str`Want to try the raw payto://-format?`} - </a> - </p> - </div> - ); - const parsed = !rawPaytoInput ? undefined : parsePaytoUri(rawPaytoInput); const errorsPayto = undefinedIfEmpty({ rawPaytoInput: !rawPaytoInput ? i18n.str`required` : !parsed - ? i18n.str`does not follow the pattern` - : !parsed.params.amount - ? i18n.str`use the "amount" parameter to specify the amount to be transferred` - : Amounts.parse(parsed.params.amount) === undefined - ? i18n.str`the amount is not valid` - : !parsed.params.message - ? i18n.str`use the "message" parameter to specify a reference text for the transfer` - : !parsed.isKnown || parsed.targetType !== "iban" - ? i18n.str`only "IBAN" target are supported` - : !IBAN_REGEX.test(parsed.iban) - ? i18n.str`IBAN should have just uppercased letters and numbers` - : validateIBAN(parsed.iban, i18n), + ? i18n.str`does not follow the pattern` + : !parsed.params.amount + ? i18n.str`use the "amount" parameter to specify the amount to be transferred` + : Amounts.parse(parsed.params.amount) === undefined + ? i18n.str`the amount is not valid` + : !parsed.params.message + ? i18n.str`use the "message" parameter to specify a reference text for the transfer` + : !parsed.isKnown || parsed.targetType !== "iban" + ? i18n.str`only "IBAN" target are supported` + : !IBAN_REGEX.test(parsed.iban) + ? i18n.str`IBAN should have just uppercased letters and numbers` + : validateIBAN(parsed.iban, i18n), }); + // if (!isRawPayto) { + 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> + <div> + <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4"> + {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */} + <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (!isRawPayto ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}> + <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" onChange={() => { + setIsRawPayto(false) + }} /> + <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"> + <i18n.Translate>form</i18n.Translate> + </span> + </span> + </span> + </label> - return ( - <div> - <p>{i18n.str`Transfer money to account identified by payto:// URI:`}</p> - <form - class="pure-form" - name="payto-form" - onSubmit={(e) => { - e.preventDefault(); - }} - autoCapitalize="none" - autoCorrect="off" - > - <p> - <label for="address">{i18n.str`payto URI:`}</label> - <input - name="address" - type="text" - size={50} - ref={ref} - id="address" - value={rawPaytoInput ?? ""} - required - placeholder={i18n.str`payto address`} - // 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); - }} - /> - <ShowInputErrorLabel - message={errorsPayto?.rawPaytoInput} - isDirty={rawPaytoInput !== undefined} - /> - <br /> - <div style={{ fontSize: "small", marginTop: 4 }}> - Hint: - <code> - payto://iban/[receiver-iban]?message=[subject]&amount=[ - {limit.currency} - :X.Y] - </code> - </div> - </p> - <p> - <input - class="pure-button pure-button-primary" - type="button" - disabled={!!errorsPayto} - value={i18n.str`Send`} - onClick={async () => { - if (!rawPaytoInput) { - logger.error("Didn't get any raw Payto string!"); - return; - } - try { - await createTransaction({ - paytoUri: rawPaytoInput, - }); - onSuccess(); - rawPaytoInputSetter(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), - }); - } - } - }} - /> - </p> - <p> - <a - href="/account" - onClick={() => { - setIsRawPayto(false); - }} - > - {i18n.str`Use wire-transfer form?`} - </a> - </p> - </form> + {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */} + <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (isRawPayto ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}> + <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" onChange={() => { + setIsRawPayto(true) + }} /> + <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"> + <i18n.Translate>payto://</i18n.Translate> + </span> + </span> + </span> + </label> + </div> + </div> </div> - ); + + <form class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"> + <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="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" + value={iban ?? ""} + placeholder="CC0123456789" + required + pattern={ibanRegex} + onInput={(e): void => { + setIban(e.currentTarget.value); + }} + /> + <ShowInputErrorLabel + message={errorsWire?.iban} + 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"> + </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="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" + placeholder="subject" + value={subject ?? ""} + required + onInput={(e): void => { + setSubject(e.currentTarget.value); + }} + /> + <ShowInputErrorLabel + message={errorsWire?.subject} + 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"> + </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> + + <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> + <div class="mt-2"> + <input + name="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); + }} + /> + <ShowInputErrorLabel + message={errorsPayto?.rawPaytoInput} + isDirty={rawPaytoInput !== undefined} + /> + </div> + </div> + + </Fragment> + } + </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"> + <i18n.Translate>Send</i18n.Translate> + </button> + </div> + </form> + </div > + ) + // } + // return ( + // <div> + // <form + // class="pure-form" + // name="wire-transfer-form" + // onSubmit={(e) => { + // e.preventDefault(); + // }} + // autoCapitalize="none" + // autoCorrect="off" + // > + // <label for="iban">{i18n.str`Receiver IBAN:`}</label> + + // <label for="subject">{i18n.str`Transfer subject:`}</label> + + // <label for="amount">{i18n.str`Amount:`}</label> + // <div style={{ width: "max-content", display: "flex" }}> + // <input + // type="text" + // readonly + // class="currency-indicator" + // size={limit.currency.length} + // maxLength={limit.currency.length} + // tabIndex={-1} + // style={{ + // borderTopRightRadius: 0, + // borderBottomRightRadius: 0, + // borderRight: 0, + // }} + // value={limit.currency} + // /> + // <input + // type="number" + // name="amount" + // id="amount" + // placeholder="amount" + // required + // style={{ + // borderTopLeftRadius: 0, + // borderBottomLeftRadius: 0, + // borderLeft: 0, + // width: 150, + // }} + // value={amount ?? ""} + // onInput={(e): void => { + // setAmount(e.currentTarget.value); + // }} + // /> + // </div> + // <ShowInputErrorLabel + // message={errorsWire?.amount} + // isDirty={amount !== undefined} + // /> + // <p style={{ display: "flex", justifyContent: "space-between" }}> + // <input + // type="submit" + // class="pure-button pure-button-primary" + // disabled={!!errorsWire} + // value="Send" + // onClick={async (e) => { + // e.preventDefault(); + // 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 + // type="button" + // class="pure-button" + // value="Clear" + // onClick={async (e) => { + // e.preventDefault(); + // setAmount(undefined); + // setIban(undefined); + // setSubject(undefined); + // }} + // /> + // </p> + // </form> + // <p> + // <a + // href="#" + // onClick={(e) => { + // setIsRawPayto(true); + // e.preventDefault(); + // }} + // > + // {i18n.str`Want to try the raw payto://-format?`} + // </a> + // </p> + // </div> + // ); + + + + // return ( + // <div> + // <p>{i18n.str`Transfer money to account identified by payto:// URI:`}</p> + + // </div> + // ); } |