From 1708d49a2d5da1f325173a030695223e5a24e75c Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Fri, 29 Sep 2023 16:02:15 -0300
Subject: more ui

---
 .../src/pages/PaytoWireTransferForm.tsx            | 268 ++++++++++++---------
 1 file changed, 159 insertions(+), 109 deletions(-)

(limited to 'packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx')

diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index 5f5a6ce3b..785dc4264 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -55,10 +55,11 @@ export function PaytoWireTransferForm({
   onCancel: (() => void) | undefined;
   limit: AmountJson;
 }): VNode {
-  const [isRawPayto, setIsRawPayto] = useState(false);
-  const [iban, setIban] = useState<string | undefined>(undefined);
-  const [subject, setSubject] = useState<string | undefined>(undefined);
-  const [amount, setAmount] = useState<string | undefined>(undefined);
+  const [isRawPayto, setIsRawPayto] = useState(true);
+  // FIXME: remove this
+  const [iban, setIban] = useState<string | undefined>("DE4745461198061");
+  const [subject, setSubject] = useState<string | undefined>("ASD");
+  const [amount, setAmount] = useState<string | undefined>("1.00001");
 
   const [rawPaytoInput, rawPaytoInputSetter] = useState<string | undefined>(
     undefined,
@@ -76,17 +77,17 @@ export function PaytoWireTransferForm({
 
   const errorsWire = undefinedIfEmpty({
     iban: !iban
-      ? i18n.str`Missing IBAN`
+      ? i18n.str`required`
       : !IBAN_REGEX.test(iban)
         ? i18n.str`IBAN should have just uppercased letters and numbers`
         : validateIBAN(iban, i18n),
-    subject: !subject ? i18n.str`Missing subject` : undefined,
+    subject: !subject ? i18n.str`required` : undefined,
     amount: !trimmedAmountStr
-      ? i18n.str`Missing amount`
+      ? i18n.str`required`
       : !parsedAmount
-        ? i18n.str`Amount is not valid`
+        ? i18n.str`not valid`
         : Amounts.isZero(parsedAmount)
-          ? i18n.str`Should be greater than 0`
+          ? i18n.str`should be greater than 0`
           : Amounts.cmp(limit, parsedAmount) === -1
             ? i18n.str`balance is not enough`
             : undefined,
@@ -101,14 +102,14 @@ export function PaytoWireTransferForm({
       ? 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`
+        : !parsed.isKnown || parsed.targetType !== "iban"
+          ? i18n.str`only "IBAN" target are supported`
+          : !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`
                 : !IBAN_REGEX.test(parsed.iban)
                   ? i18n.str`IBAN should have just uppercased letters and numbers`
                   : validateIBAN(parsed.iban, i18n),
@@ -159,6 +160,9 @@ export function PaytoWireTransferForm({
   }
 
   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">
+    {/**
+     * FIXME: Scan a qr code
+     */}
     <div class="px-4 sm:px-0">
       <h2 class="text-base font-semibold leading-7 text-gray-900">
         {title}
@@ -167,6 +171,17 @@ export function PaytoWireTransferForm({
         <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-1 sm:gap-x-4">
           <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={() => {
+              if (parsed && parsed.isKnown && parsed.targetType === "iban") {
+                setIban(parsed.iban)
+                const amount = Amounts.parse(parsed.params["amount"])
+                if (amount) {
+                  setAmount(Amounts.stringifyValue(amount))
+                }
+                const subject = parsed.params["subject"]
+                if (subject) {
+                  setSubject(subject)
+                }
+              }
               setIsRawPayto(false)
             }} />
             <span class="flex flex-1">
@@ -180,12 +195,22 @@ export function PaytoWireTransferForm({
 
           <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={() => {
+              if (iban) {
+                const payto = buildPayto("iban", iban, undefined)
+                if (parsedAmount) {
+                  payto.params["amount"] = Amounts.stringify(parsedAmount)
+                }
+                if (subject) {
+                  payto.params["message"] = subject
+                }
+                rawPaytoInputSetter(stringifyPaytoUri(payto))
+              }
               setIsRawPayto(true)
             }} />
             <span class="flex flex-1">
               <span class="flex flex-col">
                 <span class="block text-sm  font-medium text-gray-900">
-                  <i18n.Translate>using the payto:// format</i18n.Translate>
+                  <i18n.Translate>Import payto:// URI</i18n.Translate>
                 </span>
               </span>
             </span>
@@ -195,7 +220,7 @@ export function PaytoWireTransferForm({
     </div>
 
     <form
-      class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
+      class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2 w-fit mx-auto"
       autoCapitalize="none"
       autoCorrect="off"
       onSubmit={e => {
@@ -203,105 +228,106 @@ export function PaytoWireTransferForm({
       }}
     >
       <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-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"
-                    name="iban"
-                    id="iban"
-                    value={iban ?? ""}
-                    placeholder="CC0123456789"
-                    autocomplete="off"
-                    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" >the receiver of the money</p>
-              </div>
+        {!isRawPayto ?
+          <div class="grid max-w-xs grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
 
-              <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
-                    onInput={(e): void => {
-                      setSubject(e.currentTarget.value);
-                    }}
-                  />
-                  <ShowInputErrorLabel
-                    message={errorsWire?.subject}
-                    isDirty={subject !== undefined}
-                  />
-                </div>
-                <p class="mt-2 text-sm text-gray-500" >some text to identify the transfer</p>
+            <div class="sm:col-span-5">
+              <label for="iban" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Recipient`}</label>
+              <div class="mt-2">
+                <input
+                  ref={focus ? doAutoFocus : undefined}
+                  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="iban"
+                  id="iban"
+                  value={iban ?? ""}
+                  placeholder="CC0123456789"
+                  autocomplete="off"
+                  required
+                  pattern={ibanRegex}
+                  onInput={(e): void => {
+                    setIban(e.currentTarget.value.toUpperCase());
+                  }}
+                />
+                <ShowInputErrorLabel
+                  message={errorsWire?.iban}
+                  isDirty={iban !== undefined}
+                />
               </div>
+              <p class="mt-2 text-sm text-gray-500" >
+                <i18n.Translate>IBAN of the recipient's account</i18n.Translate>
+              </p>
+            </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)
+            <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
+                  onInput={(e): void => {
+                    setSubject(e.currentTarget.value);
                   }}
                 />
                 <ShowInputErrorLabel
                   message={errorsWire?.subject}
                   isDirty={subject !== undefined}
                 />
-                <p class="mt-2 text-sm text-gray-500" >amount to transfer</p>
               </div>
+              <p class="mt-2 text-sm text-gray-500" >some text to identify the transfer</p>
+            </div>
 
-            </Fragment> :
-            <Fragment>
-              <div class="sm:col-span-6">
-                <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}
-                    value={rawPaytoInput ?? ""}
-                    required
-                    placeholder={i18n.str`payto://iban/[receiver-iban]?message=[subject]&amount=[${limit.currency}:X.Y]`}
-                    onInput={(e): void => {
-                      rawPaytoInputSetter(e.currentTarget.value);
-                    }}
-                  />
-                  <ShowInputErrorLabel
-                    message={errorsPayto?.rawPaytoInput}
-                    isDirty={rawPaytoInput !== undefined}
-                  />
-                </div>
-              </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"
+                left
+                currency={limit.currency}
+                value={trimmedAmountStr}
+                onChange={(d) => {
+                  setAmount(d)
+                }}
+              />
+              <ShowInputErrorLabel
+                message={errorsWire?.amount}
+                isDirty={subject !== undefined}
+              />
+              <p class="mt-2 text-sm text-gray-500" >amount to transfer</p>
+            </div>
 
-            </Fragment>
-          }
-        </div>
+          </div> :
+          <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6 w-full">
+            <div class="sm:col-span-6">
+              <label for="address" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`payto URI:`}</label>
+              <div class="mt-2">
+                <textarea
+                  ref={focus ? doAutoFocus : undefined}
+                  name="address"
+                  id="address"
+                  type="textarea"
+                  rows={3}
+                  class="block overflow-hidden w-64 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"
+                  value={rawPaytoInput ?? ""}
+                  required
+                  placeholder={i18n.str`payto://iban/[receiver-iban]?message=[subject]&amount=[${limit.currency}:X.Y]`}
+                  onInput={(e): void => {
+                    rawPaytoInputSetter(e.currentTarget.value);
+                  }}
+                />
+                <ShowInputErrorLabel
+                  message={errorsPayto?.rawPaytoInput}
+                  isDirty={rawPaytoInput !== undefined}
+                />
+              </div>
+            </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">
         {onCancel ?
@@ -328,17 +354,37 @@ export function PaytoWireTransferForm({
   )
 
 }
+
+/**
+ * Show the element when the load ended
+ * @param element 
+ */
+export function doAutoFocus(element: HTMLElement | null) {
+  if (element) {
+    window.requestIdleCallback(() => {
+      element.focus()
+      element.scrollIntoView({
+        behavior: "smooth",
+        block: "center",
+        inline: "center"
+      })
+    })
+  }
+}
+
 export function Amount(
   {
     currency,
     name,
     value,
     error,
+    left,
     onChange,
   }: {
     error?: string;
     currency: string;
     name: string;
+    left?: boolean | undefined,
     value: string | undefined;
     onChange?: (s: string) => void;
   },
@@ -346,13 +392,16 @@ export function Amount(
 ): 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">
+      <div class="flex rounded-md shadow-sm border-0 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600">
+        <div
+          class="pointer-events-none inset-y-0 flex items-center px-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"
+          data-left={left}
+          class="text-right rounded-md rounded-l-none data-[left=true]:text-left w-full py-1.5 pl-3 text-gray-900  placeholder:text-gray-400  sm:text-sm sm:leading-6"
           placeholder="0.00" aria-describedby="price-currency"
           ref={ref}
           name={name}
@@ -371,3 +420,4 @@ export function Amount(
     </div>
   );
 }
+
-- 
cgit v1.2.3

