aboutsummaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src/pages/SolveChallengePage.tsx
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-01-23 18:00:42 -0300
committerSebastian <sebasjm@gmail.com>2024-01-24 17:14:02 -0300
commit236d4347f5884bb1d9ca1d3bb4ad0ba776577fd2 (patch)
treea38823a73006c38bd54cb438da81f13bb513dce5 /packages/demobank-ui/src/pages/SolveChallengePage.tsx
parent579128ce40c7e56f390cadaf2fc2fd4cc6290d68 (diff)
downloadwallet-core-236d4347f5884bb1d9ca1d3bb4ad0ba776577fd2.tar.xz
many changes
activate eslint update file headers removed history and preact-router remove eslint errors and more applied prettier
Diffstat (limited to 'packages/demobank-ui/src/pages/SolveChallengePage.tsx')
-rw-r--r--packages/demobank-ui/src/pages/SolveChallengePage.tsx806
1 files changed, 464 insertions, 342 deletions
diff --git a/packages/demobank-ui/src/pages/SolveChallengePage.tsx b/packages/demobank-ui/src/pages/SolveChallengePage.tsx
index 095a0f492..6d2d6512e 100644
--- a/packages/demobank-ui/src/pages/SolveChallengePage.tsx
+++ b/packages/demobank-ui/src/pages/SolveChallengePage.tsx
@@ -18,13 +18,12 @@ import {
AbsoluteTime,
Amounts,
HttpStatusCode,
- Logger,
TalerCorebankApi,
TalerError,
TalerErrorCode,
TranslatedString,
assertUnreachable,
- parsePaytoUri
+ parsePaytoUri,
} from "@gnu-taler/taler-util";
import {
Attention,
@@ -32,7 +31,7 @@ import {
LocalNotificationBanner,
ShowInputErrorLabel,
useLocalNotification,
- useTranslationContext
+ useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { format } from "date-fns";
import { Fragment, VNode, h } from "preact";
@@ -43,40 +42,41 @@ import { useWithdrawalDetails } from "../hooks/access.js";
import { useBackendState } from "../hooks/backend.js";
import { ChallengeInProgess, useBankState } from "../hooks/bank-state.js";
import { useConversionInfo } from "../hooks/circuit.js";
+import { RouteDefinition } from "../route.js";
import { undefinedIfEmpty } from "../utils.js";
import { RenderAmount } from "./PaytoWireTransferForm.js";
import { OperationNotFound } from "./WithdrawalQRCode.js";
-const logger = new Logger("SolveChallenge");
-
export function SolveChallengePage({
- onContinue,
+ onChallengeCompleted,
+ routeClose,
}: {
- onContinue: () => void;
+ onChallengeCompleted: () => void;
+ routeClose: RouteDefinition<Record<string, never>>;
}): VNode {
- const { api } = useBankCoreApiContext()
+ const { api } = useBankCoreApiContext();
const { i18n } = useTranslationContext();
const [bankState, updateBankState] = useBankState();
const [code, setCode] = useState<string | undefined>(undefined);
- const [notification, notify, handleError] = useLocalNotification()
+ const [notification, notify, handleError] = useLocalNotification();
const { state } = useBackendState();
- const creds = state.status !== "loggedIn" ? undefined : state
+ const creds = state.status !== "loggedIn" ? undefined : state;
if (!bankState.currentChallenge) {
- return <div>
- <span>no challenge to solve </span>
- <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={() => {
- onContinue()
- }}
- >
- <i18n.Translate>Continue</i18n.Translate>
- </button>
- </div>
+ return (
+ <div>
+ <span>no challenge to solve </span>
+ <a
+ href={routeClose.url({})}
+ 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"
+ >
+ <i18n.Translate>Continue</i18n.Translate>
+ </a>
+ </div>
+ );
}
- const ch = bankState.currentChallenge
+ const ch = bankState.currentChallenge;
const errors = undefinedIfEmpty({
code: !code ? i18n.str`required` : undefined,
});
@@ -86,34 +86,38 @@ export function SolveChallengePage({
await handleError(async () => {
const resp = await api.sendChallenge(creds, ch.id);
if (resp.type === "ok") {
- const newCh = structuredClone(ch)
- newCh.sent = AbsoluteTime.now()
- newCh.info = resp.body
- updateBankState("currentChallenge", newCh)
+ const newCh = structuredClone(ch);
+ newCh.sent = AbsoluteTime.now();
+ newCh.info = resp.body;
+ updateBankState("currentChallenge", newCh);
} else {
switch (resp.case) {
- case HttpStatusCode.NotFound: return notify({
- type: "error",
- title: i18n.str`Cashout not found. It may be also mean that it was already aborted.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case HttpStatusCode.Unauthorized: return notify({
- type: "error",
- title: i18n.str`Cashout not found. It may be also mean that it was already aborted.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED: return notify({
- type: "error",
- title: i18n.str`Cashout not found. It may be also mean that it was already aborted.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: assertUnreachable(resp)
+ case HttpStatusCode.NotFound:
+ return notify({
+ type: "error",
+ title: i18n.str`Cashout not found. It may be also mean that it was already aborted.`,
+ description: resp.detail.hint as TranslatedString,
+ debug: resp.detail,
+ });
+ case HttpStatusCode.Unauthorized:
+ return notify({
+ type: "error",
+ title: i18n.str`Cashout not found. It may be also mean that it was already aborted.`,
+ description: resp.detail.hint as TranslatedString,
+ debug: resp.detail,
+ });
+ case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED:
+ return notify({
+ type: "error",
+ title: i18n.str`Cashout not found. It may be also mean that it was already aborted.`,
+ description: resp.detail.hint as TranslatedString,
+ debug: resp.detail,
+ });
+ default:
+ assertUnreachable(resp);
}
}
- })
+ });
}
async function completeChallenge() {
@@ -121,55 +125,68 @@ export function SolveChallengePage({
await handleError(async () => {
{
const resp = await api.confirmChallenge(creds, ch.id, {
- tan: code
+ tan: code,
});
if (resp.type === "fail") {
- setCode("")
+ setCode("");
switch (resp.case) {
- case HttpStatusCode.NotFound: return notify({
- type: "error",
- title: i18n.str`Challenge not found.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case HttpStatusCode.Unauthorized: return notify({
- type: "error",
- title: i18n.str`This user is not authorized to complete this challenge.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case HttpStatusCode.TooManyRequests: return notify({
- type: "error",
- title: i18n.str`Too many attemps, try another code.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case TalerErrorCode.BANK_TAN_CHALLENGE_FAILED: return notify({
- type: "error",
- title: i18n.str`The confirmation code is wrong, try again.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case TalerErrorCode.BANK_TAN_CHALLENGE_EXPIRED: return notify({
- type: "error",
- title: i18n.str`The operation expired.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: assertUnreachable(resp)
+ case HttpStatusCode.NotFound:
+ return notify({
+ type: "error",
+ title: i18n.str`Challenge not found.`,
+ description: resp.detail.hint as TranslatedString,
+ debug: resp.detail,
+ });
+ case HttpStatusCode.Unauthorized:
+ return notify({
+ type: "error",
+ title: i18n.str`This user is not authorized to complete this challenge.`,
+ description: resp.detail.hint as TranslatedString,
+ debug: resp.detail,
+ });
+ case HttpStatusCode.TooManyRequests:
+ return notify({
+ type: "error",
+ title: i18n.str`Too many attemps, try another code.`,
+ description: resp.detail.hint as TranslatedString,
+ debug: resp.detail,
+ });
+ case TalerErrorCode.BANK_TAN_CHALLENGE_FAILED:
+ return notify({
+ type: "error",
+ title: i18n.str`The confirmation code is wrong, try again.`,
+ description: resp.detail.hint as TranslatedString,
+ debug: resp.detail,
+ });
+ case TalerErrorCode.BANK_TAN_CHALLENGE_EXPIRED:
+ return notify({
+ type: "error",
+ title: i18n.str`The operation expired.`,
+ description: resp.detail.hint as TranslatedString,
+ debug: resp.detail,
+ });
+ default:
+ assertUnreachable(resp);
}
}
}
{
const resp = await (async (ch: ChallengeInProgess) => {
switch (ch.operation) {
- case "delete-account": return await api.deleteAccount(creds, ch.id)
- case "update-account": return await api.updateAccount(creds, ch.request, ch.id)
- case "update-password": return await api.updatePassword(creds, ch.request, ch.id)
- case "create-transaction": return await api.createTransaction(creds, ch.request, ch.id)
- case "confirm-withdrawal": return await api.confirmWithdrawalById(creds, ch.request, ch.id)
- case "create-cashout": return await api.createCashout(creds, ch.request, ch.id)
- default: assertUnreachable(ch)
+ case "delete-account":
+ return await api.deleteAccount(creds, ch.id);
+ case "update-account":
+ return await api.updateAccount(creds, ch.request, ch.id);
+ case "update-password":
+ return await api.updatePassword(creds, ch.request, ch.id);
+ case "create-transaction":
+ return await api.createTransaction(creds, ch.request, ch.id);
+ case "confirm-withdrawal":
+ return await api.confirmWithdrawalById(creds, ch.request, ch.id);
+ case "create-cashout":
+ return await api.createCashout(creds, ch.request, ch.id);
+ default:
+ assertUnreachable(ch);
}
})(ch);
@@ -180,36 +197,43 @@ export function SolveChallengePage({
title: i18n.str`The operation failed.`,
description: resp.detail.hint as TranslatedString,
debug: resp.detail,
- })
+ });
}
- // another challenge required
+ // another challenge required, save the request and the ID
+ // @ts-expect-error no need to check the type of request, since it will be the same as the previous request
updateBankState("currentChallenge", {
operation: ch.operation,
id: String(resp.body.challenge_id),
sent: AbsoluteTime.never(),
- request: ch.request as any,
- })
+ request: ch.request,
+ });
return notify({
type: "info",
title: i18n.str`The operation needs another confirmation to complete.`,
- })
+ });
}
- updateBankState("currentChallenge", undefined)
- return onContinue()
+ updateBankState("currentChallenge", undefined);
+ return onChallengeCompleted();
}
- })
+ });
}
const subtitle = ((op): TranslatedString => {
switch (op) {
- case "delete-account": return i18n.str`Account delete`
- case "update-account": return i18n.str`Account update`
- case "update-password": return i18n.str`Password update`
- case "create-transaction": return i18n.str`Wire transfer`
- case "confirm-withdrawal": return i18n.str`Withdrawal`
- case "create-cashout": return i18n.str`Cashout`
+ case "delete-account":
+ return i18n.str`Account delete`;
+ case "update-account":
+ return i18n.str`Account update`;
+ case "update-password":
+ return i18n.str`Password update`;
+ case "create-transaction":
+ return i18n.str`Wire transfer`;
+ case "confirm-withdrawal":
+ return i18n.str`Withdrawal`;
+ case "create-cashout":
+ return i18n.str`Cashout`;
}
- })(ch.operation)
+ })(ch.operation);
return (
<Fragment>
@@ -217,25 +241,29 @@ export function SolveChallengePage({
<div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
<div class="px-4 sm:px-0">
<h2 class="text-base font-semibold leading-7 text-gray-900">
- <span class="text-sm text-black font-semibold leading-6 " id="availability-label">
+ <span
+ class="text-sm text-black font-semibold leading-6 "
+ id="availability-label"
+ >
<i18n.Translate>Confirm the operation</i18n.Translate>
</span>
</h2>
- <span>
- {subtitle}
- </span>
+ <span>{subtitle}</span>
</div>
<div class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2">
- <ChallengeDetails challenge={bankState.currentChallenge} onStart={startChallenge} />
- {ch.info &&
+ <ChallengeDetails
+ challenge={bankState.currentChallenge}
+ onStart={startChallenge}
+ />
+ {ch.info && (
<div class="mt-3 text-sm leading-6">
<form
class="bg-white shadow-sm ring-1 ring-gray-900/5"
autoCapitalize="none"
autoCorrect="off"
- onSubmit={e => {
- e.preventDefault()
+ onSubmit={(e) => {
+ e.preventDefault();
}}
>
<div class="px-4 py-6 sm:p-8">
@@ -252,314 +280,408 @@ export function SolveChallengePage({
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={code ?? ""}
required
-
name="answer"
id="answer"
autocomplete="off"
onChange={(e): void => {
- setCode(e.currentTarget.value)
+ setCode(e.currentTarget.value);
}}
/>
</div>
- <ShowInputErrorLabel message={errors?.code} isDirty={code !== undefined} />
+ <ShowInputErrorLabel
+ message={errors?.code}
+ isDirty={code !== undefined}
+ />
</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">
- <button type="button"
+ <a
+ href={routeClose.url({})}
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={() => {
- updateBankState("currentChallenge", undefined)
- onContinue()
- }}
>
<i18n.Translate>Cancel</i18n.Translate>
- </button>
- <button type="submit"
+ </a>
+ <button
+ type="submit"
class="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"
disabled={!!errors}
onClick={(e) => {
- completeChallenge()
+ completeChallenge();
+ e.preventDefault();
}}
>
<i18n.Translate>Confirm</i18n.Translate>
</button>
</div>
</form>
-
- {/* <ShouldBeSameUser username={details.username}> */}
- {/* </ShouldBeSameUser> */}
</div>
- }
+ )}
</div>
</div>
</Fragment>
-
);
}
-function ChallengeDetails({ challenge, onStart }: { challenge: ChallengeInProgess, onStart: () => void }): VNode {
+function ChallengeDetails({
+ challenge,
+ onStart,
+}: {
+ challenge: ChallengeInProgess;
+ onStart: () => void;
+}): VNode {
const { i18n, dateLocale } = useTranslationContext();
const { config } = useBankCoreApiContext();
- return <div class="px-4 mt-4 ">
- <div class="w-full">
- <div class="flex justify-center">
-
- {challenge.info ?
- <button type="submit"
- class="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"
- onClick={(e) => {
- onStart()
- }}
- >
- <i18n.Translate>Send again</i18n.Translate>
- </button>
- :
- <button type="submit"
- class="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"
- onClick={(e) => {
- onStart()
- }}
- >
- <i18n.Translate>Send code</i18n.Translate>
- </button>
- }
- </div>
- <div class="mt-6 border-t border-gray-100">
- <h2 class="text-base font-semibold leading-7 text-gray-900">
- <span class="text-sm text-black font-semibold leading-6 " id="availability-label">
- <i18n.Translate>Operation details</i18n.Translate>
- </span>
- </h2>
- <dl class="divide-y divide-gray-100">
- {((): VNode => {
- switch (challenge.operation) {
- case "delete-account": return <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">Account</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{challenge.request}</dd>
- </div>
- case "create-transaction": {
- const payto = parsePaytoUri(challenge.request.payto_uri)!
- return <Fragment>
- {challenge.request.amount &&
- <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">
- <RenderAmount value={Amounts.parseOrThrow(challenge.request.amount)} spec={config.currency_specification} />
- </dd>
- </div>
- }
- {payto.isKnown && payto.targetType === "iban" &&
- <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">To account</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- {payto.iban}
- </dd>
- </div>
- }
- </Fragment>
- }
- case "confirm-withdrawal": return <ShowWithdrawalDetails id={challenge.request} />
- case "create-cashout": {
- return <ShowCashoutDetails request={challenge.request} />
- }
- case "update-account": {
- return <Fragment>
- {challenge.request.cashout_payto_uri !== undefined &&
- <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">Cashout account</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- {challenge.request.cashout_payto_uri}
- </dd>
- </div>
- }
- {challenge.request.contact_data?.email !== undefined &&
- <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">Email</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- {challenge.request.contact_data?.email}
- </dd>
- </div>
- }
- {challenge.request.contact_data?.phone !== undefined &&
- <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">Phone</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- {challenge.request.contact_data?.phone}
- </dd>
- </div>
- }
- {challenge.request.debit_threshold !== undefined &&
- <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">Debit threshold</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- <RenderAmount value={Amounts.parseOrThrow(challenge.request.debit_threshold)} spec={config.currency_specification} />
- </dd>
- </div>
- }
- {challenge.request.is_public !== undefined &&
- <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">Is this account public?</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- {challenge.request.is_public ? "enable" : "disable"}
- </dd>
- </div>
- }
- {challenge.request.name !== undefined &&
- <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">Name</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- {challenge.request.name}
- </dd>
- </div>
- }
- {challenge.request.tan_channel !== undefined &&
+ return (
+ <div class="px-4 mt-4 ">
+ <div class="w-full">
+ <div class="flex justify-center">
+ {challenge.info ? (
+ <button
+ type="submit"
+ class="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"
+ onClick={(e) => {
+ onStart();
+ e.preventDefault();
+ }}
+ >
+ <i18n.Translate>Send again</i18n.Translate>
+ </button>
+ ) : (
+ <button
+ type="submit"
+ class="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"
+ onClick={(e) => {
+ onStart();
+ e.preventDefault();
+ }}
+ >
+ <i18n.Translate>Send code</i18n.Translate>
+ </button>
+ )}
+ </div>
+ <div class="mt-6 border-t border-gray-100">
+ <h2 class="text-base font-semibold leading-7 text-gray-900">
+ <span
+ class="text-sm text-black font-semibold leading-6 "
+ id="availability-label"
+ >
+ <i18n.Translate>Operation details</i18n.Translate>
+ </span>
+ </h2>
+ <dl class="divide-y divide-gray-100">
+ {((): VNode => {
+ switch (challenge.operation) {
+ case "delete-account":
+ return (
<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">Authentication channel</dt>
+ <dt class="text-sm font-medium leading-6 text-gray-900">
+ Account
+ </dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- {challenge.request.tan_channel}
+ {challenge.request}
</dd>
</div>
- }
- </Fragment>
+ );
+ case "create-transaction": {
+ const payto = parsePaytoUri(challenge.request.payto_uri)!;
+ return (
+ <Fragment>
+ {challenge.request.amount && (
+ <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">
+ <RenderAmount
+ value={Amounts.parseOrThrow(
+ challenge.request.amount,
+ )}
+ spec={config.currency_specification}
+ />
+ </dd>
+ </div>
+ )}
+ {payto.isKnown && payto.targetType === "iban" && (
+ <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">
+ To account
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ {payto.iban}
+ </dd>
+ </div>
+ )}
+ </Fragment>
+ );
+ }
+ case "confirm-withdrawal":
+ return <ShowWithdrawalDetails id={challenge.request} />;
+ case "create-cashout": {
+ return <ShowCashoutDetails request={challenge.request} />;
+ }
+ case "update-account": {
+ return (
+ <Fragment>
+ {challenge.request.cashout_payto_uri !== undefined && (
+ <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">
+ Cashout account
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ {challenge.request.cashout_payto_uri}
+ </dd>
+ </div>
+ )}
+ {challenge.request.contact_data?.email !== undefined && (
+ <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">
+ Email
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ {challenge.request.contact_data?.email}
+ </dd>
+ </div>
+ )}
+ {challenge.request.contact_data?.phone !== undefined && (
+ <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">
+ Phone
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ {challenge.request.contact_data?.phone}
+ </dd>
+ </div>
+ )}
+ {challenge.request.debit_threshold !== undefined && (
+ <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">
+ Debit threshold
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ <RenderAmount
+ value={Amounts.parseOrThrow(
+ challenge.request.debit_threshold,
+ )}
+ spec={config.currency_specification}
+ />
+ </dd>
+ </div>
+ )}
+ {challenge.request.is_public !== undefined && (
+ <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">
+ Is this account public?
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ {challenge.request.is_public ? "enable" : "disable"}
+ </dd>
+ </div>
+ )}
+ {challenge.request.name !== undefined && (
+ <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">
+ Name
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ {challenge.request.name}
+ </dd>
+ </div>
+ )}
+ {challenge.request.tan_channel !== undefined && (
+ <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">
+ Authentication channel
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ {challenge.request.tan_channel}
+ </dd>
+ </div>
+ )}
+ </Fragment>
+ );
+ }
+ case "update-password": {
+ return (
+ <Fragment>
+ <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">
+ New password
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ {challenge.request.new_password}
+ </dd>
+ </div>
+ </Fragment>
+ );
+ }
+ default:
+ assertUnreachable(challenge);
}
- case "update-password": {
- return <Fragment>
- <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">New password</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- {challenge.request.new_password}
- </dd>
- </div>
- </Fragment>
- }
- default: assertUnreachable(challenge)
- }
- })()}
+ })()}
- {challenge.info &&
- <h2 class="text-base font-semibold leading-7 text-gray-900">
- <span class="text-sm text-black font-semibold leading-6 " id="availability-label">
- <i18n.Translate>Challenge details</i18n.Translate>
- </span>
- </h2>
- }
- {challenge.sent.t_ms !== "never" &&
- <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"><i18n.Translate>Sent at</i18n.Translate></dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- {format(challenge.sent.t_ms, "dd/MM/yyyy HH:mm:ss", { locale: dateLocale })}
- </dd>
- </div>
- }
- {challenge.info &&
- <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">
- {((ch: TalerCorebankApi.TanChannel): VNode => {
- switch (ch) {
- case TalerCorebankApi.TanChannel.SMS: return <i18n.Translate>To phone</i18n.Translate>
- case TalerCorebankApi.TanChannel.EMAIL: return <i18n.Translate>To email</i18n.Translate>
- default: assertUnreachable(ch)
- }
- })(challenge.info.tan_channel)}
- </dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- {challenge.info.tan_info}
- </dd>
- </div>
- }
-
- </dl>
+ {challenge.info && (
+ <h2 class="text-base font-semibold leading-7 text-gray-900">
+ <span
+ class="text-sm text-black font-semibold leading-6 "
+ id="availability-label"
+ >
+ <i18n.Translate>Challenge details</i18n.Translate>
+ </span>
+ </h2>
+ )}
+ {challenge.sent.t_ms !== "never" && (
+ <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">
+ <i18n.Translate>Sent at</i18n.Translate>
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ {format(challenge.sent.t_ms, "dd/MM/yyyy HH:mm:ss", {
+ locale: dateLocale,
+ })}
+ </dd>
+ </div>
+ )}
+ {challenge.info && (
+ <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">
+ {((ch: TalerCorebankApi.TanChannel): VNode => {
+ switch (ch) {
+ case TalerCorebankApi.TanChannel.SMS:
+ return <i18n.Translate>To phone</i18n.Translate>;
+ case TalerCorebankApi.TanChannel.EMAIL:
+ return <i18n.Translate>To email</i18n.Translate>;
+ default:
+ assertUnreachable(ch);
+ }
+ })(challenge.info.tan_channel)}
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ {challenge.info.tan_info}
+ </dd>
+ </div>
+ )}
+ </dl>
+ </div>
</div>
</div>
- </div>
+ );
}
function ShowWithdrawalDetails({ id }: { id: string }): VNode {
- const { i18n } = useTranslationContext();
- const details = useWithdrawalDetails(id)
+ const details = useWithdrawalDetails(id);
const { config } = useBankCoreApiContext();
if (!details) {
- return <Loading />
+ return <Loading />;
}
if (details instanceof TalerError) {
- return <ErrorLoadingWithDebug error={details} />
+ return <ErrorLoadingWithDebug error={details} />;
}
if (details.type === "fail") {
switch (details.case) {
case HttpStatusCode.BadRequest:
- case HttpStatusCode.NotFound: return <OperationNotFound onClose={undefined} />
- default: assertUnreachable(details)
+ case HttpStatusCode.NotFound:
+ return <OperationNotFound routeClose={undefined} />;
+ default:
+ assertUnreachable(details);
}
}
- return <Fragment>
- <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">
- <RenderAmount value={Amounts.parseOrThrow(details.body.amount)} spec={config.currency_specification} />
- </dd>
- </div>
- {details.body.selected_reserve_pub !== undefined &&
- <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">Withdraw id</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0" title={details.body.selected_reserve_pub}>
- {details.body.selected_reserve_pub.substring(0, 16)}...
- </dd>
- </div>
- }
- {details.body.selected_exchange_account !== undefined &&
+ return (
+ <Fragment>
<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">To account</dt>
+ <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">
- {details.body.selected_exchange_account}
+ <RenderAmount
+ value={Amounts.parseOrThrow(details.body.amount)}
+ spec={config.currency_specification}
+ />
</dd>
</div>
- }
- </Fragment>
+ {details.body.selected_reserve_pub !== undefined && (
+ <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">
+ Withdraw id
+ </dt>
+ <dd
+ class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"
+ title={details.body.selected_reserve_pub}
+ >
+ {details.body.selected_reserve_pub.substring(0, 16)}...
+ </dd>
+ </div>
+ )}
+ {details.body.selected_exchange_account !== undefined && (
+ <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">
+ To account
+ </dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ {details.body.selected_exchange_account}
+ </dd>
+ </div>
+ )}
+ </Fragment>
+ );
}
-function ShowCashoutDetails({ request }: { request: TalerCorebankApi.CashoutRequest }): VNode {
+function ShowCashoutDetails({
+ request,
+}: {
+ request: TalerCorebankApi.CashoutRequest;
+}): VNode {
const { i18n } = useTranslationContext();
const info = useConversionInfo();
if (!info) {
- return <Loading />
+ return <Loading />;
}
if (info instanceof TalerError) {
- return <ErrorLoadingWithDebug error={info} />
+ return <ErrorLoadingWithDebug error={info} />;
}
if (info.type === "fail") {
switch (info.case) {
case HttpStatusCode.NotImplemented: {
- return <Attention type="danger" title={i18n.str`Cashout not implemented`}>
- </Attention>;
+ return (
+ <Attention
+ type="danger"
+ title={i18n.str`Cashout not implemented`}
+ ></Attention>
+ );
}
- default: assertUnreachable(info.case)
+ default:
+ assertUnreachable(info.case);
}
}
-
- return <Fragment>
- {request.subject !== undefined &&
+ return (
+ <Fragment>
+ {request.subject !== undefined && (
+ <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">Subject</dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ {request.subject}
+ </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">Subject</dt>
+ <dt class="text-sm font-medium leading-6 text-gray-900">Debit</dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- {request.subject}
+ <RenderAmount
+ value={Amounts.parseOrThrow(request.amount_credit)}
+ spec={info.body.regional_currency_specification}
+ />
</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">Debit</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- <RenderAmount value={Amounts.parseOrThrow(request.amount_credit)} spec={info.body.regional_currency_specification} />
- </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">Credit</dt>
- <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
- <RenderAmount value={Amounts.parseOrThrow(request.amount_credit)} spec={info.body.fiat_currency_specification} />
- </dd>
- </div>
- </Fragment>
-} \ No newline at end of file
+ <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">Credit</dt>
+ <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+ <RenderAmount
+ value={Amounts.parseOrThrow(request.amount_credit)}
+ spec={info.body.fiat_currency_specification}
+ />
+ </dd>
+ </div>
+ </Fragment>
+ );
+}