commit 6c6644051423e179d2d82ef18f6fbef1b7de6ee0
parent 78484b114dba78c4c553e2d18fc95c48bed4e408
Author: Sebastian <sebasjm@taler-systems.com>
Date: Wed, 3 Dec 2025 13:38:21 -0300
incoming wire transfer details
Diffstat:
5 files changed, 118 insertions(+), 20 deletions(-)
diff --git a/packages/merchant-backoffice-ui/src/components/modal/index.tsx b/packages/merchant-backoffice-ui/src/components/modal/index.tsx
@@ -616,10 +616,12 @@ export function Row({
name,
value,
literal,
+ lineBreak,
}: {
name: TranslatedString;
value: string | VNode;
literal?: boolean;
+ lineBreak?: boolean;
}): VNode {
const preRef = useRef<HTMLPreElement>(null);
const tdRef = useRef<HTMLTableCellElement>(null);
@@ -640,6 +642,7 @@ export function Row({
style={{
whiteSpace: "pre-wrap",
wordBreak: "break-word",
+ lineBreak: lineBreak ? "anywhere" : undefined,
padding: 4,
}}
>
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
@@ -153,7 +153,7 @@ export function CreatePage({ onConfirm, onBack, forceId }: Props): VNode {
};
const create = safeFunctionHandler(
async (
- token: AccessToken,
+ token: AccessToken | undefined,
data: TalerMerchantApi.InstanceConfigurationMessage,
challengeIds: string[],
) => {
@@ -178,7 +178,7 @@ export function CreatePage({ onConfirm, onBack, forceId }: Props): VNode {
}
return opEmptySuccess();
},
- !session.token || hasErrors ? undefined : [session.token, data, []],
+ hasErrors ? undefined : [session.token, data, []],
);
create.onSuccess = (success, oldtoken, data) => {
if (success) {
@@ -218,6 +218,9 @@ export function CreatePage({ onConfirm, onBack, forceId }: Props): VNode {
return (
<div>
<LocalNotificationBannerBulma notification={notification} />
+ <pre>
+ {JSON.stringify({errors}, undefined,2)}
+ </pre>
<section class="section is-main-section">
<div class="columns">
<div class="column" />
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx
@@ -33,17 +33,14 @@ export interface Props {
incomings: TalerMerchantApi.IncomingTransferDetails[];
onLoadMoreBefore?: () => void;
onLoadMoreAfter?: () => void;
- // onShowAll: () => void;
onShowVerified: () => void;
onShowUnverified: () => void;
isVerifiedTransfers?: boolean;
isNonVerifiedTransfers?: boolean;
- // isAllTransfers?: boolean;
accounts: string[];
onChangePayTo: (p?: string) => void;
payTo?: string;
- // onCreate: () => void;
- // onDelete: (wid: TalerMerchantApi.TransferDetails) => void;
+ onSelectedToConfirm: (wid: TalerMerchantApi.IncomingTransferDetails) => void;
}
export function ListPage({
@@ -51,15 +48,12 @@ export function ListPage({
onChangePayTo,
transfers,
incomings,
- // onCreate,
- // onDelete,
+ onSelectedToConfirm,
accounts,
onLoadMoreBefore,
onLoadMoreAfter,
- // isAllTransfers,
isNonVerifiedTransfers,
isVerifiedTransfers,
- // onShowAll,
onShowUnverified,
onShowVerified,
}: Props): VNode {
@@ -124,6 +118,7 @@ export function ListPage({
}))}
onLoadMoreBefore={onLoadMoreBefore}
onLoadMoreAfter={onLoadMoreAfter}
+ onSelectedToConfirm={onSelectedToConfirm}
/>
) : (
<CardTableVerified
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
@@ -35,12 +35,14 @@ interface TablePropsIncoming {
transfers: (TalerMerchantApi.IncomingTransferDetails & WithId)[];
onLoadMoreBefore?: () => void;
onLoadMoreAfter?: () => void;
+ onSelectedToConfirm: (d: TalerMerchantApi.IncomingTransferDetails) => void;
}
export function CardTableIncoming({
transfers,
onLoadMoreAfter,
onLoadMoreBefore,
+ onSelectedToConfirm,
}: TablePropsIncoming): VNode {
const { i18n } = useTranslationContext();
const [settings] = usePreference();
@@ -93,7 +95,15 @@ export function CardTableIncoming({
<tbody>
{transfers.map((i) => {
return (
- <tr key={i.id}>
+ <tr
+ key={i.id}
+ style={{
+ cursor: !i.confirmed ? "pointer" : undefined
+ }}
+ onClick={
+ !i.confirmed ? () => onSelectedToConfirm(i) : undefined
+ }
+ >
<td title={i.wtid}>{i.wtid.substring(0, 16)}...</td>
<td>{i.expected_credit_amount}</td>
<td>{i.confirmed ? i18n.str`yes` : i18n.str`no`}</td>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
@@ -24,10 +24,13 @@ import {
IncomingTransferDetails,
TalerError,
TransferDetails,
+ TransferInformation,
assertUnreachable,
} from "@gnu-taler/taler-util";
import {
- PaginatedResult
+ PaginatedResult,
+ useLocalNotificationBetter,
+ useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
@@ -36,11 +39,19 @@ import { Loading } from "../../../../components/exception/loading.js";
import { useInstanceBankAccounts } from "../../../../hooks/bank.js";
import {
useInstanceConfirmedTransfers,
- useInstanceIncomingTransfers
+ useInstanceIncomingTransfers,
} from "../../../../hooks/transfer.js";
import { LoginPage } from "../../../login/index.js";
import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
import { ListPage } from "./ListPage.js";
+import { ConfirmModal, Row } from "../../../../components/modal/index.js";
+import { Amount } from "../../../../components/Amount.js";
+import { format } from "date-fns";
+import {
+ datetimeFormatForSettings,
+ usePreference,
+} from "../../../../hooks/preference.js";
+import { useSessionContext } from "../../../../context/session.js";
interface Props {
// onCreate: () => void;
@@ -54,19 +65,21 @@ interface Form {
export default function ListTransfer({}: Props): VNode {
const setFilter = (s?: boolean) => setForm({ ...form, verified: s });
- // const { i18n } = useTranslationContext();
- // const { state: session, lib } = useSessionContext();
+ const { state: session, lib } = useSessionContext();
const [position, setPosition] = useState<string | undefined>(undefined);
+ const [settings] = usePreference();
- // const [notification, safeFunctionHandler] = useLocalNotificationBetter();
const instance = useInstanceBankAccounts();
const accounts =
!instance || instance instanceof TalerError || instance.type === "fail"
? []
: instance.body.accounts.map((a) => a.payto_uri);
- const [form, setForm] = useState<Form>({ payto_uri: "", verified: true });
+ const [form, setForm] = useState<Form>({ payto_uri: "", verified: false });
+ const [selected, setSelected] = useState<IncomingTransferDetails>();
+ const { i18n } = useTranslationContext();
+ const [notification, safeFunctionHandler] = useLocalNotificationBetter();
const shoulUseDefaultAccount = accounts.length === 1;
useEffect(() => {
@@ -75,6 +88,34 @@ export default function ListTransfer({}: Props): VNode {
}
}, [shoulUseDefaultAccount]);
+ const confirm = safeFunctionHandler(
+ lib.instance.informWireTransfer.bind(lib.instance),
+ !session.token || !selected
+ ? undefined
+ : [
+ session.token,
+ {
+ credit_amount: selected.expected_credit_amount!,
+ exchange_url: selected.exchange_url,
+ payto_uri: selected.payto_uri,
+ wtid: selected.wtid,
+ },
+ ],
+ );
+ confirm.onSuccess = () => {
+ setSelected(undefined)
+ };
+ confirm.onFail = (fail) => {
+ switch (fail.case) {
+ case HttpStatusCode.Unauthorized:
+ return i18n.str`Unauthorized.`;
+ case HttpStatusCode.NotFound:
+ return i18n.str`Not found.`;
+ case HttpStatusCode.Conflict:
+ return i18n.str`Wire transfer already confirmed.`;
+ }
+ };
+
// const isVerifiedTransfers = form.verified === true;
// const isNonVerifiedTransfers = form.verified === false;
// const isAllTransfers = form.verified === undefined;
@@ -107,7 +148,7 @@ export default function ListTransfer({}: Props): VNode {
}
}
}
- incoming = result
+ incoming = result;
}
let confirmed: PaginatedResult<TransferDetails[]>;
{
@@ -136,18 +177,64 @@ export default function ListTransfer({}: Props): VNode {
}
}
}
- confirmed = result
+ confirmed = result;
}
const show = form.verified ? confirmed : incoming;
return (
<Fragment>
{/* <LocalNotificationBannerBulma notification={notification} /> */}
-
+ {!selected ? undefined : (
+ <ConfirmModal
+ label={i18n.str`I have received the wire transfer`}
+ description={i18n.str`Confirm the wire transfer`}
+ active
+ onCancel={() => {
+ setSelected(undefined);
+ }}
+ confirm={confirm}
+ >
+ <p style={{ paddingTop: 0 }}>
+ <i18n.Translate>
+ The wire transfer has been sent and should be in your bank account
+ in any time. You can manually confirm the reception usign the
+ information below.
+ </i18n.Translate>
+ </p>
+ <div class="table-container">
+ <table>
+ <tbody>
+ <Row
+ name={i18n.str`Amount`}
+ value={<Amount value={selected.expected_credit_amount!} />}
+ />
+ {selected.execution_time &&
+ selected.execution_time.t_s !== "never" ? (
+ <Row
+ name={i18n.str`Time`}
+ value={format(
+ selected.execution_time.t_s * 1000,
+ datetimeFormatForSettings(settings),
+ )}
+ />
+ ) : undefined}
+ <Row
+ name={i18n.str`Transfer ID`}
+ value={selected.wtid}
+ literal
+ />
+ </tbody>
+ </table>
+ </div>
+ </ConfirmModal>
+ )}
<ListPage
accounts={accounts}
transfers={confirmed.body}
incomings={incoming.body}
+ onSelectedToConfirm={(d) => {
+ setSelected(d);
+ }}
onLoadMoreBefore={show.loadFirst}
onLoadMoreAfter={show.loadNext}
// onShowAll={() => setFilter(undefined)}