more ui
This commit is contained in:
parent
15af6c619d
commit
ae49194d42
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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");
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -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"
|
||||||
? ""
|
? ""
|
||||||
|
@ -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",
|
||||||
});
|
});
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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)}
|
||||||
{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""}
|
{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""}
|
||||||
{Amounts.stringifyValue(result.data.balance.amount)} */}
|
{Amounts.stringifyValue(result.data.balance.amount)} */}
|
||||||
|
@ -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) {
|
||||||
|
@ -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()
|
||||||
|
@ -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 |
|
||||||
|
@ -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()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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()
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { VNode, h } from "preact";
|
|
||||||
|
|
||||||
export function Test(): VNode {
|
|
||||||
return <div >hola</div>
|
|
||||||
}
|
|
@ -307,7 +307,6 @@ export function WalletWithdrawForm({
|
|||||||
<OperationState
|
<OperationState
|
||||||
currency={limit.currency}
|
currency={limit.currency}
|
||||||
onClose={onCancel}
|
onClose={onCancel}
|
||||||
goToConfirmOperation={goToConfirmOperation}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -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]: {
|
||||||
|
@ -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`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user