This commit is contained in:
Sebastian 2023-09-25 08:40:18 -03:00
parent 15af6c619d
commit ae49194d42
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
19 changed files with 145 additions and 95 deletions

View File

@ -15,15 +15,24 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { HttpError, useTranslationContext } from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
export function ErrorLoading(): VNode { export function ErrorLoading({ error }: { error: HttpError<SandboxBackend.SandboxError> }): VNode {
const { i18n } = useTranslationContext() const { i18n } = useTranslationContext()
return ( return (
<div><i18n.Translate> <div><div class="rounded-md bg-red-50 p-4">
Could not complete the request <div class="flex">
</i18n.Translate> <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>
</div> </div>
); );
} }

View File

@ -23,7 +23,6 @@ import { BusinessAccount } from "../pages/business/Home.js";
import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js"; import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js";
import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js"; import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js";
import { RegistrationPage } from "../pages/RegistrationPage.js"; import { RegistrationPage } from "../pages/RegistrationPage.js";
import { Test } from "../pages/Test.js";
import { useBackendContext } from "../context/backend.js"; import { useBackendContext } from "../context/backend.js";
import { LoginForm } from "../pages/LoginForm.js"; import { LoginForm } from "../pages/LoginForm.js";
import { AdminHome } from "../pages/admin/Home.js"; import { AdminHome } from "../pages/admin/Home.js";
@ -69,6 +68,9 @@ export function Routing(): VNode {
onComplete={() => { onComplete={() => {
route("/account"); route("/account");
}} }}
onCancel={() => {
route("/account");
}}
/> />
)} )}
/> />

View File

@ -32,6 +32,7 @@ export function LoadingUriView({ error }: State.LoadingUriError): VNode {
export function ReadyView({ transactions }: State.Ready): VNode { export function ReadyView({ transactions }: State.Ready): VNode {
const { i18n } = useTranslationContext(); const { i18n } = useTranslationContext();
if (!transactions.length) return <div />
const txByDate = transactions.reduce((prev, cur) => { const txByDate = transactions.reduce((prev, cur) => {
const d = cur.when.t_ms === "never" const d = cur.when.t_ms === "never"
? "" ? ""

View File

@ -94,7 +94,7 @@ export function useAccessAnonAPI(): AccessAnonAPI {
const { request } = useAuthenticatedBackend(); const { request } = useAuthenticatedBackend();
const abortWithdrawal = async (id: string): Promise<HttpResponseOk<void>> => { const abortWithdrawal = async (id: string): Promise<HttpResponseOk<void>> => {
const res = await request<void>(`accounts/withdrawals/${id}/abort`, { const res = await request<void>(`withdrawals/${id}/abort`, {
method: "POST", method: "POST",
contentType: "json", contentType: "json",
}); });

View File

@ -18,7 +18,7 @@ import { HttpError, HttpResponseOk, HttpResponsePaginated, utils } from "@gnu-ta
import { AbsoluteTime, AmountJson, PaytoUriIBAN, PaytoUriTalerBank } from "@gnu-taler/taler-util"; import { AbsoluteTime, AmountJson, PaytoUriIBAN, PaytoUriTalerBank } from "@gnu-taler/taler-util";
import { Loading } from "../../components/Loading.js"; import { Loading } from "../../components/Loading.js";
import { useComponentState } from "./state.js"; import { useComponentState } from "./state.js";
import { ReadyView, InvalidIbanView} from "./views.js"; import { ReadyView, InvalidIbanView } from "./views.js";
import { VNode } from "preact"; import { VNode } from "preact";
import { LoginForm } from "../LoginForm.js"; import { LoginForm } from "../LoginForm.js";
import { ErrorLoading } from "../../components/ErrorLoading.js"; import { ErrorLoading } from "../../components/ErrorLoading.js";
@ -29,7 +29,7 @@ export interface Props {
error: HttpResponsePaginated<T, SandboxBackend.SandboxError>, error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
) => VNode; ) => VNode;
goToBusinessAccount: () => void; goToBusinessAccount: () => void;
goToConfirmOperation: (id:string) => void; goToConfirmOperation: (id: string) => void;
} }
export type State = State.Loading | State.LoadingError | State.Ready | State.InvalidIban | State.UserNotFound; export type State = State.Loading | State.LoadingError | State.Ready | State.InvalidIban | State.UserNotFound;
@ -48,14 +48,14 @@ export namespace State {
export interface BaseInfo { export interface BaseInfo {
error: undefined; error: undefined;
} }
export interface Ready extends BaseInfo { export interface Ready extends BaseInfo {
status: "ready"; status: "ready";
error: undefined; error: undefined;
account: string, account: string,
limit: AmountJson, limit: AmountJson,
goToBusinessAccount: () => void; goToBusinessAccount: () => void;
goToConfirmOperation: (id:string) => void; goToConfirmOperation: (id: string) => void;
} }
export interface InvalidIban { export interface InvalidIban {

View File

@ -48,6 +48,13 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
error: result, error: result,
}; };
} }
if (result.status === HttpStatusCode.Unauthorized) {
notifyError(i18n.str`Require login`, undefined);
return {
status: "error-user-not-found",
error: result,
};
}
return { return {
status: "loading-error", status: "loading-error",
error: result, error: result,

View File

@ -344,8 +344,6 @@ function StatusBanner(): VNode {
class="fixed top-10 z-20 ml-4 mr-4" class="fixed top-10 z-20 ml-4 mr-4"
> { > {
notifs.map(n => { notifs.map(n => {
const info = n.message.type === "info" ? n.message : undefined
const error = n.message.type === "error" ? n.message : undefined
switch (n.message.type) { switch (n.message.type) {
case "error": case "error":
return <div class="rounded-md bg-red-50 p-4"> return <div class="rounded-md bg-red-50 p-4">
@ -478,8 +476,7 @@ function AccountBalance({ account }: { account: string }): VNode {
// FIXME: balance // FIXME: balance
return <div> return <div>
{Amounts.currencyOf(result.data.balance)} {Amounts.currencyOf(result.data.balance)} {Amounts.stringifyValue(result.data.balance)}
{Amounts.stringifyValue(result.data.balance)}
{/* {Amounts.currencyOf(result.data.balance.amount)} {/* {Amounts.currencyOf(result.data.balance.amount)}
&nbsp;{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""} &nbsp;{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""}
{Amounts.stringifyValue(result.data.balance.amount)} */} {Amounts.stringifyValue(result.data.balance.amount)} */}

View File

@ -59,7 +59,7 @@ export function HomePage({
account: string, account: string,
onRegister: () => void; onRegister: () => void;
goToBusinessAccount: () => void; goToBusinessAccount: () => void;
goToConfirmOperation: (id:string) => void; goToConfirmOperation: (id: string) => void;
}): VNode { }): VNode {
const { i18n } = useTranslationContext(); const { i18n } = useTranslationContext();
@ -84,7 +84,7 @@ export function WithdrawalOperationPage({
//or return withdrawal uri from response //or return withdrawal uri from response
const baseUrl = getInitialBackendBaseURL() const baseUrl = getInitialBackendBaseURL()
const uri = stringifyWithdrawUri({ const uri = stringifyWithdrawUri({
bankIntegrationApiBaseUrl: `${baseUrl}/integration-api`, bankIntegrationApiBaseUrl: `${baseUrl}/taler-integration`,
withdrawalOperationId: operationId, withdrawalOperationId: operationId,
}); });
const parsedUri = parseWithdrawUri(uri); const parsedUri = parseWithdrawUri(uri);
@ -98,7 +98,7 @@ export function WithdrawalOperationPage({
); );
return <Loading />; return <Loading />;
} }
return ( return (
<WithdrawalQRCode <WithdrawalQRCode
withdrawUri={parsedUri} withdrawUri={parsedUri}
@ -121,7 +121,7 @@ export function handleNotOkResult(
return function handleNotOkResult2<T>( return function handleNotOkResult2<T>(
result: result:
| HttpResponsePaginated<T, SandboxBackend.SandboxError | undefined> | HttpResponsePaginated<T, SandboxBackend.SandboxError | undefined>
| HttpResponse<T, SandboxBackend.SandboxError| undefined>, | HttpResponse<T, SandboxBackend.SandboxError | undefined>,
): VNode { ): VNode {
if (result.loading) return <Loading />; if (result.loading) return <Loading />;
if (!result.ok) { if (!result.ok) {

View File

@ -49,7 +49,7 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
? i18n.str`Missing username` ? i18n.str`Missing username`
// : !USERNAME_REGEX.test(username) // : !USERNAME_REGEX.test(username)
// ? i18n.str`Use letters and numbers only, and start with a lowercase letter` // ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
: undefined, : undefined,
password: !password ? i18n.str`Missing password` : undefined, password: !password ? i18n.str`Missing password` : undefined,
}) ?? busy; }) ?? busy;
@ -213,7 +213,7 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
{bankUiSettings.allowRegistrations && onRegister && {bankUiSettings.allowRegistrations && onRegister &&
<p class="mt-10 text-center text-sm text-gray-500 border-t"> <p class="mt-10 text-center text-sm text-gray-500 border-t">
<button type="submit" <button type="submit"
class="flex mt-4 w-full justify-center rounded-md bg-green-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600" class="flex mt-4 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
onClick={(e) => { onClick={(e) => {
e.preventDefault() e.preventDefault()
onRegister() onRegister()

View File

@ -14,19 +14,16 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { HttpError, HttpResponseOk, HttpResponsePaginated, utils } from "@gnu-taler/web-util/browser"; import { AbsoluteTime, AmountJson, WithdrawUriResult } from "@gnu-taler/taler-util";
import { AbsoluteTime, AmountJson, PaytoUriIBAN, PaytoUriTalerBank, WithdrawUriResult } from "@gnu-taler/taler-util"; import { HttpError, utils } from "@gnu-taler/web-util/browser";
import { ErrorLoading } from "../../components/ErrorLoading.js";
import { Loading } from "../../components/Loading.js"; import { Loading } from "../../components/Loading.js";
import { useComponentState } from "./state.js"; import { useComponentState } from "./state.js";
import { ReadyView, AbortedView, ConfirmedView, InvalidPaytoView, InvalidReserveView, InvalidWithdrawalView, NeedConfirmationView } from "./views.js"; import { AbortedView, ConfirmedView, InvalidPaytoView, InvalidReserveView, InvalidWithdrawalView, NeedConfirmationView, ReadyView } from "./views.js";
import { VNode } from "preact";
import { LoginForm } from "../LoginForm.js";
import { ErrorLoading } from "../../components/ErrorLoading.js";
export interface Props { export interface Props {
currency: string; currency: string;
onClose: () => void; onClose: () => void;
goToConfirmOperation: (id: string) => void;
} }
export type State = State.Loading | export type State = State.Loading |

View File

@ -15,16 +15,15 @@
*/ */
import { Amounts, HttpStatusCode, TranslatedString, parsePaytoUri, parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util"; import { Amounts, HttpStatusCode, TranslatedString, parsePaytoUri, parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util";
import { ErrorType, RequestError, notify, notifyError, notifyInfo, useTranslationContext, utils } from "@gnu-taler/web-util/browser"; import { RequestError, notify, notifyError, notifyInfo, useTranslationContext, utils } from "@gnu-taler/web-util/browser";
import { useBackendContext } from "../../context/backend.js"; import { useEffect, useState } from "preact/hooks";
import { useAccessAPI, useAccessAnonAPI, useAccountDetails, useWithdrawalDetails } from "../../hooks/access.js"; import { useAccessAPI, useAccessAnonAPI, useWithdrawalDetails } from "../../hooks/access.js";
import { Props, State } from "./index.js";
import { useSettings } from "../../hooks/settings.js";
import { buildRequestErrorMessage, undefinedIfEmpty } from "../../utils.js";
import { useEffect, useMemo, useState } from "preact/hooks";
import { getInitialBackendBaseURL } from "../../hooks/backend.js"; import { getInitialBackendBaseURL } from "../../hooks/backend.js";
import { useSettings } from "../../hooks/settings.js";
import { buildRequestErrorMessage } from "../../utils.js";
import { Props, State } from "./index.js";
export function useComponentState({ currency, onClose,goToConfirmOperation }: Props): utils.RecursiveState<State> { export function useComponentState({ currency, onClose }: Props): utils.RecursiveState<State> {
const { i18n } = useTranslationContext(); const { i18n } = useTranslationContext();
const [settings, updateSettings] = useSettings() const [settings, updateSettings] = useSettings()
const { createWithdrawal } = useAccessAPI(); const { createWithdrawal } = useAccessAPI();
@ -142,8 +141,8 @@ export function useComponentState({ currency, onClose,goToConfirmOperation }: Pr
} }
} }
setBusy(undefined) setBusy(undefined)
} }
const bankIntegrationApiBaseUrl = `${baseUrl}/integration-api` const bankIntegrationApiBaseUrl = `${baseUrl}/taler-integration`
const uri = stringifyWithdrawUri({ const uri = stringifyWithdrawUri({
bankIntegrationApiBaseUrl, bankIntegrationApiBaseUrl,
withdrawalOperationId, withdrawalOperationId,
@ -160,7 +159,13 @@ export function useComponentState({ currency, onClose,goToConfirmOperation }: Pr
return (): utils.RecursiveState<State> => { return (): utils.RecursiveState<State> => {
const result = useWithdrawalDetails(withdrawalOperationId); const result = useWithdrawalDetails(withdrawalOperationId);
const shouldCreateNewOperation = !result.ok && !result.loading && result.info.status === HttpStatusCode.NotFound
useEffect(() => {
if (shouldCreateNewOperation) {
doSilentStart()
}
}, [])
if (!result.ok) { if (!result.ok) {
if (result.loading) { if (result.loading) {
return { return {
@ -168,6 +173,12 @@ export function useComponentState({ currency, onClose,goToConfirmOperation }: Pr
error: undefined error: undefined
} }
} }
if (result.info.status === HttpStatusCode.NotFound) {
return {
status: "loading",
error: undefined,
}
}
return { return {
status: "loading-error", status: "loading-error",
error: result error: result
@ -178,7 +189,10 @@ export function useComponentState({ currency, onClose,goToConfirmOperation }: Pr
return { return {
status: "aborted", status: "aborted",
error: undefined, error: undefined,
onClose, onClose: async () => {
updateSettings("currentWithdrawalOperationId", undefined)
onClose()
},
} }
} }

View File

@ -256,6 +256,7 @@ export function AbortedView({ error, onClose }: State.Aborted) {
<div>aborted</div> <div>aborted</div>
); );
} }
export function ConfirmedView({ error, onClose }: State.Confirmed) { export function ConfirmedView({ error, onClose }: State.Confirmed) {
const { i18n } = useTranslationContext(); const { i18n } = useTranslationContext();
const [settings, updateSettings] = useSettings() const [settings, updateSettings] = useSettings()
@ -325,8 +326,17 @@ export function ReadyView({ uri, onClose }: State.Ready): VNode<{}> {
document.title = `${document.title} ${uri.withdrawalOperationId}`; document.title = `${document.title} ${uri.withdrawalOperationId}`;
}, []); }, []);
const talerWithdrawUri = stringifyWithdrawUri(uri); const talerWithdrawUri = stringifyWithdrawUri(uri);
const [show, setShow] = useState(false)
return <Fragment> return <Fragment>
<div class="flex justify-end mt-4">
<button type="button"
class="inline-flex items-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500"
onClick={() => {
onClose()
}}
>
Cancel
</button>
</div>
<div class="bg-white shadow sm:rounded-lg mt-4"> <div class="bg-white shadow sm:rounded-lg mt-4">
<div class="p-4"> <div class="p-4">
@ -360,39 +370,13 @@ export function ReadyView({ uri, onClose }: State.Ready): VNode<{}> {
<i18n.Translate>Scan the QR code with your mobile device.</i18n.Translate> <i18n.Translate>Scan the QR code with your mobile device.</i18n.Translate>
</p> </p>
</div> </div>
<div class="mt-5 sm:ml-6 sm:mt-0 sm:flex sm:flex-shrink-0 sm:items-center">
<button type="button"
class="inline-flex items-center 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-500"
onClick={() => {
setShow(!show)
}}
>
{!show ?
<i18n.Translate>Show QR</i18n.Translate>
:
<i18n.Translate>Hide QR</i18n.Translate>
}
</button>
</div>
</div> </div>
{show && <div class="mt-2 max-w-md ml-auto mr-auto">
<div class="mt-2 max-w-md ml-auto mr-auto"> <QR text={talerWithdrawUri} />
<QR text={talerWithdrawUri} /> </div>
</div>
}
</div> </div>
</div> </div>
<div class="flex justify-end mt-4">
<button type="button"
class="inline-flex items-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500"
onClick={() => {
onClose()
}}
>
Cancel
</button>
</div>
</Fragment> </Fragment>
} }

View File

@ -56,11 +56,11 @@ export function PaymentOptions({ limit, goToConfirmOperation }: { limit: AmountJ
<i18n.Translate>Withdraw digital money into your mobile wallet or browser extension</i18n.Translate> <i18n.Translate>Withdraw digital money into your mobile wallet or browser extension</i18n.Translate>
</span> </span>
{!!settings.currentWithdrawalOperationId && {!!settings.currentWithdrawalOperationId &&
<span class="inline-flex items-center gap-x-1.5 rounded-full bg-green-100 px-1.5 py-0.5 text-xs font-medium text-green-700"> <span class="inline-flex items-center gap-x-1.5 w-fit rounded-full bg-green-100 px-2 py-1 text-xs font-medium text-green-700">
<svg class="h-1.5 w-1.5 fill-green-500" viewBox="0 0 6 6" aria-hidden="true"> <svg class="h-1.5 w-1.5 fill-green-500" viewBox="0 0 6 6" aria-hidden="true">
<circle cx="3" cy="3" r="3" /> <circle cx="3" cy="3" r="3" />
</svg> </svg>
<i18n.Translate>Operation in progress</i18n.Translate> <i18n.Translate>operation ready</i18n.Translate>
</span> </span>
} }
</span> </span>

View File

@ -33,8 +33,10 @@ const logger = new Logger("RegistrationPage");
export function RegistrationPage({ export function RegistrationPage({
onComplete, onComplete,
onCancel
}: { }: {
onComplete: () => void; onComplete: () => void;
onCancel: () => void;
}): VNode { }): VNode {
const { i18n } = useTranslationContext(); const { i18n } = useTranslationContext();
if (!bankUiSettings.allowRegistrations) { if (!bankUiSettings.allowRegistrations) {
@ -42,7 +44,7 @@ export function RegistrationPage({
<p>{i18n.str`Currently, the bank is not accepting new registrations!`}</p> <p>{i18n.str`Currently, the bank is not accepting new registrations!`}</p>
); );
} }
return <RegistrationForm onComplete={onComplete} />; return <RegistrationForm onComplete={onComplete} onCancel={onCancel} />;
} }
export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/; export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/;
@ -50,7 +52,7 @@ export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/;
/** /**
* Collect and submit registration data. * Collect and submit registration data.
*/ */
function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode { function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, onCancel: () => void }): VNode {
const backend = useBackendContext(); const backend = useBackendContext();
const [username, setUsername] = useState<string | undefined>(); const [username, setUsername] = useState<string | undefined>();
const [name, setName] = useState<string | undefined>(); const [name, setName] = useState<string | undefined>();
@ -168,9 +170,38 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
autoCapitalize="none" autoCapitalize="none"
autoCorrect="off" autoCorrect="off"
> >
<div>
<label for="name" class="block text-sm font-medium leading-6 text-gray-900">
<i18n.Translate>Name</i18n.Translate>
<b style={{ color: "red" }}> *</b>
</label>
<div class="mt-2">
<input
autoFocus
type="text"
name="name"
id="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"
value={name ?? ""}
enterkeyhint="next"
placeholder="your name"
autocomplete="name"
required
onInput={(e): void => {
setName(e.currentTarget.value);
}}
/>
<ShowInputErrorLabel
message={errors?.name}
isDirty={name !== undefined}
/>
</div>
</div>
<div> <div>
<label for="username" class="block text-sm font-medium leading-6 text-gray-900"> <label for="username" class="block text-sm font-medium leading-6 text-gray-900">
<i18n.Translate>Username</i18n.Translate> <i18n.Translate>Username</i18n.Translate>
<b style={{ color: "red" }}> *</b>
</label> </label>
<div class="mt-2"> <div class="mt-2">
<input <input
@ -197,7 +228,10 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
<div> <div>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label> <label for="password" class="block text-sm font-medium leading-6 text-gray-900">
Password
<b style={{ color: "red" }}> *</b>
</label>
</div> </div>
<div class="mt-2"> <div class="mt-2">
<input <input
@ -223,7 +257,10 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
<div> <div>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<label for="register-repeat" class="block text-sm font-medium leading-6 text-gray-900">Repeat assword</label> <label for="register-repeat" class="block text-sm font-medium leading-6 text-gray-900">
Repeat assword
<b style={{ color: "red" }}> *</b>
</label>
</div> </div>
<div class="mt-2"> <div class="mt-2">
<input <input
@ -247,9 +284,18 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
</div> </div>
</div> </div>
<div> <div class="flex w-full justify-between">
<button type="submit" <button type="submit"
class="flex w-full justify-center rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 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" class="ring-1 ring-gray-600 rounded-md bg-white disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-white-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
onClick={(e) => {
e.preventDefault()
onCancel()
}}
>
<i18n.Translate>Cancel</i18n.Translate>
</button>
<button type="submit"
class=" rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
disabled={!!errors} disabled={!!errors}
onClick={(e) => { onClick={(e) => {
e.preventDefault() e.preventDefault()

View File

@ -1,5 +0,0 @@
import { VNode, h } from "preact";
export function Test(): VNode {
return <div >hola</div>
}

View File

@ -307,7 +307,6 @@ export function WalletWithdrawForm({
<OperationState <OperationState
currency={limit.currency} currency={limit.currency}
onClose={onCancel} onClose={onCancel}
goToConfirmOperation={goToConfirmOperation}
/> />
} }
</div> </div>

View File

@ -78,7 +78,7 @@ export interface DetailsMap {
stack?: string; stack?: string;
}; };
[TalerErrorCode.WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE]: { [TalerErrorCode.WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE]: {
exchangeProtocolVersion: string; bankProtocolVersion: string;
walletProtocolVersion: string; walletProtocolVersion: string;
}; };
[TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN]: { [TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN]: {

View File

@ -570,7 +570,7 @@ export async function getBankWithdrawalInfo(
throw TalerError.fromDetail( throw TalerError.fromDetail(
TalerErrorCode.WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE, TalerErrorCode.WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE,
{ {
exchangeProtocolVersion: config.version, bankProtocolVersion: config.version,
walletProtocolVersion: WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, walletProtocolVersion: WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
}, },
"bank integration protocol version not compatible with wallet", "bank integration protocol version not compatible with wallet",
@ -813,10 +813,10 @@ async function handleKycRequired(
amlStatus === AmlStatus.normal || amlStatus === undefined amlStatus === AmlStatus.normal || amlStatus === undefined
? WithdrawalGroupStatus.PendingKyc ? WithdrawalGroupStatus.PendingKyc
: amlStatus === AmlStatus.pending : amlStatus === AmlStatus.pending
? WithdrawalGroupStatus.PendingAml ? WithdrawalGroupStatus.PendingAml
: amlStatus === AmlStatus.fronzen : amlStatus === AmlStatus.fronzen
? WithdrawalGroupStatus.SuspendedAml ? WithdrawalGroupStatus.SuspendedAml
: assertUnreachable(amlStatus); : assertUnreachable(amlStatus);
await tx.withdrawalGroups.put(wg2); await tx.withdrawalGroups.put(wg2);
const newTxState = computeWithdrawalTransactionStatus(wg2); const newTxState = computeWithdrawalTransactionStatus(wg2);
@ -1145,8 +1145,7 @@ export async function updateWithdrawalDenoms(
denom.verificationStatus === DenominationVerificationStatus.Unverified denom.verificationStatus === DenominationVerificationStatus.Unverified
) { ) {
logger.trace( logger.trace(
`Validating denomination (${current + 1}/${ `Validating denomination (${current + 1}/${denominations.length
denominations.length
}) signature of ${denom.denomPubHash}`, }) signature of ${denom.denomPubHash}`,
); );
let valid = false; let valid = false;
@ -1240,7 +1239,7 @@ async function queryReserve(
if ( if (
resp.status === 404 && resp.status === 404 &&
result.talerErrorResponse.code === result.talerErrorResponse.code ===
TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
) { ) {
return { ready: false }; return { ready: false };
} else { } else {
@ -1775,7 +1774,7 @@ export async function getExchangeWithdrawalInfo(
) { ) {
logger.warn( logger.warn(
`wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` + `wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` +
`(exchange has ${exchangeDetails.protocolVersionRange}), checking for updates`, `(exchange has ${exchangeDetails.protocolVersionRange}), checking for updates`,
); );
} }
} }

View File

@ -29,7 +29,7 @@ export const WALLET_EXCHANGE_PROTOCOL_VERSION = "17:0:0";
export const WALLET_MERCHANT_PROTOCOL_VERSION = "2:0:1"; export const WALLET_MERCHANT_PROTOCOL_VERSION = "2:0:1";
/** /**
* Protocol version spoken with the merchant. * Protocol version spoken with the bank.
* *
* Uses libtool's current:revision:age versioning. * Uses libtool's current:revision:age versioning.
*/ */