aboutsummaryrefslogtreecommitdiff
path: root/packages/bank-ui/src/pages/regional
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-03-11 14:56:25 -0300
committerSebastian <sebasjm@gmail.com>2024-03-11 14:57:48 -0300
commit37f46f4d6b821d163c3e4db5c374b1120212ac74 (patch)
tree641c5bccd6d1b77fa440e67b80543eec9378ef4a /packages/bank-ui/src/pages/regional
parent4cbe754aca72b6edee922e3a84f251030293f088 (diff)
downloadwallet-core-37f46f4d6b821d163c3e4db5c374b1120212ac74.tar.xz
obs and cancel request, plus lint
Diffstat (limited to 'packages/bank-ui/src/pages/regional')
-rw-r--r--packages/bank-ui/src/pages/regional/ConversionConfig.tsx1464
-rw-r--r--packages/bank-ui/src/pages/regional/CreateCashout.tsx104
-rw-r--r--packages/bank-ui/src/pages/regional/ShowCashoutDetails.tsx34
3 files changed, 914 insertions, 688 deletions
diff --git a/packages/bank-ui/src/pages/regional/ConversionConfig.tsx b/packages/bank-ui/src/pages/regional/ConversionConfig.tsx
index 8845ec9a0..818a131e0 100644
--- a/packages/bank-ui/src/pages/regional/ConversionConfig.tsx
+++ b/packages/bank-ui/src/pages/regional/ConversionConfig.tsx
@@ -15,13 +15,14 @@
*/
import {
+ AbsoluteTime,
AmountJson,
Amounts,
HttpStatusCode,
TalerBankConversionApi,
TalerError,
TranslatedString,
- assertUnreachable
+ assertUnreachable,
} from "@gnu-taler/taler-util";
import {
Attention,
@@ -30,18 +31,30 @@ import {
ShowInputErrorLabel,
useLocalNotification,
useTranslationContext,
- utils
+ utils,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import { useBankCoreApiContext } from "../../context/config.js";
import { useSessionState } from "../../hooks/session.js";
-import { TransferCalculation, useCashinEstimator, useCashoutEstimator, useConversionInfo } from "../../hooks/regional.js";
+import {
+ TransferCalculation,
+ useCashinEstimator,
+ useCashoutEstimator,
+ useConversionInfo,
+} from "../../hooks/regional.js";
import { RouteDefinition } from "../../route.js";
import { undefinedIfEmpty } from "../../utils.js";
import { InputAmount, RenderAmount } from "../PaytoWireTransferForm.js";
import { ProfileNavigation } from "../ProfileNavigation.js";
-import { FormErrors, FormStatus, FormValues, RecursivePartial, UIField, useFormState } from "../../hooks/form.js";
+import {
+ FormErrors,
+ FormStatus,
+ FormValues,
+ RecursivePartial,
+ UIField,
+ useFormState,
+} from "../../hooks/form.js";
interface Props {
routeMyAccountDetails: RouteDefinition;
@@ -53,11 +66,12 @@ interface Props {
onUpdateSuccess: () => void;
}
-type FormType = { amount: AmountJson, conv: TalerBankConversionApi.ConversionRate }
-
+type FormType = {
+ amount: AmountJson;
+ conv: TalerBankConversionApi.ConversionRate;
+};
function useComponentState({
- onUpdateSuccess,
routeCancel,
routeConversionConfig,
routeMyAccountCashout,
@@ -67,9 +81,11 @@ function useComponentState({
}: Props): utils.RecursiveState<VNode> {
const { i18n } = useTranslationContext();
- const result = useConversionInfo()
- const info = result && !(result instanceof TalerError) && result.type === "ok" ?
- result.body : undefined;
+ const result = useConversionInfo();
+ const info =
+ result && !(result instanceof TalerError) && result.type === "ok"
+ ? result.body
+ : undefined;
const { state: credentials } = useSessionState();
const creds =
@@ -78,17 +94,17 @@ function useComponentState({
: credentials;
if (!info) {
- return <i18n.Translate>loading...</i18n.Translate>
+ return <i18n.Translate>loading...</i18n.Translate>;
}
if (!creds) {
- return <i18n.Translate>only admin can setup conversion</i18n.Translate>
+ return <i18n.Translate>only admin can setup conversion</i18n.Translate>;
}
- return () => {
+ return function afterComponentLoads() {
const { i18n } = useTranslationContext();
- const { bank, conversion, config } = useBankCoreApiContext();
+ const { conversion } = useBankCoreApiContext();
const [notification, notify, handleError] = useLocalNotification();
@@ -96,66 +112,91 @@ function useComponentState({
amount: "100",
conv: {
cashin_min_amount: info.conversion_rate.cashin_min_amount.split(":")[1],
- cashin_tiny_amount: info.conversion_rate.cashin_tiny_amount.split(":")[1],
+ cashin_tiny_amount:
+ info.conversion_rate.cashin_tiny_amount.split(":")[1],
cashin_fee: info.conversion_rate.cashin_fee.split(":")[1],
cashin_ratio: info.conversion_rate.cashin_ratio,
cashin_rounding_mode: info.conversion_rate.cashin_rounding_mode,
- cashout_min_amount: info.conversion_rate.cashout_min_amount.split(":")[1],
- cashout_tiny_amount: info.conversion_rate.cashout_tiny_amount.split(":")[1],
+ cashout_min_amount:
+ info.conversion_rate.cashout_min_amount.split(":")[1],
+ cashout_tiny_amount:
+ info.conversion_rate.cashout_tiny_amount.split(":")[1],
cashout_fee: info.conversion_rate.cashout_fee.split(":")[1],
cashout_ratio: info.conversion_rate.cashout_ratio,
cashout_rounding_mode: info.conversion_rate.cashout_rounding_mode,
- }
- }
+ },
+ };
const [form, status] = useFormState<FormType>(
initalState,
- createFormValidator(i18n, info.regional_currency, info.fiat_currency)
- )
+ createFormValidator(i18n, info.regional_currency, info.fiat_currency),
+ );
- const {
- estimateByDebit: calculateCashoutFromDebit,
- } = useCashoutEstimator();
+ const { estimateByDebit: calculateCashoutFromDebit } =
+ useCashoutEstimator();
- const {
- estimateByDebit: calculateCashinFromDebit,
- } = useCashinEstimator();
+ const { estimateByDebit: calculateCashinFromDebit } = useCashinEstimator();
- const [calculationResult, setCalc] = useState<{ cashin: TransferCalculation, cashout: TransferCalculation }>()
+ const [calculationResult, setCalc] = useState<{
+ cashin: TransferCalculation;
+ cashout: TransferCalculation;
+ }>();
useEffect(() => {
async function doAsync() {
await handleError(async () => {
if (!info) return;
if (!form.amount?.value || form.amount.error) return;
- const in_amount = Amounts.parseOrThrow(`${info.fiat_currency}:${form.amount.value}`)
- const in_fee = Amounts.parseOrThrow(info.conversion_rate.cashin_fee)
+ const in_amount = Amounts.parseOrThrow(
+ `${info.fiat_currency}:${form.amount.value}`,
+ );
+ const in_fee = Amounts.parseOrThrow(info.conversion_rate.cashin_fee);
const cashin = await calculateCashinFromDebit(in_amount, in_fee);
if (cashin === "amount-is-too-small") {
- setCalc(undefined)
+ setCalc(undefined);
return;
}
// const out_amount = Amounts.parseOrThrow(`${info.regional_currency}:${form.amount.value}`)
- const out_fee = Amounts.parseOrThrow(info.conversion_rate.cashout_fee)
- const cashout = await calculateCashoutFromDebit(cashin.credit, out_fee);
+ const out_fee = Amounts.parseOrThrow(
+ info.conversion_rate.cashout_fee,
+ );
+ const cashout = await calculateCashoutFromDebit(
+ cashin.credit,
+ out_fee,
+ );
setCalc({ cashin, cashout });
});
}
doAsync();
- }, [form.amount?.value, form.conv?.cashin_fee?.value, form.conv?.cashout_fee?.value]);
-
- const [section, setSection] = useState<"detail" | "cashout" | "cashin">("detail")
- const cashinCalc = calculationResult?.cashin === "amount-is-too-small" ? undefined : calculationResult?.cashin
- const cashoutCalc = calculationResult?.cashout === "amount-is-too-small" ? undefined : calculationResult?.cashout
+ }, [
+ form.amount?.value,
+ form.conv?.cashin_fee?.value,
+ form.conv?.cashout_fee?.value,
+ ]);
+
+ const [section, setSection] = useState<"detail" | "cashout" | "cashin">(
+ "detail",
+ );
+ const cashinCalc =
+ calculationResult?.cashin === "amount-is-too-small"
+ ? undefined
+ : calculationResult?.cashin;
+ const cashoutCalc =
+ calculationResult?.cashout === "amount-is-too-small"
+ ? undefined
+ : calculationResult?.cashout;
async function doUpdate() {
- if (!creds) return
+ if (!creds) return;
await handleError(async () => {
if (status.status === "fail") return;
- const resp = await conversion.updateConversionRate(creds.token, status.result.conv)
+ const resp = await conversion.updateConversionRate(
+ creds.token,
+ status.result.conv,
+ );
if (resp.type === "ok") {
- setSection("detail")
+ setSection("detail");
} else {
switch (resp.case) {
case HttpStatusCode.Unauthorized: {
@@ -164,6 +205,7 @@ function useComponentState({
title: i18n.str`Wrong credentials`,
description: resp.detail.hint as TranslatedString,
debug: resp.detail,
+ when: AbsoluteTime.now(),
});
}
case HttpStatusCode.NotImplemented: {
@@ -172,6 +214,7 @@ function useComponentState({
title: i18n.str`Conversion is disabled`,
description: resp.detail.hint as TranslatedString,
debug: resp.detail,
+ when: AbsoluteTime.now(),
});
}
default:
@@ -181,16 +224,16 @@ function useComponentState({
});
}
- const in_ratio = Number.parseFloat(info.conversion_rate.cashin_ratio)
- const out_ratio = Number.parseFloat(info.conversion_rate.cashout_ratio)
+ const in_ratio = Number.parseFloat(info.conversion_rate.cashin_ratio);
+ const out_ratio = Number.parseFloat(info.conversion_rate.cashout_ratio);
const both_high = in_ratio > 1 && out_ratio > 1;
const both_low = in_ratio < 1 && out_ratio < 1;
-
return (
<div>
- <ProfileNavigation current="conversion"
+ <ProfileNavigation
+ current="conversion"
routeMyAccountCashout={routeMyAccountCashout}
routeMyAccountDelete={routeMyAccountDelete}
routeMyAccountDetails={routeMyAccountDetails}
@@ -200,7 +243,6 @@ function useComponentState({
<LocalNotificationBanner notification={notification} />
<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">
<i18n.Translate>Conversion</i18n.Translate>
@@ -218,7 +260,7 @@ function useComponentState({
aria-labelledby="project-type-0-label"
aria-describedby="project-type-0-description-0 project-type-0-description-1"
onChange={() => {
- setSection("detail")
+ setSection("detail");
}}
/>
<span class="flex flex-1">
@@ -242,7 +284,7 @@ function useComponentState({
aria-labelledby="project-type-1-label"
aria-describedby="project-type-1-description-0 project-type-1-description-1"
onChange={() => {
- setSection("cashout")
+ setSection("cashout");
}}
/>
<span class="flex flex-1">
@@ -265,7 +307,7 @@ function useComponentState({
aria-labelledby="project-type-1-label"
aria-describedby="project-type-1-description-0 project-type-1-description-1"
onChange={() => {
- setSection("cashin")
+ setSection("cashin");
}}
/>
<span class="flex flex-1">
@@ -277,7 +319,6 @@ function useComponentState({
</span>
</label>
</div>
-
</div>
<form
@@ -288,8 +329,9 @@ function useComponentState({
e.preventDefault();
}}
>
- {section == "cashin" &&
- <ConversionForm id="cashin"
+ {section == "cashin" && (
+ <ConversionForm
+ id="cashin"
inputCurrency={info.fiat_currency}
outputCurrency={info.regional_currency}
fee={form?.conv?.cashin_fee}
@@ -297,682 +339,830 @@ function useComponentState({
ratio={form?.conv?.cashin_ratio}
rounding={form?.conv?.cashin_rounding_mode}
tiny={form?.conv?.cashin_tiny_amount}
- />}
-
- {section == "cashout" && <Fragment>
- <ConversionForm id="cashout"
- inputCurrency={info.regional_currency}
- outputCurrency={info.fiat_currency}
- fee={form?.conv?.cashout_fee}
- minimum={form?.conv?.cashout_min_amount}
- ratio={form?.conv?.cashout_ratio}
- rounding={form?.conv?.cashout_rounding_mode}
- tiny={form?.conv?.cashout_tiny_amount}
/>
- </Fragment>}
-
- {section == "detail" && <Fragment>
- <div class="px-6 pt-6">
- <div class="justify-between items-center flex ">
- <dt class="text-sm text-gray-600">
- <i18n.Translate>Cashin ratio</i18n.Translate>
- </dt>
- <dd class="text-sm text-gray-900">
- {info.conversion_rate.cashin_ratio}
- </dd>
- </div>
- </div>
+ )}
+
+ {section == "cashout" && (
+ <Fragment>
+ <ConversionForm
+ id="cashout"
+ inputCurrency={info.regional_currency}
+ outputCurrency={info.fiat_currency}
+ fee={form?.conv?.cashout_fee}
+ minimum={form?.conv?.cashout_min_amount}
+ ratio={form?.conv?.cashout_ratio}
+ rounding={form?.conv?.cashout_rounding_mode}
+ tiny={form?.conv?.cashout_tiny_amount}
+ />
+ </Fragment>
+ )}
- <div class="px-6 pt-6">
- <div class="justify-between items-center flex ">
- <dt class="text-sm text-gray-600">
- <i18n.Translate>Cashout ratio</i18n.Translate>
- </dt>
- <dd class="text-sm text-gray-900">
- {info.conversion_rate.cashout_ratio}
- </dd>
+ {section == "detail" && (
+ <Fragment>
+ <div class="px-6 pt-6">
+ <div class="justify-between items-center flex ">
+ <dt class="text-sm text-gray-600">
+ <i18n.Translate>Cashin ratio</i18n.Translate>
+ </dt>
+ <dd class="text-sm text-gray-900">
+ {info.conversion_rate.cashin_ratio}
+ </dd>
+ </div>
</div>
- </div>
- {both_low || both_high ? <div class="p-4">
- <Attention title={i18n.str`Bad ratios`} type="warning">
- <i18n.Translate>
- One of the ratios should be higher or equal than 1 an the other should be lower or equal than 1.
- </i18n.Translate>
- </Attention>
- </div> : undefined}
-
- <div class="px-6 pt-6">
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
- <div class="sm:col-span-5">
- <label
- for="amount"
- class="block text-sm font-medium leading-6 text-gray-900"
- >{i18n.str`Initial amount`}</label>
- <InputAmount
- name="amount"
- left
- currency={info.fiat_currency}
- value={form.amount?.value ?? ""}
- onChange={form.amount?.onUpdate}
- />
- <ShowInputErrorLabel
- message={form.amount?.error}
- isDirty={form.amount?.value !== undefined}
- />
- <p class="mt-2 text-sm text-gray-500">
- <i18n.Translate>Use it to test how the conversion will affect the amount.</i18n.Translate>
- </p>
+ <div class="px-6 pt-6">
+ <div class="justify-between items-center flex ">
+ <dt class="text-sm text-gray-600">
+ <i18n.Translate>Cashout ratio</i18n.Translate>
+ </dt>
+ <dd class="text-sm text-gray-900">
+ {info.conversion_rate.cashout_ratio}
+ </dd>
</div>
</div>
- </div>
- {!cashoutCalc || !cashinCalc ? undefined : (
+ {both_low || both_high ? (
+ <div class="p-4">
+ <Attention title={i18n.str`Bad ratios`} type="warning">
+ <i18n.Translate>
+ One of the ratios should be higher or equal than 1 an
+ the other should be lower or equal than 1.
+ </i18n.Translate>
+ </Attention>
+ </div>
+ ) : undefined}
+
<div class="px-6 pt-6">
- <div class="sm:col-span-5">
- <dl class="mt-4 space-y-4">
- <div class="justify-between items-center flex ">
- <dt class="text-sm text-gray-600">
- <i18n.Translate>Sending to this bank</i18n.Translate>
- </dt>
- <dd class="text-sm text-gray-900">
- <RenderAmount
- value={cashinCalc.debit}
- negative
- withColor
- spec={info.regional_currency_specification}
- />
- </dd>
- </div>
+ <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+ <div class="sm:col-span-5">
+ <label
+ for="amount"
+ class="block text-sm font-medium leading-6 text-gray-900"
+ >{i18n.str`Initial amount`}</label>
+ <InputAmount
+ name="amount"
+ left
+ currency={info.fiat_currency}
+ value={form.amount?.value ?? ""}
+ onChange={form.amount?.onUpdate}
+ />
+ <ShowInputErrorLabel
+ message={form.amount?.error}
+ isDirty={form.amount?.value !== undefined}
+ />
+ <p class="mt-2 text-sm text-gray-500">
+ <i18n.Translate>
+ Use it to test how the conversion will affect the
+ amount.
+ </i18n.Translate>
+ </p>
+ </div>
+ </div>
+ </div>
- {Amounts.isZero(cashinCalc.beforeFee) ? undefined : (
- <div class="flex items-center justify-between afu ">
- <dt class="flex items-center text-sm text-gray-600">
- <span>
- <i18n.Translate>Converted</i18n.Translate>
- </span>
+ {!cashoutCalc || !cashinCalc ? undefined : (
+ <div class="px-6 pt-6">
+ <div class="sm:col-span-5">
+ <dl class="mt-4 space-y-4">
+ <div class="justify-between items-center flex ">
+ <dt class="text-sm text-gray-600">
+ <i18n.Translate>
+ Sending to this bank
+ </i18n.Translate>
</dt>
<dd class="text-sm text-gray-900">
<RenderAmount
- value={cashinCalc.beforeFee}
- spec={info.fiat_currency_specification}
+ value={cashinCalc.debit}
+ negative
+ withColor
+ spec={info.regional_currency_specification}
/>
</dd>
</div>
- )}
- <div class="flex justify-between items-center border-t-2 afu pt-4">
- <dt class="text-lg text-gray-900 font-medium">
- <i18n.Translate>Cashin after fee</i18n.Translate>
- </dt>
- <dd class="text-lg text-gray-900 font-medium">
- <RenderAmount
- value={cashinCalc.credit}
- withColor
- spec={info.fiat_currency_specification}
- />
- </dd>
- </div>
- </dl>
- </div>
-
- <div class="sm:col-span-5">
- <dl class="mt-4 space-y-4">
- <div class="justify-between items-center flex ">
- <dt class="text-sm text-gray-600">
- <i18n.Translate>Sending from this bank</i18n.Translate>
- </dt>
- <dd class="text-sm text-gray-900">
- <RenderAmount
- value={cashoutCalc.debit}
- negative
- withColor
- spec={info.fiat_currency_specification}
- />
- </dd>
- </div>
- {Amounts.isZero(cashoutCalc.beforeFee) ? undefined : (
- <div class="flex items-center justify-between afu">
- <dt class="flex items-center text-sm text-gray-600">
- <span>
- <i18n.Translate>Converted</i18n.Translate>
- </span>
+ {Amounts.isZero(cashinCalc.beforeFee) ? undefined : (
+ <div class="flex items-center justify-between afu ">
+ <dt class="flex items-center text-sm text-gray-600">
+ <span>
+ <i18n.Translate>Converted</i18n.Translate>
+ </span>
+ </dt>
+ <dd class="text-sm text-gray-900">
+ <RenderAmount
+ value={cashinCalc.beforeFee}
+ spec={info.fiat_currency_specification}
+ />
+ </dd>
+ </div>
+ )}
+ <div class="flex justify-between items-center border-t-2 afu pt-4">
+ <dt class="text-lg text-gray-900 font-medium">
+ <i18n.Translate>Cashin after fee</i18n.Translate>
+ </dt>
+ <dd class="text-lg text-gray-900 font-medium">
+ <RenderAmount
+ value={cashinCalc.credit}
+ withColor
+ spec={info.fiat_currency_specification}
+ />
+ </dd>
+ </div>
+ </dl>
+ </div>
+
+ <div class="sm:col-span-5">
+ <dl class="mt-4 space-y-4">
+ <div class="justify-between items-center flex ">
+ <dt class="text-sm text-gray-600">
+ <i18n.Translate>
+ Sending from this bank
+ </i18n.Translate>
</dt>
<dd class="text-sm text-gray-900">
<RenderAmount
- value={cashoutCalc.beforeFee}
+ value={cashoutCalc.debit}
+ negative
+ withColor
+ spec={info.fiat_currency_specification}
+ />
+ </dd>
+ </div>
+
+ {Amounts.isZero(cashoutCalc.beforeFee) ? undefined : (
+ <div class="flex items-center justify-between afu">
+ <dt class="flex items-center text-sm text-gray-600">
+ <span>
+ <i18n.Translate>Converted</i18n.Translate>
+ </span>
+ </dt>
+ <dd class="text-sm text-gray-900">
+ <RenderAmount
+ value={cashoutCalc.beforeFee}
+ spec={info.regional_currency_specification}
+ />
+ </dd>
+ </div>
+ )}
+ <div class="flex justify-between items-center border-t-2 afu pt-4">
+ <dt class="text-lg text-gray-900 font-medium">
+ <i18n.Translate>Cashout after fee</i18n.Translate>
+ </dt>
+ <dd class="text-lg text-gray-900 font-medium">
+ <RenderAmount
+ value={cashoutCalc.credit}
+ withColor
spec={info.regional_currency_specification}
/>
</dd>
</div>
- )}
- <div class="flex justify-between items-center border-t-2 afu pt-4">
- <dt class="text-lg text-gray-900 font-medium">
- <i18n.Translate>Cashout after fee</i18n.Translate>
- </dt>
- <dd class="text-lg text-gray-900 font-medium">
- <RenderAmount
- value={cashoutCalc.credit}
- withColor
- spec={info.regional_currency_specification}
- />
- </dd>
+ </dl>
+ </div>
+
+ {cashoutCalc &&
+ status.status === "ok" &&
+ Amounts.cmp(status.result.amount, cashoutCalc.credit) <
+ 0 ? (
+ <div class="p-4">
+ <Attention
+ title={i18n.str`Bad configuration`}
+ type="warning"
+ >
+ <i18n.Translate>
+ This configuration allows users to cash out more of
+ what has been cashed in.
+ </i18n.Translate>
+ </Attention>
</div>
- </dl>
+ ) : undefined}
</div>
-
- {cashoutCalc && status.status === "ok" && Amounts.cmp(status.result.amount, cashoutCalc.credit) < 0 ? <div class="p-4">
- <Attention title={i18n.str`Bad configuration`} type="warning">
- <i18n.Translate>
- This configuration allows users to cash out more of what has been cashed in.
- </i18n.Translate>
- </Attention>
- </div> : undefined}
- </div>
- )}
- </Fragment>}
-
+ )}
+ </Fragment>
+ )}
<div class="flex items-center justify-between mt-4 gap-x-6 border-t border-gray-900/10 px-4 py-4">
- <a name="cancel"
+ <a
+ name="cancel"
href={routeCancel.url({})}
class="text-sm font-semibold leading-6 text-gray-900"
>
<i18n.Translate>Cancel</i18n.Translate>
</a>
- {section == "cashin" || section == "cashout" ? <Fragment>
- <button
- type="submit"
- name="update conversion"
- 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={async () => {
- doUpdate()
- }}
- >
- <i18n.Translate>Update</i18n.Translate>
- </button>
- </Fragment> : <div />}
+ {section == "cashin" || section == "cashout" ? (
+ <Fragment>
+ <button
+ type="submit"
+ name="update conversion"
+ 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={async () => {
+ doUpdate();
+ }}
+ >
+ <i18n.Translate>Update</i18n.Translate>
+ </button>
+ </Fragment>
+ ) : (
+ <div />
+ )}
</div>
-
-
</form>
</div>
</div>
);
-
- }
+ };
}
export const ConversionConfig = utils.recursive(useComponentState);
/**
- *
- * @param i18n
- * @param regional
- * @param fiat
+ *
+ * @param i18n
+ * @param regional
+ * @param fiat
* @returns form validator
*/
-function createFormValidator(i18n: InternationalizationAPI, regional: string, fiat: string) {
+function createFormValidator(
+ i18n: InternationalizationAPI,
+ regional: string,
+ fiat: string,
+) {
return function check(state: FormValues<FormType>): FormStatus<FormType> {
+ const cashin_min_amount = Amounts.parse(
+ `${fiat}:${state.conv.cashin_min_amount}`,
+ );
+ const cashin_tiny_amount = Amounts.parse(
+ `${regional}:${state.conv.cashin_tiny_amount}`,
+ );
+ const cashin_fee = Amounts.parse(`${regional}:${state.conv.cashin_fee}`);
- const cashin_min_amount = Amounts.parse(`${fiat}:${state.conv.cashin_min_amount}`)
- const cashin_tiny_amount = Amounts.parse(`${regional}:${state.conv.cashin_tiny_amount}`)
- const cashin_fee = Amounts.parse(`${regional}:${state.conv.cashin_fee}`)
-
- const cashout_min_amount = Amounts.parse(`${regional}:${state.conv.cashout_min_amount}`)
- const cashout_tiny_amount = Amounts.parse(`${fiat}:${state.conv.cashout_tiny_amount}`)
- const cashout_fee = Amounts.parse(`${fiat}:${state.conv.cashout_fee}`)
+ const cashout_min_amount = Amounts.parse(
+ `${regional}:${state.conv.cashout_min_amount}`,
+ );
+ const cashout_tiny_amount = Amounts.parse(
+ `${fiat}:${state.conv.cashout_tiny_amount}`,
+ );
+ const cashout_fee = Amounts.parse(`${fiat}:${state.conv.cashout_fee}`);
- const am = Amounts.parse(`${fiat}:${state.amount}`)
+ const am = Amounts.parse(`${fiat}:${state.amount}`);
- const cashin_ratio = Number.parseFloat(state.conv.cashin_ratio ?? "")
- const cashout_ratio = Number.parseFloat(state.conv.cashout_ratio ?? "")
+ const cashin_ratio = Number.parseFloat(state.conv.cashin_ratio ?? "");
+ const cashout_ratio = Number.parseFloat(state.conv.cashout_ratio ?? "");
const errors = undefinedIfEmpty<FormErrors<FormType>>({
conv: undefinedIfEmpty<FormErrors<FormType["conv"]>>({
- cashin_min_amount: !state.conv.cashin_min_amount ? i18n.str`required` :
- !cashin_min_amount ? i18n.str`invalid` :
- undefined,
- cashin_tiny_amount: !state.conv.cashin_tiny_amount ? i18n.str`required` :
- !cashin_tiny_amount ? i18n.str`invalid` :
- undefined,
- cashin_fee: !state.conv.cashin_fee ? i18n.str`required` :
- !cashin_fee ? i18n.str`invalid` :
- undefined,
-
- cashout_min_amount: !state.conv.cashout_min_amount ? i18n.str`required` :
- !cashout_min_amount ? i18n.str`invalid` :
- undefined,
- cashout_tiny_amount: !state.conv.cashin_tiny_amount ? i18n.str`required` :
- !cashout_tiny_amount ? i18n.str`invalid` :
- undefined,
- cashout_fee: !state.conv.cashin_fee ? i18n.str`required` :
- !cashout_fee ? i18n.str`invalid` :
- undefined,
-
- cashin_rounding_mode: !state.conv.cashin_rounding_mode ? i18n.str`required` : undefined,
- cashout_rounding_mode: !state.conv.cashout_rounding_mode ? i18n.str`required` : undefined,
-
- cashin_ratio: !state.conv.cashin_ratio ? i18n.str`required` : Number.isNaN(cashin_ratio) ? i18n.str`invalid` : undefined,
- cashout_ratio: !state.conv.cashout_ratio ? i18n.str`required` : Number.isNaN(cashout_ratio) ? i18n.str`invalid` : undefined,
+ cashin_min_amount: !state.conv.cashin_min_amount
+ ? i18n.str`required`
+ : !cashin_min_amount
+ ? i18n.str`invalid`
+ : undefined,
+ cashin_tiny_amount: !state.conv.cashin_tiny_amount
+ ? i18n.str`required`
+ : !cashin_tiny_amount
+ ? i18n.str`invalid`
+ : undefined,
+ cashin_fee: !state.conv.cashin_fee
+ ? i18n.str`required`
+ : !cashin_fee
+ ? i18n.str`invalid`
+ : undefined,
+
+ cashout_min_amount: !state.conv.cashout_min_amount
+ ? i18n.str`required`
+ : !cashout_min_amount
+ ? i18n.str`invalid`
+ : undefined,
+ cashout_tiny_amount: !state.conv.cashin_tiny_amount
+ ? i18n.str`required`
+ : !cashout_tiny_amount
+ ? i18n.str`invalid`
+ : undefined,
+ cashout_fee: !state.conv.cashin_fee
+ ? i18n.str`required`
+ : !cashout_fee
+ ? i18n.str`invalid`
+ : undefined,
+
+ cashin_rounding_mode: !state.conv.cashin_rounding_mode
+ ? i18n.str`required`
+ : undefined,
+ cashout_rounding_mode: !state.conv.cashout_rounding_mode
+ ? i18n.str`required`
+ : undefined,
+
+ cashin_ratio: !state.conv.cashin_ratio
+ ? i18n.str`required`
+ : Number.isNaN(cashin_ratio)
+ ? i18n.str`invalid`
+ : undefined,
+ cashout_ratio: !state.conv.cashout_ratio
+ ? i18n.str`required`
+ : Number.isNaN(cashout_ratio)
+ ? i18n.str`invalid`
+ : undefined,
}),
- amount: !state.amount ? i18n.str`required` :
- !am ? i18n.str`invalid` :
- undefined,
- })
+ amount: !state.amount
+ ? i18n.str`required`
+ : !am
+ ? i18n.str`invalid`
+ : undefined,
+ });
const result: RecursivePartial<FormType> = {
amount: am,
conv: {
- cashin_fee: !errors?.conv?.cashin_fee ? Amounts.stringify(cashin_fee!) : undefined,
- cashin_min_amount: !errors?.conv?.cashin_min_amount ? Amounts.stringify(cashin_min_amount!) : undefined,
- cashin_ratio: !errors?.conv?.cashin_ratio ? String(cashin_ratio!) : undefined,
- cashin_rounding_mode: !errors?.conv?.cashin_rounding_mode ? (state.conv.cashin_rounding_mode!) : undefined,
- cashin_tiny_amount: !errors?.conv?.cashin_tiny_amount ? Amounts.stringify(cashin_tiny_amount!) : undefined,
- cashout_fee: !errors?.conv?.cashout_fee ? Amounts.stringify(cashout_fee!) : undefined,
- cashout_min_amount: !errors?.conv?.cashout_min_amount ? Amounts.stringify(cashout_min_amount!) : undefined,
- cashout_ratio: !errors?.conv?.cashout_ratio ? String(cashout_ratio!) : undefined,
- cashout_rounding_mode: !errors?.conv?.cashout_rounding_mode ? (state.conv.cashout_rounding_mode!) : undefined,
- cashout_tiny_amount: !errors?.conv?.cashout_tiny_amount ? Amounts.stringify(cashout_tiny_amount!) : undefined,
- }
-
- }
- return errors === undefined ?
- { status: "ok", result: result as FormType, errors } :
- { status: "fail", result, errors }
- }
+ cashin_fee: !errors?.conv?.cashin_fee
+ ? Amounts.stringify(cashin_fee!)
+ : undefined,
+ cashin_min_amount: !errors?.conv?.cashin_min_amount
+ ? Amounts.stringify(cashin_min_amount!)
+ : undefined,
+ cashin_ratio: !errors?.conv?.cashin_ratio
+ ? String(cashin_ratio!)
+ : undefined,
+ cashin_rounding_mode: !errors?.conv?.cashin_rounding_mode
+ ? state.conv.cashin_rounding_mode!
+ : undefined,
+ cashin_tiny_amount: !errors?.conv?.cashin_tiny_amount
+ ? Amounts.stringify(cashin_tiny_amount!)
+ : undefined,
+ cashout_fee: !errors?.conv?.cashout_fee
+ ? Amounts.stringify(cashout_fee!)
+ : undefined,
+ cashout_min_amount: !errors?.conv?.cashout_min_amount
+ ? Amounts.stringify(cashout_min_amount!)
+ : undefined,
+ cashout_ratio: !errors?.conv?.cashout_ratio
+ ? String(cashout_ratio!)
+ : undefined,
+ cashout_rounding_mode: !errors?.conv?.cashout_rounding_mode
+ ? state.conv.cashout_rounding_mode!
+ : undefined,
+ cashout_tiny_amount: !errors?.conv?.cashout_tiny_amount
+ ? Amounts.stringify(cashout_tiny_amount!)
+ : undefined,
+ },
+ };
+ return errors === undefined
+ ? { status: "ok", result: result as FormType, errors }
+ : { status: "fail", result, errors };
+ };
}
-
-function ConversionForm({ id, inputCurrency, outputCurrency, fee, minimum, ratio, rounding, tiny }: {
- inputCurrency: string,
- outputCurrency: string,
- minimum: UIField | undefined,
- tiny: UIField | undefined,
- fee: UIField | undefined,
- rounding: UIField | undefined,
- ratio: UIField | undefined,
- id: string,
+function ConversionForm({
+ id,
+ inputCurrency,
+ outputCurrency,
+ fee,
+ minimum,
+ ratio,
+ rounding,
+ tiny,
+}: {
+ inputCurrency: string;
+ outputCurrency: string;
+ minimum: UIField | undefined;
+ tiny: UIField | undefined;
+ fee: UIField | undefined;
+ rounding: UIField | undefined;
+ ratio: UIField | undefined;
+ id: string;
}): VNode {
const { i18n } = useTranslationContext();
- return <Fragment>
- <div class="px-6 pt-6">
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
- <div class="sm:col-span-5">
- <label
- for="cashin_min_amount"
- class="block text-sm font-medium leading-6 text-gray-900"
- >{i18n.str`Minimum amount`}</label>
- <InputAmount
- name="cashin_min_amount"
- left
- currency={inputCurrency}
- value={minimum?.value ?? ""}
- onChange={minimum?.onUpdate}
+ return (
+ <Fragment>
+ <div class="px-6 pt-6">
+ <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+ <div class="sm:col-span-5">
+ <label
+ for={`${id}_min_amount`}
+ class="block text-sm font-medium leading-6 text-gray-900"
+ >{i18n.str`Minimum amount`}</label>
+ <InputAmount
+ name={`${id}_min_amount`}
+ left
+ currency={inputCurrency}
+ value={minimum?.value ?? ""}
+ onChange={minimum?.onUpdate}
+ />
+ <ShowInputErrorLabel
+ message={minimum?.error}
+ isDirty={minimum?.value !== undefined}
+ />
+ <p class="mt-2 text-sm text-gray-500">
+ <i18n.Translate>
+ Only cashout operation above this threshold will be allowed
+ </i18n.Translate>
+ </p>
+ </div>
+ </div>
+ </div>
+
+ <div class="px-6 pt-6">
+ <label
+ class="block text-sm font-medium leading-6 text-gray-900"
+ for={`${id}_ratio`}
+ >
+ {i18n.str`Ratio`}
+ </label>
+ <div class="mt-2">
+ <input
+ type="number"
+ class="block rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+ name="current"
+ id={`${id}_ratio`}
+ data-error={!!ratio?.error && ratio?.value !== undefined}
+ value={ratio?.value ?? ""}
+ onChange={(e) => {
+ ratio?.onUpdate(e.currentTarget.value);
+ }}
+ autocomplete="off"
/>
<ShowInputErrorLabel
- message={minimum?.error}
- isDirty={minimum?.value !== undefined}
+ message={ratio?.error}
+ isDirty={ratio?.value !== undefined}
/>
- <p class="mt-2 text-sm text-gray-500">
- <i18n.Translate>Only cashout operation above this threshold will be allowed</i18n.Translate>
- </p>
</div>
+ <p class="mt-2 text-sm text-gray-500">
+ <i18n.Translate>Conversion ratio between currencies</i18n.Translate>
+ </p>
</div>
- </div>
-
- <div class="px-6 pt-6">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="password"
- >
- {i18n.str`Ratio`}
- </label>
- <div class="mt-2">
- <input
- type="number"
- class="block rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
- name="current"
- id="cashin_ratio"
- data-error={!!ratio?.error && ratio?.value !== undefined}
- value={ratio?.value ?? ""}
- onChange={(e) => {
- ratio?.onUpdate(e.currentTarget.value);
- }}
- autocomplete="off"
- />
- <ShowInputErrorLabel
- message={ratio?.error}
- isDirty={ratio?.value !== undefined}
- />
+
+ <div class="px-6 pt-4">
+ <Attention title={i18n.str`Example conversion`}>
+ <i18n.Translate>
+ 1 {inputCurrency} will be converted into {ratio?.value}{" "}
+ {outputCurrency}
+ </i18n.Translate>
+ </Attention>
</div>
- <p class="mt-2 text-sm text-gray-500">
- <i18n.Translate>
- Conversion ratio between currencies
- </i18n.Translate>
- </p>
- </div>
-
- <div class="px-6 pt-4">
- <Attention title={i18n.str`Example conversion`}>
- <i18n.Translate>1 {inputCurrency} will be converted into {ratio?.value} {outputCurrency}</i18n.Translate>
- </Attention>
- </div>
-
- <div class="px-6 pt-6">
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
- <div class="sm:col-span-5">
- <label
- for="cashin_tiny_amount"
- class="block text-sm font-medium leading-6 text-gray-900"
- >{i18n.str`Rounding value`}</label>
- <InputAmount
- name="cashin_tiny_amount"
- left
- currency={outputCurrency}
- value={tiny?.value ?? ""}
- onChange={tiny?.onUpdate}
- />
- <ShowInputErrorLabel
- message={tiny?.error}
- isDirty={tiny?.value !== undefined}
- />
- <p class="mt-2 text-sm text-gray-500">
- <i18n.Translate>Smallest difference between two amounts after the ratio is applied.</i18n.Translate>
- </p>
+
+ <div class="px-6 pt-6">
+ <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+ <div class="sm:col-span-5">
+ <label
+ for={`${id}_tiny_amount`}
+ class="block text-sm font-medium leading-6 text-gray-900"
+ >{i18n.str`Rounding value`}</label>
+ <InputAmount
+ name={`${id}_tiny_amount`}
+ left
+ currency={outputCurrency}
+ value={tiny?.value ?? ""}
+ onChange={tiny?.onUpdate}
+ />
+ <ShowInputErrorLabel
+ message={tiny?.error}
+ isDirty={tiny?.value !== undefined}
+ />
+ <p class="mt-2 text-sm text-gray-500">
+ <i18n.Translate>
+ Smallest difference between two amounts after the ratio is
+ applied.
+ </i18n.Translate>
+ </p>
+ </div>
</div>
</div>
- </div>
-
- <div class="px-6 pt-6">
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
- <div class="sm:col-span-5">
- <label
- class="block text-sm font-medium leading-6 text-gray-900"
- for="channel"
- >
- {i18n.str`Rounding mode`}
- </label>
- <div class="mt-2 max-w-xl text-sm text-gray-500">
- <div class="px-4 mt-4 grid grid-cols-1 gap-y-6">
- <label
- onClick={(e) => {
- e.preventDefault();
- rounding?.onUpdate("zero")
- }}
- data-selected={rounding?.value === "zero"}
- class="relative flex data-[disabled=false]:cursor-pointer rounded-lg border bg-white data-[disabled=true]:bg-gray-200 p-4 shadow-sm focus:outline-none border-gray-300 data-[selected=true]:ring-2 data-[selected=true]:ring-indigo-600"
- >
- <input
- type="radio"
- name="channel"
- value="Newsletter"
- class="sr-only"
- />
- <span class="flex flex-1">
- <span class="flex flex-col">
- <span
- id="project-type-0-label"
- class="block text-sm font-medium text-gray-900 "
- >
- <i18n.Translate>Zero</i18n.Translate>
+
+ <div class="px-6 pt-6">
+ <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+ <div class="sm:col-span-5">
+ <label
+ class="block text-sm font-medium leading-6 text-gray-900"
+ for={`${id}_channel`}
+ >
+ {i18n.str`Rounding mode`}
+ </label>
+ <div class="mt-2 max-w-xl text-sm text-gray-500">
+ <div class="px-4 mt-4 grid grid-cols-1 gap-y-6">
+ <label
+ onClick={(e) => {
+ e.preventDefault();
+ rounding?.onUpdate("zero");
+ }}
+ data-selected={rounding?.value === "zero"}
+ class="relative flex data-[disabled=false]:cursor-pointer rounded-lg border bg-white data-[disabled=true]:bg-gray-200 p-4 shadow-sm focus:outline-none border-gray-300 data-[selected=true]:ring-2 data-[selected=true]:ring-indigo-600"
+ >
+ <input
+ type="radio"
+ name="channel"
+ value="Newsletter"
+ class="sr-only"
+ />
+ <span class="flex flex-1">
+ <span class="flex flex-col">
+ <span class="block text-sm font-medium text-gray-900 ">
+ <i18n.Translate>Zero</i18n.Translate>
+ </span>
+ <i18n.Translate>
+ Amount will be round below to the largest possible value
+ smaller than the input.
+ </i18n.Translate>
</span>
- <i18n.Translate>Amount will be round below to the largest possible value smaller than the input.</i18n.Translate>
</span>
- </span>
+ <svg
+ data-selected={rounding?.value === "zero"}
+ class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden"
+ viewBox="0 0 20 20"
+ fill="currentColor"
+ aria-hidden="true"
+ >
+ <path
+ fill-rule="evenodd"
+ d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
+ clip-rule="evenodd"
+ />
+ </svg>
+ </label>
+
+ <label
+ onClick={(e) => {
+ e.preventDefault();
+ rounding?.onUpdate("up");
+ }}
+ data-selected={rounding?.value === "up"}
+ class="relative flex data-[disabled=false]:cursor-pointer rounded-lg border data-[disabled=true]:bg-gray-200 p-4 shadow-sm focus:outline-none border-gray-300 data-[selected=true]:ring-2 data-[selected=true]:ring-indigo-600"
+ >
+ <input
+ type="radio"
+ name="channel"
+ value="Existing Customers"
+ class="sr-only"
+ />
+ <span class="flex flex-1">
+ <span class="flex flex-col">
+ <span class="block text-sm font-medium text-gray-900 ">
+ <i18n.Translate>Up</i18n.Translate>
+ </span>
+ <i18n.Translate>
+ Amount will be round up to the smallest possible value
+ larger than the input.
+ </i18n.Translate>
+ </span>
+ </span>
+ <svg
+ data-selected={rounding?.value === "up"}
+ class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden"
+ viewBox="0 0 20 20"
+ fill="currentColor"
+ aria-hidden="true"
+ >
+ <path
+ fill-rule="evenodd"
+ d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
+ clip-rule="evenodd"
+ />
+ </svg>
+ </label>
+ <label
+ onClick={(e) => {
+ e.preventDefault();
+ rounding?.onUpdate("nearest");
+ }}
+ data-selected={rounding?.value === "nearest"}
+ class="relative flex data-[disabled=false]:cursor-pointer rounded-lg border data-[disabled=true]:bg-gray-200 p-4 shadow-sm focus:outline-none border-gray-300 data-[selected=true]:ring-2 data-[selected=true]:ring-indigo-600"
+ >
+ <input
+ type="radio"
+ name="channel"
+ value="Existing Customers"
+ class="sr-only"
+ />
+ <span class="flex flex-1">
+ <span class="flex flex-col">
+ <span class="block text-sm font-medium text-gray-900 ">
+ <i18n.Translate>Nearest</i18n.Translate>
+ </span>
+ <i18n.Translate>
+ Amount will be round to the closest possible value.
+ </i18n.Translate>
+ </span>
+ </span>
+ <svg
+ data-selected={rounding?.value === "nearest"}
+ class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden"
+ viewBox="0 0 20 20"
+ fill="currentColor"
+ aria-hidden="true"
+ >
+ <path
+ fill-rule="evenodd"
+ d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
+ clip-rule="evenodd"
+ />
+ </svg>
+ </label>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="px-6 pt-4">
+ <Attention title={i18n.str`Examples`}>
+ <section class="grid grid-cols-1 gap-y-3 text-gray-600">
+ <details class="group text-sm">
+ <summary class="flex cursor-pointer flex-row items-center justify-between ">
+ <i18n.Translate>
+ Rounding an amount of 1.24 with rounding value 0.1
+ </i18n.Translate>
<svg
- data-selected={rounding?.value === "zero"}
- class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden"
- viewBox="0 0 20 20"
- fill="currentColor"
+ class="h-6 w-6 rotate-0 transform group-open:rotate-180"
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ stroke-width="2"
+ stroke="currentColor"
aria-hidden="true"
>
<path
- fill-rule="evenodd"
- d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
- clip-rule="evenodd"
- />
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ d="M19 9l-7 7-7-7"
+ ></path>
</svg>
- </label>
-
- <label
- onClick={(e) => {
- e.preventDefault();
- rounding?.onUpdate("up")
- }}
- data-selected={rounding?.value === "up"}
- class="relative flex data-[disabled=false]:cursor-pointer rounded-lg border data-[disabled=true]:bg-gray-200 p-4 shadow-sm focus:outline-none border-gray-300 data-[selected=true]:ring-2 data-[selected=true]:ring-indigo-600"
- >
- <input
- type="radio"
- name="channel"
- value="Existing Customers"
- class="sr-only"
- />
- <span class="flex flex-1">
- <span class="flex flex-col">
- <span
- id="project-type-0-label"
- class="block text-sm font-medium text-gray-900 "
- >
- <i18n.Translate>Up</i18n.Translate>
- </span>
- <i18n.Translate>Amount will be round up to the smallest possible value larger than the input.</i18n.Translate>
- </span>
- </span>
+ </summary>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ Given the rounding value of 0.1 the possible values closest to
+ 1.24 are: 1.1, 1.2, 1.3, 1.4.
+ </i18n.Translate>
+ </p>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ With the "zero" mode the value will be rounded to 1.2
+ </i18n.Translate>
+ </p>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ With the "nearest" mode the value will be rounded to 1.2
+ </i18n.Translate>
+ </p>
+ <p class="text-gray-900 mt-4">
+ <i18n.Translate>
+ With the "up" mode the value will be rounded to 1.3
+ </i18n.Translate>
+ </p>
+ </details>
+ <details class="group ">
+ <summary class="flex cursor-pointer flex-row items-center justify-between ">
+ <i18n.Translate>
+ Rounding an amount of 1.26 with rounding value 0.1
+ </i18n.Translate>
<svg
- data-selected={rounding?.value === "up"}
- class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden"
- viewBox="0 0 20 20"
- fill="currentColor"
+ class="h-6 w-6 rotate-0 transform group-open:rotate-180"
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ stroke-width="2"
+ stroke="currentColor"
aria-hidden="true"
>
<path
- fill-rule="evenodd"
- d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
- clip-rule="evenodd"
- />
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ d="M19 9l-7 7-7-7"
+ ></path>
</svg>
- </label>
- <label
- onClick={(e) => {
- e.preventDefault();
- rounding?.onUpdate("nearest")
- }}
- data-selected={rounding?.value === "nearest"}
- class="relative flex data-[disabled=false]:cursor-pointer rounded-lg border data-[disabled=true]:bg-gray-200 p-4 shadow-sm focus:outline-none border-gray-300 data-[selected=true]:ring-2 data-[selected=true]:ring-indigo-600"
- >
- <input
- type="radio"
- name="channel"
- value="Existing Customers"
- class="sr-only"
- />
- <span class="flex flex-1">
- <span class="flex flex-col">
- <span
- id="project-type-0-label"
- class="block text-sm font-medium text-gray-900 "
- >
- <i18n.Translate>Nearest</i18n.Translate>
- </span>
- <i18n.Translate>Amount will be round to the closest possible value.</i18n.Translate>
- </span>
- </span>
+ </summary>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ Given the rounding value of 0.1 the possible values closest to
+ 1.24 are: 1.1, 1.2, 1.3, 1.4.
+ </i18n.Translate>
+ </p>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ With the "zero" mode the value will be rounded to 1.2
+ </i18n.Translate>
+ </p>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ With the "nearest" mode the value will be rounded to 1.3
+ </i18n.Translate>
+ </p>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ With the "up" mode the value will be rounded to 1.3
+ </i18n.Translate>
+ </p>
+ </details>
+ <details class="group ">
+ <summary class="flex cursor-pointer flex-row items-center justify-between ">
+ <i18n.Translate>
+ Rounding an amount of 1.24 with rounding value 0.3
+ </i18n.Translate>
<svg
- data-selected={rounding?.value === "nearest"}
- class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden"
- viewBox="0 0 20 20"
- fill="currentColor"
+ class="h-6 w-6 rotate-0 transform group-open:rotate-180"
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ stroke-width="2"
+ stroke="currentColor"
aria-hidden="true"
>
<path
- fill-rule="evenodd"
- d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
- clip-rule="evenodd"
- />
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ d="M19 9l-7 7-7-7"
+ ></path>
</svg>
- </label>
- </div>
- </div>
- </div>
+ </summary>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ Given the rounding value of 0.3 the possible values closest to
+ 1.24 are: 0.9, 1.2, 1.5, 1.8.
+ </i18n.Translate>
+ </p>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ With the "zero" mode the value will be rounded to 1.2
+ </i18n.Translate>
+ </p>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ With the "nearest" mode the value will be rounded to 1.2
+ </i18n.Translate>
+ </p>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ With the "up" mode the value will be rounded to 1.5
+ </i18n.Translate>
+ </p>
+ </details>
+ <details class="group ">
+ <summary class="flex cursor-pointer flex-row items-center justify-between ">
+ <i18n.Translate>
+ Rounding an amount of 1.26 with rounding value 0.3
+ </i18n.Translate>
+ <svg
+ class="h-6 w-6 rotate-0 transform group-open:rotate-180"
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ stroke-width="2"
+ stroke="currentColor"
+ aria-hidden="true"
+ >
+ <path
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ d="M19 9l-7 7-7-7"
+ ></path>
+ </svg>
+ </summary>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ Given the rounding value of 0.3 the possible values closest to
+ 1.24 are: 0.9, 1.2, 1.5, 1.8.
+ </i18n.Translate>
+ </p>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ With the "zero" mode the value will be rounded to 1.2
+ </i18n.Translate>
+ </p>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ With the "nearest" mode the value will be rounded to 1.3
+ </i18n.Translate>
+ </p>
+ <p class="text-gray-900 my-4">
+ <i18n.Translate>
+ With the "up" mode the value will be rounded to 1.3
+ </i18n.Translate>
+ </p>
+ </details>
+ </section>
+ </Attention>
</div>
- </div>
- <div class="px-6 pt-4">
- <Attention title={i18n.str`Examples`}>
- <section class="grid grid-cols-1 gap-y-3 text-gray-600">
- <details class="group text-sm">
- <summary class="flex cursor-pointer flex-row items-center justify-between ">
+ <div class="px-6 pt-6">
+ <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+ <div class="sm:col-span-5">
+ <label
+ for={`${id}_fee`}
+ class="block text-sm font-medium leading-6 text-gray-900"
+ >{i18n.str`Fee`}</label>
+ <InputAmount
+ name={`${id}_fee`}
+ left
+ currency={outputCurrency}
+ value={fee?.value ?? ""}
+ onChange={fee?.onUpdate}
+ />
+ <ShowInputErrorLabel
+ message={fee?.error}
+ isDirty={fee?.value !== undefined}
+ />
+ <p class="mt-2 text-sm text-gray-500">
<i18n.Translate>
- Rounding an amount of 1.24 with rounding value 0.1
- </i18n.Translate>
- <svg class="h-6 w-6 rotate-0 transform group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
- <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path>
- </svg>
- </summary>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- Given the rounding value of 0.1 the possible values closest to 1.24 are: 1.1, 1.2, 1.3, 1.4.
- </i18n.Translate>
- </p>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- With the "zero" mode the value will be rounded to 1.2
- </i18n.Translate>
- </p>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- With the "nearest" mode the value will be rounded to 1.2
- </i18n.Translate>
- </p>
- <p class="text-gray-900 mt-4">
- <i18n.Translate>
- With the "up" mode the value will be rounded to 1.3
- </i18n.Translate>
- </p>
- </details>
- <details class="group ">
- <summary class="flex cursor-pointer flex-row items-center justify-between ">
- <i18n.Translate>
- Rounding an amount of 1.26 with rounding value 0.1
- </i18n.Translate>
- <svg class="h-6 w-6 rotate-0 transform group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
- <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path>
- </svg>
- </summary>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- Given the rounding value of 0.1 the possible values closest to 1.24 are: 1.1, 1.2, 1.3, 1.4.
- </i18n.Translate>
- </p>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- With the "zero" mode the value will be rounded to 1.2
- </i18n.Translate>
- </p>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- With the "nearest" mode the value will be rounded to 1.3
- </i18n.Translate>
- </p>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- With the "up" mode the value will be rounded to 1.3
- </i18n.Translate>
- </p>
- </details>
- <details class="group ">
- <summary class="flex cursor-pointer flex-row items-center justify-between ">
- <i18n.Translate>
- Rounding an amount of 1.24 with rounding value 0.3
- </i18n.Translate>
- <svg class="h-6 w-6 rotate-0 transform group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
- <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path>
- </svg>
- </summary>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- Given the rounding value of 0.3 the possible values closest to 1.24 are: 0.9, 1.2, 1.5, 1.8.
- </i18n.Translate>
- </p>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- With the "zero" mode the value will be rounded to 1.2
- </i18n.Translate>
- </p>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- With the "nearest" mode the value will be rounded to 1.2
- </i18n.Translate>
- </p>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- With the "up" mode the value will be rounded to 1.5
- </i18n.Translate>
- </p>
- </details>
- <details class="group ">
- <summary class="flex cursor-pointer flex-row items-center justify-between ">
- <i18n.Translate>
- Rounding an amount of 1.26 with rounding value 0.3
- </i18n.Translate>
- <svg class="h-6 w-6 rotate-0 transform group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
- <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path>
- </svg>
- </summary>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- Given the rounding value of 0.3 the possible values closest to 1.24 are: 0.9, 1.2, 1.5, 1.8.
+ Amount to be deducted before amount is credited.
</i18n.Translate>
</p>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- With the "zero" mode the value will be rounded to 1.2
- </i18n.Translate>
- </p>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- With the "nearest" mode the value will be rounded to 1.3
- </i18n.Translate>
- </p>
- <p class="text-gray-900 my-4">
- <i18n.Translate>
- With the "up" mode the value will be rounded to 1.3
- </i18n.Translate>
- </p>
- </details>
- </section>
- </Attention>
- </div>
-
-
-
- <div class="px-6 pt-6">
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
- <div class="sm:col-span-5">
- <label
- for="cashin_fee"
- class="block text-sm font-medium leading-6 text-gray-900"
- >{i18n.str`Fee`}</label>
- <InputAmount
- name="cashin_fee"
- left
- currency={outputCurrency}
- value={fee?.value ?? ""}
- onChange={fee?.onUpdate}
- />
- <ShowInputErrorLabel
- message={fee?.error}
- isDirty={fee?.value !== undefined}
- />
- <p class="mt-2 text-sm text-gray-500">
- <i18n.Translate>Amount to be deducted before amount is credited.</i18n.Translate>
- </p>
+ </div>
</div>
</div>
- </div>
-
- </Fragment>
+ </Fragment>
+ );
}
diff --git a/packages/bank-ui/src/pages/regional/CreateCashout.tsx b/packages/bank-ui/src/pages/regional/CreateCashout.tsx
index 2f15d16b4..a76179b4d 100644
--- a/packages/bank-ui/src/pages/regional/CreateCashout.tsx
+++ b/packages/bank-ui/src/pages/regional/CreateCashout.tsx
@@ -39,9 +39,13 @@ import { useEffect, useState } from "preact/hooks";
import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js";
import { VersionHint, useBankCoreApiContext } from "../../context/config.js";
import { useAccountDetails } from "../../hooks/account.js";
-import { useSessionState } from "../../hooks/session.js";
import { useBankState } from "../../hooks/bank-state.js";
-import { TransferCalculation, useCashoutEstimator, useConversionInfo, useEstimator } from "../../hooks/regional.js";
+import {
+ TransferCalculation,
+ useCashoutEstimator,
+ useConversionInfo,
+} from "../../hooks/regional.js";
+import { useSessionState } from "../../hooks/session.js";
import { RouteDefinition } from "../../route.js";
import { TanChannel, undefinedIfEmpty } from "../../utils.js";
import { LoginForm } from "../LoginForm.js";
@@ -141,11 +145,11 @@ export function CreateCashout({
switch (info.case) {
case HttpStatusCode.NotImplemented: {
return (
- <Attention
- type="danger"
- title={i18n.str`Cashout are disabled`}
- >
- <i18n.Translate>Cashout should be enable by configuration and the conversion rate should be initialized with fee, ratio and rounding mode.</i18n.Translate>
+ <Attention type="danger" title={i18n.str`Cashout are disabled`}>
+ <i18n.Translate>
+ Cashout should be enable by configuration and the conversion rate
+ should be initialized with fee, ratio and rounding mode.
+ </i18n.Translate>
</Attention>
);
}
@@ -185,7 +189,8 @@ export function CreateCashout({
credit: fiatZero,
beforeFee: fiatZero,
};
- const [calculationResult, setCalculation] = useState<TransferCalculation>(zeroCalc);
+ const [calculationResult, setCalculation] =
+ useState<TransferCalculation>(zeroCalc);
const sellFee = Amounts.parseOrThrow(conversionInfo.cashout_fee);
const sellRate = conversionInfo.cashout_ratio;
/**
@@ -193,30 +198,33 @@ export function CreateCashout({
* depending on the isDebit flag
*/
const inputAmount = Amounts.parseOrThrow(
- `${form.isDebit ? regional_currency : fiat_currency}:${!form.amount ? "0" : form.amount
+ `${form.isDebit ? regional_currency : fiat_currency}:${
+ !form.amount ? "0" : form.amount
}`,
);
useEffect(() => {
async function doAsync() {
await handleError(async () => {
- const higerThanMin = form.isDebit ?
- Amounts.cmp(inputAmount, conversionInfo.cashout_min_amount) === 1 : true;
- const notZero = Amounts.isNonZero(inputAmount)
+ const higerThanMin = form.isDebit
+ ? Amounts.cmp(inputAmount, conversionInfo.cashout_min_amount) === 1
+ : true;
+ const notZero = Amounts.isNonZero(inputAmount);
if (notZero && higerThanMin) {
const resp = await (form.isDebit
? calculateFromDebit(inputAmount, sellFee)
: calculateFromCredit(inputAmount, sellFee));
setCalculation(resp);
} else {
- setCalculation(zeroCalc)
+ setCalculation(zeroCalc);
}
});
}
doAsync();
}, [form.amount, form.isDebit]);
- const calc = calculationResult === "amount-is-too-small" ? zeroCalc : calculationResult
+ const calc =
+ calculationResult === "amount-is-too-small" ? zeroCalc : calculationResult;
const balanceAfter = Amounts.sub(account.balance, calc.debit).amount;
@@ -231,8 +239,14 @@ export function CreateCashout({
? i18n.str`Invalid`
: Amounts.cmp(limit, calc.debit) === -1
? i18n.str`Balance is not enough`
- : form.isDebit && Amounts.cmp(inputAmount, conversionInfo.cashout_min_amount) < 1
- ? i18n.str`Needs to be higher than ${Amounts.stringifyValueWithSpec(Amounts.parseOrThrow(conversionInfo.cashout_min_amount), regional_currency_specification).normal}`
+ : form.isDebit &&
+ Amounts.cmp(inputAmount, conversionInfo.cashout_min_amount) < 1
+ ? i18n.str`Needs to be higher than ${
+ Amounts.stringifyValueWithSpec(
+ Amounts.parseOrThrow(conversionInfo.cashout_min_amount),
+ regional_currency_specification,
+ ).normal
+ }`
: calculationResult === "amount-is-too-small"
? i18n.str`Amount needs to be higher`
: Amounts.isZero(calc.credit)
@@ -280,6 +294,7 @@ export function CreateCashout({
title: i18n.str`Account not found`,
description: resp.detail.hint as TranslatedString,
debug: resp.detail,
+ when: AbsoluteTime.now(),
});
case TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED:
return notify({
@@ -287,6 +302,7 @@ export function CreateCashout({
title: i18n.str`Duplicated request detected, check if the operation succeeded or try again.`,
description: resp.detail.hint as TranslatedString,
debug: resp.detail,
+ when: AbsoluteTime.now(),
});
case TalerErrorCode.BANK_BAD_CONVERSION:
return notify({
@@ -294,6 +310,7 @@ export function CreateCashout({
title: i18n.str`The conversion rate was incorrectly applied`,
description: resp.detail.hint as TranslatedString,
debug: resp.detail,
+ when: AbsoluteTime.now(),
});
case TalerErrorCode.BANK_UNALLOWED_DEBIT:
return notify({
@@ -301,6 +318,7 @@ export function CreateCashout({
title: i18n.str`The account does not have sufficient funds`,
description: resp.detail.hint as TranslatedString,
debug: resp.detail,
+ when: AbsoluteTime.now(),
});
case HttpStatusCode.NotImplemented:
return notify({
@@ -308,6 +326,7 @@ export function CreateCashout({
title: i18n.str`Cashout are disabled`,
description: resp.detail.hint as TranslatedString,
debug: resp.detail,
+ when: AbsoluteTime.now(),
});
case TalerErrorCode.BANK_CONFIRM_INCOMPLETE:
return notify({
@@ -315,6 +334,7 @@ export function CreateCashout({
title: i18n.str`Missing cashout URI in the profile`,
description: resp.detail.hint as TranslatedString,
debug: resp.detail,
+ when: AbsoluteTime.now(),
});
case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED:
return notify({
@@ -322,6 +342,7 @@ export function CreateCashout({
title: i18n.str`Sending the confirmation message failed, retry later or contact the administrator.`,
description: resp.detail.hint as TranslatedString,
debug: resp.detail,
+ when: AbsoluteTime.now(),
});
}
assertUnreachable(resp);
@@ -406,7 +427,10 @@ export function CreateCashout({
<dd class="text-sm text-gray-900">{cashoutLegalName}</dd>
</div>
<p class="mt-2 text-sm text-gray-500">
- <i18n.Translate>If this name doesn't match the account holder's name your transaction may fail.</i18n.Translate>
+ <i18n.Translate>
+ If this name doesn't match the account holder's name your
+ transaction may fail.
+ </i18n.Translate>
</p>
</Fragment>
) : (
@@ -482,7 +506,7 @@ export function CreateCashout({
updateForm(structuredClone(form));
}}
>
- {form.isDebit ?
+ {form.isDebit ? (
<svg
class="self-center flex-none h-5 w-5 text-indigo-600"
viewBox="0 0 20 20"
@@ -495,12 +519,17 @@ export function CreateCashout({
clip-rule="evenodd"
/>
</svg>
-
- :
- <svg fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
+ ) : (
+ <svg
+ fill="none"
+ viewBox="0 0 24 24"
+ stroke-width="1.5"
+ stroke="currentColor"
+ class="w-5 h-5"
+ >
<path d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
- }
+ )}
<i18n.Translate>Send {regional_currency}</i18n.Translate>
</button>
@@ -514,7 +543,7 @@ export function CreateCashout({
updateForm(structuredClone(form));
}}
>
- {!form.isDebit ?
+ {!form.isDebit ? (
<svg
class="self-center flex-none h-5 w-5 text-indigo-600"
viewBox="0 0 20 20"
@@ -527,12 +556,17 @@ export function CreateCashout({
clip-rule="evenodd"
/>
</svg>
-
- :
- <svg fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
+ ) : (
+ <svg
+ fill="none"
+ viewBox="0 0 24 24"
+ stroke-width="1.5"
+ stroke="currentColor"
+ class="w-5 h-5"
+ >
<path d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
- }
+ )}
<i18n.Translate>Receive {fiat_currency}</i18n.Translate>
</button>
@@ -579,9 +613,9 @@ export function CreateCashout({
cashoutDisabled
? undefined
: (value) => {
- form.amount = value;
- updateForm(structuredClone(form));
- }
+ form.amount = value;
+ updateForm(structuredClone(form));
+ }
}
/>
<ShowInputErrorLabel
@@ -622,7 +656,7 @@ export function CreateCashout({
</dd>
</div>
{Amounts.isZero(sellFee) ||
- Amounts.isZero(calc.beforeFee) ? undefined : (
+ Amounts.isZero(calc.beforeFee) ? undefined : (
<div class="flex items-center justify-between border-t-2 afu pt-4">
<dt class="flex items-center text-sm text-gray-600">
<span>
@@ -655,7 +689,7 @@ export function CreateCashout({
{/* channel, not shown if new cashout api */}
{!OLD_CASHOUT_API ? undefined : config.supported_tan_channels
- .length === 0 ? (
+ .length === 0 ? (
<div class="sm:col-span-5">
<Attention
type="warning"
@@ -727,7 +761,7 @@ export function CreateCashout({
)}
{config.supported_tan_channels.indexOf(TanChannel.SMS) ===
- -1 ? undefined : (
+ -1 ? undefined : (
<label
onClick={() => {
if (!resultAccount.body.contact_data?.phone) return;
@@ -803,7 +837,7 @@ export function CreateCashout({
</button>
</div>
</form>
- </div >
- </div >
+ </div>
+ </div>
);
}
diff --git a/packages/bank-ui/src/pages/regional/ShowCashoutDetails.tsx b/packages/bank-ui/src/pages/regional/ShowCashoutDetails.tsx
index 415f88868..3f635db7e 100644
--- a/packages/bank-ui/src/pages/regional/ShowCashoutDetails.tsx
+++ b/packages/bank-ui/src/pages/regional/ShowCashoutDetails.tsx
@@ -16,7 +16,6 @@
import {
AbsoluteTime,
Amounts,
- Duration,
HttpStatusCode,
TalerError,
assertUnreachable,
@@ -26,20 +25,19 @@ import {
Loading,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
-import { format } from "date-fns";
import { VNode, h } from "preact";
import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js";
+import { Time } from "../../components/Time.js";
import { useCashoutDetails, useConversionInfo } from "../../hooks/regional.js";
import { RouteDefinition } from "../../route.js";
import { RenderAmount } from "../PaytoWireTransferForm.js";
-import { Time } from "../../components/Time.js";
interface Props {
id: string;
routeClose: RouteDefinition;
}
export function ShowCashoutDetails({ id, routeClose }: Props): VNode {
- const { i18n, dateLocale } = useTranslationContext();
+ const { i18n } = useTranslationContext();
const cid = Number.parseInt(id, 10);
const result = useCashoutDetails(Number.isNaN(cid) ? undefined : cid);
@@ -70,11 +68,11 @@ export function ShowCashoutDetails({ id, routeClose }: Props): VNode {
);
case HttpStatusCode.NotImplemented:
return (
- <Attention
- type="warning"
- title={i18n.str`Cashout are disabled`}
- >
- <i18n.Translate>Cashout should be enable by configuration and the conversion rate should be initialized with fee, ratio and rounding mode.</i18n.Translate>
+ <Attention type="warning" title={i18n.str`Cashout are disabled`}>
+ <i18n.Translate>
+ Cashout should be enable by configuration and the conversion rate
+ should be initialized with fee, ratio and rounding mode.
+ </i18n.Translate>
</Attention>
);
default:
@@ -92,10 +90,11 @@ export function ShowCashoutDetails({ id, routeClose }: Props): VNode {
switch (info.case) {
case HttpStatusCode.NotImplemented: {
return (
- <Attention type="danger"
- title={i18n.str`Cashout are disabled`}
- >
- <i18n.Translate>Cashout should be enable by configuration and the conversion rate should be initialized with fee, ratio and rounding mode.</i18n.Translate>
+ <Attention type="danger" title={i18n.str`Cashout are disabled`}>
+ <i18n.Translate>
+ Cashout should be enable by configuration and the conversion rate
+ should be initialized with fee, ratio and rounding mode.
+ </i18n.Translate>
</Attention>
);
}
@@ -134,9 +133,12 @@ export function ShowCashoutDetails({ id, routeClose }: Props): VNode {
<i18n.Translate>Created</i18n.Translate>
</dt>
<dd class="text-sm ">
- <Time format="dd/MM/yyyy HH:mm:ss"
- timestamp={AbsoluteTime.fromProtocolTimestamp(result.body.creation_time)}
- // relative={Duration.fromSpec({ days: 1 })}
+ <Time
+ format="dd/MM/yyyy HH:mm:ss"
+ timestamp={AbsoluteTime.fromProtocolTimestamp(
+ result.body.creation_time,
+ )}
+ // relative={Duration.fromSpec({ days: 1 })}
/>
</dd>
</div>