[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-wallet-core] branch master updated (779ddae8b -> 1708d49a2)
From: |
gnunet |
Subject: |
[taler-wallet-core] branch master updated (779ddae8b -> 1708d49a2) |
Date: |
Fri, 29 Sep 2023 21:02:42 +0200 |
This is an automated email from the git hooks/post-receive script.
sebasjm pushed a change to branch master
in repository wallet-core.
from 779ddae8b iban country code should be always uppercased
new c10f3f3ad notifications exposed
new 1708d49a2 more ui
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
packages/demobank-ui/dev.mjs | 2 +-
packages/demobank-ui/src/components/Attention.tsx | 59 +++++
.../demobank-ui/src/components/ErrorLoading.tsx | 22 +-
.../src/components/Transactions/views.tsx | 51 ++--
packages/demobank-ui/src/demobank-ui-settings.js | 21 ++
packages/demobank-ui/src/hooks/access.ts | 3 +-
packages/demobank-ui/src/hooks/circuit.ts | 2 +-
packages/demobank-ui/src/hooks/settings.ts | 3 +
.../demobank-ui/src/pages/AccountPage/views.tsx | 62 ++---
packages/demobank-ui/src/pages/BankFrame.tsx | 104 ++++----
packages/demobank-ui/src/pages/HomePage.tsx | 6 +-
packages/demobank-ui/src/pages/LoginForm.tsx | 8 +-
.../demobank-ui/src/pages/OperationState/state.ts | 4 +-
.../demobank-ui/src/pages/OperationState/views.tsx | 5 +-
packages/demobank-ui/src/pages/PaymentOptions.tsx | 5 +-
.../src/pages/PaytoWireTransferForm.tsx | 268 ++++++++++++---------
packages/demobank-ui/src/pages/QrCodeSection.tsx | 1 -
.../src/pages/UpdateAccountPassword.tsx | 8 +-
.../demobank-ui/src/pages/WalletWithdrawForm.tsx | 183 +++++---------
.../src/pages/WithdrawalConfirmationQuestion.tsx | 14 +-
.../demobank-ui/src/pages/WithdrawalQRCode.tsx | 12 +-
.../demobank-ui/src/pages/admin/AccountForm.tsx | 8 +-
packages/demobank-ui/src/pages/admin/Home.tsx | 2 +
.../demobank-ui/src/pages/admin/RemoveAccount.tsx | 65 +----
packages/web-util/src/hooks/index.ts | 10 +-
packages/web-util/src/hooks/useNotifications.ts | 2 +-
26 files changed, 450 insertions(+), 480 deletions(-)
create mode 100644 packages/demobank-ui/src/components/Attention.tsx
create mode 100644 packages/demobank-ui/src/demobank-ui-settings.js
diff --git a/packages/demobank-ui/dev.mjs b/packages/demobank-ui/dev.mjs
index 9c09e5716..f29a05e49 100755
--- a/packages/demobank-ui/dev.mjs
+++ b/packages/demobank-ui/dev.mjs
@@ -18,7 +18,7 @@
import { serve } from "@gnu-taler/web-util/node";
import { initializeDev } from "@gnu-taler/web-util/build";
-const devEntryPoints = ["src/stories.tsx", "src/index.tsx"];
+const devEntryPoints = ["src/stories.tsx", "src/index.tsx",
"src/demobank-ui-settings.js"];
const build = initializeDev({
type: "development",
diff --git a/packages/demobank-ui/src/components/Attention.tsx
b/packages/demobank-ui/src/components/Attention.tsx
new file mode 100644
index 000000000..3313e5796
--- /dev/null
+++ b/packages/demobank-ui/src/components/Attention.tsx
@@ -0,0 +1,59 @@
+import { TranslatedString } from "@gnu-taler/taler-util";
+import { ComponentChildren, Fragment, VNode, h } from "preact";
+import { assertUnreachable } from "./Routing.js";
+
+interface Props {
+ type?: "info" | "success" | "warning" | "danger",
+ onClose?: () => void,
+ title: TranslatedString,
+ children?: ComponentChildren ,
+}
+export function Attention({ type = "info", title, children, onClose }: Props):
VNode {
+ return <div class={`group attention-${type} mt-2`}>
+ <div class="rounded-md group-[.attention-info]:bg-blue-50
group-[.attention-warning]:bg-yellow-50 group-[.attention-danger]:bg-red-50
group-[.attention-success]:bg-green-50 p-4 shadow">
+ <div class="flex">
+ <div >
+ <svg xmlns="http://www.w3.org/2000/svg" stroke="none" viewBox="0 0
24 24" fill="currentColor" class="w-8 h-8 group-[.attention-info]:text-blue-400
group-[.attention-warning]:text-yellow-400
group-[.attention-danger]:text-red-400
group-[.attention-success]:text-green-400">
+ {(() => {
+ switch (type) {
+ case "info":
+ return <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8
0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0
01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25
0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" />
+ case "warning":
+ return <path fill-rule="evenodd" d="M9.401 3.003c1.155-2
4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309
0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5
0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z" />
+ case "danger":
+ return <path fill-rule="evenodd" d="M2.25 12c0-5.385
4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25
12zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0
8.25a.75.75 0 100-1.5.75.75 0 000 1.5z" />
+ case "success":
+ return <path fill-rule="evenodd" d="M7.493 18.75c-.425
0-.82-.236-.975-.632A7.48 7.48 0 016 15.375c0-1.75.599-3.358
1.602-4.634.151-.192.373-.309.6-.397.473-.183.89-.514 1.212-.924a9.042 9.042 0
012.861-2.4c.723-.384 1.35-.956 1.653-1.715a4.498 4.498 0 00.322-1.672V3a.75.75
0 01.75-.75 2.25 2.25 0 012.25 2.25c0 1.152-.26 2.243-.723 3.218-.266.558.107
1.282.725 1.282h3.126c1.026 0 1.945.694 2.054 1.715.045.422.068.85.068
1.285a11.95 11.95 0 01-2.649 7.521c-.388.482-.987. [...]
+ default:
+ assertUnreachable(type)
+ }
+ })()}
+ </svg>
+ </div>
+ <div class="ml-3 w-full">
+ <h3 class="text-sm group-hover:text-white font-bold
group-[.attention-info]:text-blue-800 group-[.attention-success]:text-green-800
group-[.attention-warning]:text-yellow-800
group-[.attention-danger]:text-red-800">
+ {title}
+ </h3>
+ <div class="mt-2 text-sm group-[.attention-info]:text-blue-700
group-[.attention-warning]:text-yellow-700
group-[.attention-danger]:text-red-700
group-[.attention-success]:text-green-700">
+ {children}
+ </div>
+ </div>
+ {onClose &&
+ <div>
+ <button type="button" class="font-semibold items-center rounded
bg-transparent px-2 py-1 text-xs text-gray-900 hover:bg-gray-50"
+ onClick={(e) => {
+ e.preventDefault();
+ onClose();
+ }}
+ >
+ <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"
aria-hidden="true">
+ <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72
3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06
10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
+ </svg>
+ </button>
+ </div>
+ }
+ </div>
+ </div>
+
+ </div>
+}
diff --git a/packages/demobank-ui/src/components/ErrorLoading.tsx
b/packages/demobank-ui/src/components/ErrorLoading.tsx
index f83b61234..ee62671ce 100644
--- a/packages/demobank-ui/src/components/ErrorLoading.tsx
+++ b/packages/demobank-ui/src/components/ErrorLoading.tsx
@@ -17,25 +17,13 @@
import { HttpError, useTranslationContext } from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
+import { Attention } from "./Attention.js";
+import { TranslatedString } from "@gnu-taler/taler-util";
export function ErrorLoading({ error }: { error:
HttpError<SandboxBackend.SandboxError> }): VNode {
const { i18n } = useTranslationContext()
- return (
- <div><div class="rounded-md bg-red-50 p-4">
- <div class="flex">
- <div class="flex-shrink-0">
- <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000
16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10
11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0
00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
- </svg>
- </div>
- <div class="ml-3 flex-1 md:flex md:justify-between">
- <p class="text-sm font-medium text-red-800">{error.message}</p>
- </div>
- </div>
- <div class="ml-3 flex-1 md:flex md:justify-between">
- <p class="text-sm font-medium text-red-800">Got status
"{error.info.status}" on {error.info.url}</p>
- </div>
- </div>
- </div>
+ return (<Attention type="danger" title={error.message as TranslatedString}>
+ <p class="text-sm font-medium text-red-800">Got status
"{error.info.status}" on {error.info.url}</p>
+ </Attention>
);
}
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx
b/packages/demobank-ui/src/components/Transactions/views.tsx
index f8b2e3113..f92c874f3 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -19,6 +19,7 @@ import { useTranslationContext } from
"@gnu-taler/web-util/browser";
import { State } from "./index.js";
import { format, isToday } from "date-fns";
import { Amounts } from "@gnu-taler/taler-util";
+import { useEffect, useRef } from "preact/hooks";
export function LoadingUriView({ error }: State.LoadingUriError): VNode {
const { i18n } = useTranslationContext();
@@ -55,9 +56,9 @@ export function ReadyView({ transactions, onNext, onPrev }:
State.Ready): VNode
<thead>
<tr>
<th scope="col" class="pl-2 py-3.5 text-left text-sm
font-semibold text-gray-900 ">{i18n.str`Date`}</th>
- <th scope="col" class="pl-2 py-3.5 text-left text-sm
font-semibold text-gray-900 lg:table-cell">{i18n.str`Amount`}</th>
- <th scope="col" class="pl-2 py-3.5 text-left text-sm
font-semibold text-gray-900 lg:table-cell">{i18n.str`Counterpart`}</th>
- <th scope="col" class="pl-2 py-3.5 text-left text-sm
font-semibold text-gray-900 lg:table-cell">{i18n.str`Subject`}</th>
+ <th scope="col" class="hidden sm:table-cell pl-2 py-3.5
text-left text-sm font-semibold text-gray-900 ">{i18n.str`Amount`}</th>
+ <th scope="col" class="hidden sm:table-cell pl-2 py-3.5
text-left text-sm font-semibold text-gray-900 ">{i18n.str`Counterpart`}</th>
+ <th scope="col" class="pl-2 py-3.5 text-left text-sm
font-semibold text-gray-900 ">{i18n.str`Subject`}</th>
</tr>
</thead>
<tbody>
@@ -69,22 +70,38 @@ export function ReadyView({ transactions, onNext, onPrev }:
State.Ready): VNode
</th>
</tr>
{txs.map(item => {
+ const time = item.when.t_ms === "never" ? "" :
format(item.when.t_ms, "HH:mm:ss")
+ const amount = <Fragment>
+ {item.negative ? "-" : ""}
+ {item.amount ? (
+ `${Amounts.stringifyValue(item.amount)}
${item.amount.currency
+ }`
+ ) : (
+ <span style={{ color: "grey" }}><{i18n.str`invalid
value`}></span>
+ )}
+ </Fragment>
return (<tr key={idx}>
<td class="relative py-2 pl-2 pr-2 text-sm ">
- <div class="font-medium text-gray-900">{item.when.t_ms
=== "never"
- ? ""
- : format(item.when.t_ms, "HH:mm:ss")}</div>
+ <div class="font-medium text-gray-900">{time}</div>
+ <dl class="font-normal sm:hidden">
+ <dt class="sr-only
sm:hidden"><i18n.Translate>Amount</i18n.Translate></dt>
+ <dd class="mt-1 truncate text-gray-700">
+ {item.negative ? i18n.str`sent` :
i18n.str`received`} {item.amount ? (
+ `${Amounts.stringifyValue(item.amount)}`
+ ) : (
+ <span style={{ color: "grey"
}}><{i18n.str`invalid value`}></span>
+ )}</dd>
+ <dt class="sr-only
sm:hidden"><i18n.Translate>Counterpart</i18n.Translate></dt>
+ <dd class="mt-1 truncate text-gray-500 sm:hidden">
+ {item.negative ? i18n.str`to` : i18n.str`from`}
{item.counterpart}
+ </dd>
+ </dl>
</td>
<td data-negative={item.negative ? "true" : "false"}
- class="px-3 py-3.5 text-sm text-gray-500
data-[negative=false]:text-green-600 data-[negative=true]:text-red-600">
- {item.negative ? "-" : ""}
- {item.amount ? (
- `${Amounts.stringifyValue(item.amount)}
${item.amount.currency
- }`
- ) : (
- <span style={{ color: "grey" }}><{i18n.str`invalid
value`}></span>
- )}</td>
- <td class="px-3 py-3.5 text-sm
text-gray-500">{item.counterpart}</td>
+ class="hidden sm:table-cell px-3 py-3.5 text-sm
text-gray-500 data-[negative=false]:text-green-600
data-[negative=true]:text-red-600">
+ {amount}
+ </td>
+ <td class="hidden sm:table-cell px-3 py-3.5 text-sm
text-gray-500">{item.counterpart}</td>
<td class="px-3 py-3.5 text-sm text-gray-500 break-all
min-w-md">{item.subject}</td>
</tr>)
})}
@@ -94,8 +111,8 @@ export function ReadyView({ transactions, onNext, onPrev }:
State.Ready): VNode
</tbody>
</table>
-
- <nav class="flex items-center justify-between border-t border-gray-200
bg-white px-4 py-3 sm:px-6 rounded-lg" aria-label="Pagination">
+
+ <nav class="flex items-center justify-between border-t border-gray-200
bg-white px-4 py-3 sm:px-6 rounded-lg" aria-label="Pagination">
<div class="flex flex-1 justify-between sm:justify-end">
<button
class="relative disabled:bg-gray-100 disabled:text-gray-500
inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold
text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50
focus-visible:outline-offset-0"
diff --git a/packages/demobank-ui/src/demobank-ui-settings.js
b/packages/demobank-ui/src/demobank-ui-settings.js
new file mode 100644
index 000000000..8a0961831
--- /dev/null
+++ b/packages/demobank-ui/src/demobank-ui-settings.js
@@ -0,0 +1,21 @@
+// Values for development environment
+
+/**
+ * Global settings for the demobank UI.
+ */
+localStorage.setItem("bank-base-url", "http://bank.taler.test/");
+
+globalThis.talerDemobankSettings = {
+ backendBaseURL: "http://bank.taler.test/",
+ allowRegistrations: true,
+ showDemoNav: true,
+ simplePasswordForRandomAccounts: true,
+ allowRandomAccountCreation: true,
+ bankName: "Taler DEVELOPMENT Bank",
+ // Names and links for other demo sites to show in the navbar
+ demoSites: [
+ ["Exchange", "https://Exchnage.taler.test/"],
+ ["Bank", "https://bank-ui.taler.test/"],
+ ["Merchant", "https://merchant.taler.test/"],
+ ],
+};
diff --git a/packages/demobank-ui/src/hooks/access.ts
b/packages/demobank-ui/src/hooks/access.ts
index 20fd64bfa..154c43ae6 100644
--- a/packages/demobank-ui/src/hooks/access.ts
+++ b/packages/demobank-ui/src/hooks/access.ts
@@ -70,7 +70,7 @@ export function useAccessAPI(): AccessAPI {
contentType: "json",
},
);
- await mutateAll(/.*accounts\/.*\/transactions.*/);
+ await mutateAll(/.*accounts\/.*/);
return res;
};
const deleteAccount = async (): Promise<HttpResponseOk<void>> => {
@@ -382,7 +382,6 @@ export function useTransactions(
loadMore: () => {
if (!afterData || isReachingEnd) return;
// if (afterData.data.transactions.length < MAX_RESULT_SIZE) {
- // console.log("load more", page)
const l =
afterData.data.transactions[afterData.data.transactions.length-1]
setStart(String(l.row_id));
// }
diff --git a/packages/demobank-ui/src/hooks/circuit.ts
b/packages/demobank-ui/src/hooks/circuit.ts
index 82caafdf2..5dba60951 100644
--- a/packages/demobank-ui/src/hooks/circuit.ts
+++ b/packages/demobank-ui/src/hooks/circuit.ts
@@ -435,7 +435,7 @@ export function useBusinessAccounts(
HttpResponseOk<SandboxBackend.Circuit.CircuitAccounts>,
RequestError<SandboxBackend.SandboxError>
>(
- [`circuit-api/accounts`, args?.page, PAGE_SIZE, args?.account],
+ [`accounts`, args?.page, PAGE_SIZE, args?.account],
sandboxAccountsFetcher,
{
refreshInterval: 0,
diff --git a/packages/demobank-ui/src/hooks/settings.ts
b/packages/demobank-ui/src/hooks/settings.ts
index 5f004c6d4..ad853f9d7 100644
--- a/packages/demobank-ui/src/hooks/settings.ts
+++ b/packages/demobank-ui/src/hooks/settings.ts
@@ -33,6 +33,7 @@ interface Settings {
showInstallWallet: boolean;
maxWithdrawalAmount: number;
fastWithdrawal: boolean;
+ showDebugInfo: boolean;
}
export const codecForSettings = (): Codec<Settings> =>
@@ -42,6 +43,7 @@ export const codecForSettings = (): Codec<Settings> =>
.property("showDemoDescription", (codecForBoolean()))
.property("showInstallWallet", (codecForBoolean()))
.property("fastWithdrawal", (codecForBoolean()))
+ .property("showDebugInfo", (codecForBoolean()))
.property("maxWithdrawalAmount", codecForNumber())
.build("Settings");
@@ -52,6 +54,7 @@ const defaultSettings: Settings = {
showInstallWallet: true,
maxWithdrawalAmount: 25,
fastWithdrawal: false,
+ showDebugInfo: false,
};
const DEMOBANK_SETTINGS_KEY = buildStorageKey(
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx
b/packages/demobank-ui/src/pages/AccountPage/views.tsx
index 83846be90..483cb579a 100644
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx
@@ -16,10 +16,10 @@
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
+import { Attention } from "../../components/Attention.js";
import { Transactions } from "../../components/Transactions/index.js";
import { useBusinessAccountDetails } from "../../hooks/circuit.js";
import { useSettings } from "../../hooks/settings.js";
-import { bankUiSettings } from "../../settings.js";
import { PaymentOptions } from "../PaymentOptions.js";
import { State } from "./index.js";
@@ -31,53 +31,27 @@ export function InvalidIbanView({ error }:
State.InvalidIban) {
const IS_PUBLIC_ACCOUNT_ENABLED = false
-
function ShowDemoInfo(): VNode {
const { i18n } = useTranslationContext();
const [settings, updateSettings] = useSettings();
if (!settings.showDemoDescription) return <Fragment />
- return <div class="rounded-md bg-blue-50 p-4">
- <div class="flex">
- <div class="flex-shrink-0">
- <svg class="h-5 w-5 text-blue-400" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
- <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116
0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0
01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25
0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
- </svg>
- </div>
- <div class="ml-3">
- <h3 class="text-sm font-bold text-blue-800">
- <i18n.Translate>This is a demo bank!</i18n.Translate>
- </h3>
- <div class="mt-2 text-sm text-blue-700">
- {IS_PUBLIC_ACCOUNT_ENABLED ? (
- <i18n.Translate>
- This part of the demo shows how a bank that supports Taler
- directly would work. In addition to using your own bank
- account, you can also see the transaction history of some{" "}
- <a href="/public-accounts">Public Accounts</a>.
- </i18n.Translate>
- ) : (
- <i18n.Translate>
- This part of the demo shows how a bank that supports Taler
- directly would work.
- </i18n.Translate>
- )}
- <p class="mt-3 text-sm flex justify-end">
- <button type="button" class="inline-flex font-semibold
items-center rounded bg-white px-2 py-1 text-xs text-gray-900 shadow-sm ring-1
ring-inset ring-gray-300 hover:bg-gray-50"
- onClick={(e) => {
- e.preventDefault();
- updateSettings("showDemoDescription", false);
- }}
- >
- <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"
aria-hidden="true">
- <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72
3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06
10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
- </svg>
- </button>
- </p>
-
- </div>
- </div>
- </div>
- </div>
+ return <Attention title={i18n.str`This is a demo bank`} onClose={() => {
+ updateSettings("showDemoDescription", false);
+ }}>
+ {IS_PUBLIC_ACCOUNT_ENABLED ? (
+ <i18n.Translate>
+ This part of the demo shows how a bank that supports Taler
+ directly would work. In addition to using your own bank
+ account, you can also see the transaction history of some{" "}
+ <a href="/public-accounts">Public Accounts</a>.
+ </i18n.Translate>
+ ) : (
+ <i18n.Translate>
+ This part of the demo shows how a bank that supports Taler
+ directly would work.
+ </i18n.Translate>
+ )}
+ </Attention>
}
export function ReadyView({ account, limit, goToBusinessAccount,
goToConfirmOperation }: State.Ready): VNode<{}> {
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx
b/packages/demobank-ui/src/pages/BankFrame.tsx
index 15ef8a036..29334cae4 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -15,7 +15,7 @@
*/
import { Amounts, Logger, PaytoUriIBAN, TranslatedString, parsePaytoUri,
stringifyPaytoUri } from "@gnu-taler/taler-util";
-import { notifyError, notifyException, useNotifications, useTranslationContext
} from "@gnu-taler/web-util/browser";
+import { NotificationMessage, notifyError, notifyException, useNotifications,
useTranslationContext } from "@gnu-taler/web-util/browser";
import { ComponentChildren, Fragment, h, VNode } from "preact";
import { StateUpdater, useEffect, useErrorBoundary, useState } from
"preact/hooks";
import { LangSelector } from "../components/LangSelector.js";
@@ -26,6 +26,7 @@ import { useSettings } from "../hooks/settings.js";
import { CopyButton, CopyIcon } from "../components/CopyButton.js";
import logo from "../assets/logo-2021.svg";
import { useAccountDetails } from "../hooks/access.js";
+import { Attention } from "../components/Attention.js";
const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ :
undefined;
const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
@@ -108,7 +109,7 @@ export function BankFrame({
setOpen(!open)
}}>
<span class="absolute -inset-0.5"></span>
- <span class="sr-only">Open main menu</span>
+ <span class="sr-only">Open settings</span>
<svg class="block h-10 w-10" fill="none" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round"
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
@@ -227,6 +228,22 @@ export function BankFrame({
</button>
</div>
</li>
+ <li class="mt-2">
+ <div class="flex items-center
justify-between">
+ <span class="flex flex-grow flex-col">
+ <span class="text-sm text-black
font-medium leading-6 " id="availability-label">
+ <i18n.Translate>Show debug
info</i18n.Translate>
+ </span>
+ </span>
+ <button type="button"
data-enabled={settings.showDebugInfo} class="bg-indigo-600
data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0
cursor-pointer rounded-full border-2 border-transparent transition-colors
duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600
focus:ring-offset-2" role="switch" aria-checked="false"
aria-labelledby="availability-label" aria-describedby="availability-description"
+
+ onClick={() => {
+ updateSettings("showDebugInfo",
!settings.showDebugInfo);
+ }}>
+ <span aria-hidden="true"
data-enabled={settings.showDebugInfo} class="translate-x-5
data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5
transform rounded-full bg-white shadow ring-0 transition duration-200
ease-in-out"></span>
+ </button>
+ </div>
+ </li>
<li class="mt-2">
<div class="flex items-center
justify-between">
<span class="flex flex-grow flex-col">
@@ -286,10 +303,10 @@ export function BankFrame({
}
</div >
+ <StatusBanner />
<main class="-mt-32 flex-1">
<div class="mx-auto max-w-7xl px-4 pb-12 sm:px-6 lg:px-8">
<div class="rounded-lg bg-white px-5 py-6 shadow sm:px-6">
- <StatusBanner />
{children}
</div>
</div>
@@ -301,79 +318,46 @@ export function BankFrame({
);
}
+function MaybeShowDebugInfo({ info }: { info: any }): VNode {
+ const [settings] = useSettings()
+ if (settings.showDebugInfo) {
+ return <pre class="whitespace-break-spaces ">
+ {info}
+ </pre>
+ }
+ return <Fragment />
+}
+
function StatusBanner(): VNode {
const notifs = useNotifications()
- return <div
- class="fixed top-10 z-20 ml-4 mr-4"
- > {
+ if (notifs.length === 0) return <Fragment />
+ return <div class="fixed z-20 w-full p-4"> {
notifs.map(n => {
switch (n.message.type) {
case "error":
- return <div class="rounded-md bg-red-50 p-4">
- <div class="flex">
- <div class="flex-shrink-0">
- <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000
16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10
11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0
00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
- </svg>
- </div>
- <div class="ml-3 flex-1 md:flex md:justify-between">
- <p class="text-sm font-medium
text-red-800">{n.message.title}</p>
- </div>
- <div>
- <p class="text-sm">
- <button type="button" class="inline-flex font-semibold
items-center rounded bg-white px-2 py-1 text-xs text-gray-900 shadow-sm ring-1
ring-inset ring-gray-300 hover:bg-gray-50"
- onClick={(e) => {
- e.preventDefault();
- n.remove()
- }}
- >
- Close
- <svg class="h-5 w-5" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
- <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94
10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0
101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
- </svg>
- </button>
- </p>
- </div>
- </div>
+ return <Attention type="danger" title={n.message.title}
onClose={() => {
+ n.remove()
+ }}>
{n.message.description &&
<div class="mt-2 text-sm text-red-700">
{n.message.description}
</div>
}
+ <MaybeShowDebugInfo info={n.message.debug} />
+ {/* <a href="#" class="text-gray-500">
+ show debug info
+ </a>
{n.message.debug &&
<div class="mt-2 text-sm text-red-700 font-mono break-all">
{n.message.debug}
</div>
- }
- </div>
+ } */}
+ </Attention>
case "info":
- return <div class="rounded-md bg-green-50 border-4
border-green-600 p-6">
- <div class="flex">
- <div class="flex-shrink-0">
- <svg class="h-8 w-8 text-green-400" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000
16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06
1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
- </svg>
- </div>
- <div class="ml-3 flex-1 md:flex md:justify-between">
- <h3 class="text-lg font-medium
text-green-800">{n.message.title}</h3>
-
- <p class="mt-3 text-sm md:ml-6 md:mt-0">
- <button type="button" class="inline-flex font-semibold
items-center rounded bg-white px-2 py-1 text-md text-gray-900 shadow-sm ring-1
ring-inset ring-gray-300 hover:bg-gray-50"
- onClick={(e) => {
- e.preventDefault();
- n.remove();
- }}
- >
- <svg class="h-8 w-8" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
- <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94
10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0
101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
- </svg>
- </button>
- </p>
- </div>
-
- </div>
- </div>
+ return <Attention type="success" title={n.message.title}
onClose={() => {
+ n.remove();
+ }} />
}
})}
</div>
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx
b/packages/demobank-ui/src/pages/HomePage.tsx
index d945d80d1..95144f086 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -137,8 +137,8 @@ export function handleNotOkResult(
const errorData = result.payload;
notify({
type: "error",
- title: i18n.str`Could not load due to a client error`,
- description: errorData?.error?.description as TranslatedString,
+ title: i18n.str`Could not load due to a request error`,
+ description: i18n.str`Request to url "${result.info.url}" returned
${result.info.status}`,
debug: JSON.stringify(result),
});
break;
@@ -174,7 +174,7 @@ export function handleNotOkResult(
assertUnreachable(result);
}
}
- route("/")
+ // route("/")
return <div>error</div>;
}
return <div />;
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx
b/packages/demobank-ui/src/pages/LoginForm.tsx
index 14d261622..3ea94b899 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -23,6 +23,7 @@ import { useBackendContext } from "../context/backend.js";
import { useCredentialsChecker } from "../hooks/useCredentialsChecker.js";
import { bankUiSettings } from "../settings.js";
import { undefinedIfEmpty } from "../utils.js";
+import { doAutoFocus } from "./PaytoWireTransferForm.js";
/**
@@ -98,8 +99,8 @@ export function LoginForm({ onRegister }: { onRegister?: ()
=> void }): VNode {
});
} else {
saveError({
- title: i18n.str`Could not load due to a client error`,
- // description: cause.payload.error.description,
+ title: i18n.str`Could not load due to a request error`,
+ description: i18n.str`Request to url "${cause.info.url}"
returned ${cause.info.status}`,
debug: JSON.stringify(cause.payload),
});
}
@@ -159,8 +160,7 @@ export function LoginForm({ onRegister }: { onRegister?: ()
=> void }): VNode {
</label>
<div class="mt-2">
<input
- ref={ref}
- autoFocus
+ ref={doAutoFocus}
type="text"
name="username"
id="username"
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts
b/packages/demobank-ui/src/pages/OperationState/state.ts
index 56e79f9ab..4be680377 100644
--- a/packages/demobank-ui/src/pages/OperationState/state.ts
+++ b/packages/demobank-ui/src/pages/OperationState/state.ts
@@ -118,7 +118,9 @@ export function useComponentState({ currency, onClose }:
Props): utils.Recursive
try {
setBusy({})
await confirmWithdrawal(wid);
- notifyInfo(i18n.str`Wire transfer completed!`)
+ if (!settings.showWithdrawalSuccess) {
+ notifyInfo(i18n.str`Wire transfer completed!`)
+ }
} catch (error) {
if (error instanceof RequestError) {
notify(
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx
b/packages/demobank-ui/src/pages/OperationState/views.tsx
index 93b3694d7..2cb7385db 100644
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ b/packages/demobank-ui/src/pages/OperationState/views.tsx
@@ -267,13 +267,12 @@ export function ConfirmedView({ error, onClose }:
State.Confirmed) {
</div>
<div class="mt-3 text-center sm:mt-5">
<h3 class="text-base font-semibold leading-6 text-gray-900"
id="modal-title">
- <i18n.Translate>Withdrawal OK</i18n.Translate>
+ <i18n.Translate>Withdrawal confirmed</i18n.Translate>
</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">
<i18n.Translate>
- The wire transfer to the Taler exchange bank's account is
completed, now the
- exchange will send the requested amount into your GNU Taler
wallet.
+ The wire transfer to the Taler operator has been initiated.
You will soon receive the requested amount in your Taler wallet.
</i18n.Translate>
</p>
</div>
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx
b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index 49419d0dc..fef272831 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -30,7 +30,7 @@ export function PaymentOptions({ limit, goToConfirmOperation
}: { limit: AmountJ
const { i18n } = useTranslationContext();
const [settings] = useSettings();
- const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" |
undefined>();
+ const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" |
undefined>("wire-transfer");
return (
<div class="mt-2">
@@ -82,7 +82,7 @@ export function PaymentOptions({ limit, goToConfirmOperation
}: { limit: AmountJ
<i18n.Translate>another bank account</i18n.Translate>
</span>
<span id="project-type-1-description-0" class="mt-1 flex
items-center text-sm text-gray-500">
- <i18n.Translate>Make a wire transfer to an account which you
know the address.</i18n.Translate>
+ <i18n.Translate>Make a wire transfer to an account which you
know the bank account number</i18n.Translate>
</span>
</span>
</span>
@@ -108,6 +108,7 @@ export function PaymentOptions({ limit,
goToConfirmOperation }: { limit: AmountJ
limit={limit}
onSuccess={() => {
notifyInfo(i18n.str`Wire transfer created!`);
+ setTab(undefined)
}}
onCancel={() => {
setTab(undefined)
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>
);
}
+
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx
b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index 0a5a386ae..6a50d4ef3 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -86,7 +86,6 @@ export function QrCodeSection({
</h3>
<div class="mt-4">
<a href={talerWithdrawUri}
- // class="text-sm font-semibold leading-6 text-gray-900 btn "
class="inline-flex items-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>Click here to start</i18n.Translate>
diff --git a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
index d19c411f3..46f4fe0ef 100644
--- a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
+++ b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
@@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from "preact/hooks";
import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
import { useAdminAccountAPI, useBusinessAccountDetails } from
"../hooks/circuit.js";
import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
+import { doAutoFocus } from "./PaytoWireTransferForm.js";
export function UpdateAccountPassword({
account,
@@ -27,11 +28,6 @@ export function UpdateAccountPassword({
const [password, setPassword] = useState<string | undefined>();
const [repeat, setRepeat] = useState<string | undefined>();
- const ref = useRef<HTMLInputElement>(null);
- useEffect(() => {
- if (focus) ref.current?.focus();
- }, [focus]);
-
if (!result.ok) {
if (result.loading || result.type === ErrorType.TIMEOUT) {
return onLoadNotOk(result);
@@ -96,7 +92,7 @@ export function UpdateAccountPassword({
</label>
<div class="mt-2">
<input
- ref={ref}
+ ref={focus ? doAutoFocus : undefined}
type="password"
class="block w-full 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="password"
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index 3c5ee34fd..7357223b7 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -29,14 +29,15 @@ import {
notifyError,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
-import { VNode, h } from "preact";
+import { Fragment, VNode, h } from "preact";
import { forwardRef } from "preact/compat";
import { useEffect, useRef, useState } from "preact/hooks";
import { useAccessAPI } from "../hooks/access.js";
import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
-import { Amount } from "./PaytoWireTransferForm.js";
+import { Amount, doAutoFocus } from "./PaytoWireTransferForm.js";
import { useSettings } from "../hooks/settings.js";
import { OperationState } from "./OperationState/index.js";
+import { Attention } from "../components/Attention.js";
const logger = new Logger("WalletWithdrawForm");
const RefAmount = forwardRef(Amount);
@@ -53,47 +54,13 @@ function OldWithdrawalForm({ goToConfirmOperation, limit,
onCancel, focus }: {
const { createWithdrawal } = useAccessAPI();
const [amountStr, setAmountStr] = useState<string |
undefined>(`${settings.maxWithdrawalAmount}`);
- const ref = useRef<HTMLInputElement>(null);
- useEffect(() => {
- if (focus) ref.current?.focus();
- }, [focus]);
if (!!settings.currentWithdrawalOperationId) {
- return <div>
-
- <div class="rounded-md bg-yellow-50 ring-yellow-2 p-4">
- <div class="flex">
- <div class="flex-shrink-0">
- <svg class="h-5 w-5 text-yellow-300" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
- <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116
0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0
01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25
0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
- </svg>
- </div>
- <div class="ml-3">
- <h3 class="text-sm font-bold text-yellow-800">
- <i18n.Translate>There is an operation already</i18n.Translate>
- </h3>
- <div class="mt-2 text-sm text-yellow-700">
- <p>
- <i18n.Translate>
- To complete or cancel the operation click <a
class="font-semibold text-yellow-700 hover:text-yellow-600"
href={`#/operation/${settings.currentWithdrawalOperationId}`}>here</a>
- </i18n.Translate>
- </p>
- </div>
-
- </div>
- </div>
- </div >
- <div class="flex 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 bg-white p-2 rounded-sm"
- onClick={() => {
- updateSettings("currentWithdrawalOperationId", undefined)
- onCancel()
- }}
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- </div>
- </div>
+ return <Attention type="warning" title={i18n.str`There is an operation
already`}>
+ <i18n.Translate>
+ To complete or cancel the operation click <a class="font-semibold
text-yellow-700 hover:text-yellow-600"
href={`#/operation/${settings.currentWithdrawalOperationId}`}>here</a>
+ </i18n.Translate>
+ </Attention>
}
const trimmedAmountStr = amountStr?.trim();
@@ -157,8 +124,8 @@ function OldWithdrawalForm({ goToConfirmOperation, limit,
onCancel, focus }: {
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">
+ <div class="px-4 py-6 ">
+ <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="withdraw-amount">{i18n.str`Amount`}</label>
<RefAmount
@@ -169,51 +136,53 @@ function OldWithdrawalForm({ goToConfirmOperation, limit,
onCancel, focus }: {
setAmountStr(v);
}}
error={errors?.amount}
- ref={ref}
+ ref={focus ? doAutoFocus : undefined}
/>
</div>
- <div class="sm:col-span-5">
- <span class="isolate inline-flex rounded-md shadow-sm">
- <button type="button"
- class="relative inline-flex px-6 py-4 text-sm
items-center rounded-l-md bg-white text-gray-900 ring-1 ring-inset
ring-gray-300 hover:bg-gray-50 focus:z-10"
- onClick={(e) => {
- e.preventDefault();
- setAmountStr("50.00")
- }}
- >
- 50.00
- </button>
- <button type="button"
- class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm
items-center bg-white text-gray-900 ring-1 ring-inset
ring-gray-300 hover:bg-gray-50 focus:z-10"
- onClick={(e) => {
- e.preventDefault();
- setAmountStr("25.00")
- }}
- >
+ </div>
+ <div class="mt-4">
+ <div class="sm:inline">
- 25.00
- </button>
- <button type="button"
- class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm
items-center bg-white text-gray-900 ring-1 ring-inset
ring-gray-300 hover:bg-gray-50 focus:z-10"
- onClick={(e) => {
- e.preventDefault();
- setAmountStr("10.00")
- }}
- >
- 10.00
- </button>
- <button type="button"
- class="relative inline-flex px-6 py-4 text-sm
items-center rounded-r-md bg-white text-gray-900 ring-1 ring-inset
ring-gray-300 hover:bg-gray-50 focus:z-10"
- onClick={(e) => {
- e.preventDefault();
- setAmountStr("5.00")
- }}
- >
- 5.00
- </button>
- </span>
- </div>
+ <button type="button"
+ class=" inline-flex px-6 py-4 text-sm items-center
rounded-l-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300
hover:bg-gray-50 focus:z-10"
+ onClick={(e) => {
+ e.preventDefault();
+ setAmountStr("50.00")
+ }}
+ >
+ 50.00
+ </button>
+ <button type="button"
+ class=" -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center
rounded-r-md sm:rounded-none bg-white text-gray-900 ring-1
ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+ onClick={(e) => {
+ e.preventDefault();
+ setAmountStr("25.00")
+ }}
+ >
+ 25.00
+ </button>
+ </div>
+ <div class="mt-4 sm:inline">
+ <button type="button"
+ class=" -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center
rounded-l-md sm:rounded-none bg-white text-gray-900 ring-1
ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+ onClick={(e) => {
+ e.preventDefault();
+ setAmountStr("10.00")
+ }}
+ >
+ 10.00
+ </button>
+ <button type="button"
+ class=" inline-flex px-6 py-4 text-sm items-center
rounded-r-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300
hover:bg-gray-50 focus:z-10"
+ onClick={(e) => {
+ e.preventDefault();
+ setAmountStr("5.00")
+ }}
+ >
+ 5.00
+ </button>
+ </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">
@@ -255,46 +224,20 @@ export function WalletWithdrawForm({
<div class="px-4 sm:px-0">
<h2 class="text-base font-semibold leading-7
text-gray-900"><i18n.Translate>Prepare your wallet</i18n.Translate></h2>
<p class="mt-1 text-sm text-gray-500">
- <i18n.Translate>After using your wallet you will confirm or cancel the
operation.</i18n.Translate>
+ <i18n.Translate>After using your wallet you will need to confirm or
cancel the operation on this site.</i18n.Translate>
</p>
</div>
<div class="col-span-2">
- {settings.showInstallWallet && <div class="rounded-md bg-blue-50
ring-blue-2 ring-2 p-4">
- <div class="flex">
- <div class="flex-shrink-0">
- <svg class="h-5 w-5 text-blue-300" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
- <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116
0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0
01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25
0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
- </svg>
- </div>
- <div class="ml-3">
- <h3 class="text-sm font-bold text-blue-800">
- <i18n.Translate>You need a GNU Taler Wallet</i18n.Translate>
- </h3>
- <div class="mt-2 text-sm text-blue-700">
- <p>
- <i18n.Translate>
- If you dont have one yet you can follow the instruction <a
target="_blank" rel="noreferrer noopener" class="font-semibold text-blue-700
hover:text-blue-600" href="https://taler.net/en/wallet.html">here</a>
- </i18n.Translate>
- </p>
- <p class="mt-3 text-sm flex justify-end">
- <button type="button" class="inline-flex font-semibold
items-center rounded bg-white px-2 py-1 text-xs text-gray-900 shadow-sm ring-1
ring-inset ring-gray-300 hover:bg-gray-50"
- onClick={(e) => {
- e.preventDefault();
- updateSettings("showInstallWallet", false);
- }}
- >
- I know
- <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"
aria-hidden="true">
- <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72
3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06
10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
- </svg>
- </button>
- </p>
-
- </div>
- </div>
- </div>
- </div>}
+ {settings.showInstallWallet &&
+ <Attention title={i18n.str`You need a GNU Taler Wallet`} onClose={()
=> {
+ updateSettings("showInstallWallet", false);
+ }}>
+ <i18n.Translate>
+ If you don't have one yet you can follow the instruction <a
target="_blank" rel="noreferrer noopener" class="font-semibold text-blue-700
hover:text-blue-600" href="https://taler.net/en/wallet.html">here</a>
+ </i18n.Translate>
+ </Attention>
+ }
{!settings.fastWithdrawal ?
<OldWithdrawalForm
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index d160a88b3..208d4b859 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -37,6 +37,7 @@ import { useMemo, useState } from "preact/hooks";
import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
import { useAccessAnonAPI } from "../hooks/access.js";
import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
+import { useSettings } from "../hooks/settings.js";
const logger = new Logger("WithdrawalConfirmationQuestion");
@@ -59,6 +60,7 @@ export function WithdrawalConfirmationQuestion({
withdrawUri,
}: Props): VNode {
const { i18n } = useTranslationContext();
+ const [settings, updateSettings] = useSettings()
const captchaNumbers = useMemo(() => {
return {
@@ -87,7 +89,9 @@ export function WithdrawalConfirmationQuestion({
await confirmWithdrawal(
withdrawUri.withdrawalOperationId,
);
- notifyInfo(i18n.str`Wire transfer completed!`)
+ if (!settings.showWithdrawalSuccess) {
+ notifyInfo(i18n.str`Wire transfer completed!`)
+ }
} catch (error) {
if (error instanceof RequestError) {
notify(
@@ -203,7 +207,7 @@ export function WithdrawalConfirmationQuestion({
<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
text-gray-900"><i18n.Translate>Answer the next question to authorize the wire
transfer</i18n.Translate></h2>
+ <h2 class="text-base font-semibold
text-gray-900"><i18n.Translate>Answer the next question to authorize the wire
transfer.</i18n.Translate></h2>
</div>
<form
class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl
md:col-span-2"
@@ -311,14 +315,10 @@ export function WithdrawalConfirmationQuestion({
}
})()}
- <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">Withdrawal identification</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700
sm:col-span-2 sm:mt-0 break-words">{details.reserve}</dd>
- </div>
<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">Amount</dt>
<dd class="mt-1 text-sm leading-6 text-gray-700
sm:col-span-2 sm:mt-0">
- {Amounts.stringifyValue(details.amount)}
+ {Amounts.currencyOf(details.amount)}
{Amounts.stringifyValue(details.amount)}
</dd>
</div>
</dl>
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index 8f4e175f6..c8efc033b 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -94,20 +94,12 @@ export function WithdrawalQRCode({
</div>
<div class="mt-3 text-center sm:mt-5">
<h3 class="text-base font-semibold leading-6 text-gray-900"
id="modal-title">
- <i18n.Translate>Withdrawal OK</i18n.Translate>
+ <i18n.Translate>Withdrawal confirmed</i18n.Translate>
</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">
<i18n.Translate>
- The wire transfer to the Taler exchange bank's account is
completed, now the
- exchange will send the requested amount into your GNU Taler
wallet.
- </i18n.Translate>
- </p>
- </div>
- <div class="mt-2">
- <p >
- <i18n.Translate>
- You can close this page now or continue to the account page.
+ The wire transfer to the Taler operator has been initiated.
You will soon receive the requested amount in your Taler wallet.
</i18n.Translate>
</p>
</div>
diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx
b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
index 02df824a2..ed8bf610d 100644
--- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
@@ -4,6 +4,7 @@ import { PartialButDefined, RecursivePartial, WithIntermediate,
undefinedIfEmpty
import { useEffect, useRef, useState } from "preact/hooks";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { buildPayto, parsePaytoUri } from "@gnu-taler/taler-util";
+import { doAutoFocus } from "../PaytoWireTransferForm.js";
const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
const EMAIL_REGEX =
@@ -37,10 +38,6 @@ export function AccountForm({
RecursivePartial<typeof initial> | undefined
>(undefined);
const { i18n } = useTranslationContext();
- const ref = useRef<HTMLInputElement>(null);
- useEffect(() => {
- if (focus) ref.current?.focus();
- }, [focus]);
function updateForm(newForm: typeof initial): void {
@@ -97,7 +94,6 @@ export function AccountForm({
<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">
-
<div class="sm:col-span-5">
<label
class="block text-sm font-medium leading-6 text-gray-900"
@@ -108,7 +104,7 @@ export function AccountForm({
</label>
<div class="mt-2">
<input
- ref={ref}
+ ref={focus ? doAutoFocus : undefined}
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="username"
diff --git a/packages/demobank-ui/src/pages/admin/Home.tsx
b/packages/demobank-ui/src/pages/admin/Home.tsx
index ffa559097..d50ff14b4 100644
--- a/packages/demobank-ui/src/pages/admin/Home.tsx
+++ b/packages/demobank-ui/src/pages/admin/Home.tsx
@@ -10,6 +10,7 @@ import { AdminAccount } from "./Account.js";
import { AccountList } from "./AccountList.js";
import { CreateNewAccount } from "./CreateNewAccount.js";
import { RemoveAccount } from "./RemoveAccount.js";
+import { Transactions } from "../../components/Transactions/index.js";
/**
* Query account information and show QR code if there is pending withdrawal
@@ -141,6 +142,7 @@ export function AdminHome({ onRegister }: Props): VNode {
<AdminAccount onRegister={onRegister} />
+ <Transactions account="admin"/>
</Fragment>
);
}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
index 1e5370afc..b323b0d01 100644
--- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
@@ -6,6 +6,8 @@ import { Amounts, HttpStatusCode, TranslatedString } from
"@gnu-taler/taler-util
import { buildRequestErrorMessage, undefinedIfEmpty } from "../../utils.js";
import { useEffect, useRef, useState } from "preact/hooks";
import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { Attention } from "../../components/Attention.js";
+import { doAutoFocus } from "../PaytoWireTransferForm.js";
export function RemoveAccount({
account,
@@ -36,47 +38,15 @@ export function RemoveAccount({
}
return onLoadNotOk(result);
}
- const ref = useRef<HTMLInputElement>(null);
- useEffect(() => {
- if (focus) ref.current?.focus();
- }, [focus]);
-
const balance = Amounts.parse(result.data.balance.amount);
if (!balance) {
return <div>there was an error reading the balance</div>;
}
const isBalanceEmpty = Amounts.isZero(balance);
if (!isBalanceEmpty) {
- return <div>
- <div class="rounded-md bg-yellow-50 p-4">
- <div class="flex">
- <div class="flex-shrink-0">
- <svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
- <path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167
3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347
0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0
01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z"
clip-rule="evenodd" />
- </svg>
- </div>
- <div class="ml-3">
- <h3 class="text-sm font-medium text-yellow-800">
- <i18n.Translate>Can't delete the account</i18n.Translate>
- </h3>
- <div class="mt-2 text-sm text-yellow-700">
- <p>
- <i18n.Translate>The account can't be delete while still
holding some balance. First make sure that the owner make a complete
cashout.</i18n.Translate>
- </p>
- </div>
- </div>
-
- </div>
- </div>
- <div class="mt-2 flex justify-end">
- <button type="button" class="rounded-md ring-1 ring-gray-400 bg-white
px-3 py-2 text-sm font-semibold shadow-sm hover:bg-gray-100
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 "
- onClick={() => {
- onCancel()
- }}>
- <i18n.Translate>Go back</i18n.Translate>
- </button>
- </div>
- </div>
+ return <Attention type="warning" title={i18n.str`Can't delete the
account`} onClose={onCancel}>
+ <i18n.Translate>The account can't be delete while still holding some
balance. First make sure that the owner make a complete
cashout.</i18n.Translate>
+ </Attention>
}
async function doRemove() {
@@ -117,26 +87,9 @@ export function RemoveAccount({
return (
<div>
- <div class="rounded-md bg-yellow-50 p-4">
- <div class="flex">
- <div class="flex-shrink-0">
- <svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
- <path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167
3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347
0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0
01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z"
clip-rule="evenodd" />
- </svg>
- </div>
- <div class="ml-3">
- <h3 class="text-sm font-bold text-yellow-800">
- <i18n.Translate>You are going to remove the
account</i18n.Translate>
- </h3>
- <div class="mt-2 text-sm text-yellow-700">
- <p>
- <i18n.Translate>This step can't be undone.</i18n.Translate>
- </p>
- </div>
- </div>
-
- </div>
- </div>
+ <Attention type="warning" title={i18n.str`You are going to remove the
account`}>
+ <i18n.Translate>This step can't be undone.</i18n.Translate>
+ </Attention>
<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">
@@ -164,7 +117,7 @@ export function RemoveAccount({
</label>
<div class="mt-2">
<input
- ref={ref}
+ 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
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="password"
diff --git a/packages/web-util/src/hooks/index.ts
b/packages/web-util/src/hooks/index.ts
index cc3267dbd..f6c74ff22 100644
--- a/packages/web-util/src/hooks/index.ts
+++ b/packages/web-util/src/hooks/index.ts
@@ -1,15 +1,7 @@
export { useLang } from "./useLang.js";
export { useLocalStorage, buildStorageKey } from "./useLocalStorage.js";
export { useMemoryStorage } from "./useMemoryStorage.js";
-export {
- useNotifications,
- notifyError,
- notifyException,
- notifyInfo,
- notify,
- ErrorNotification,
- InfoNotification
-} from "./useNotifications.js";
+export * from "./useNotifications.js";
export {
useAsyncAsHook,
HookError,
diff --git a/packages/web-util/src/hooks/useNotifications.ts
b/packages/web-util/src/hooks/useNotifications.ts
index 792095b06..e9e8a240b 100644
--- a/packages/web-util/src/hooks/useNotifications.ts
+++ b/packages/web-util/src/hooks/useNotifications.ts
@@ -54,7 +54,7 @@ export function notifyInfo(title: TranslatedString) {
});
}
-type Notification = {
+export type Notification = {
message: NotificationMessage;
remove: () => void;
};
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [taler-wallet-core] branch master updated (779ddae8b -> 1708d49a2),
gnunet <=