aboutsummaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-06-13 10:46:14 -0300
committerSebastian <sebasjm@gmail.com>2024-06-13 12:40:35 -0300
commit9f08f6e95c6b76ce12d7946901787f8a6884533b (patch)
treea7755cb7489eb54bc8a46edf81c4223134db5b9a /packages/merchant-backoffice-ui
parent9e7731e560ae360ed9f00cf2065c0a80c3eb8a85 (diff)
downloadwallet-core-9f08f6e95c6b76ce12d7946901787f8a6884533b.tar.xz
fix issues found in QC meeting
- templates payment timeout en merchant backoffice has a layout problem - Summary is editable with empty summary - better example for account info url - repeat password - delete transfers
Diffstat (limited to 'packages/merchant-backoffice-ui')
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx118
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx1
-rw-r--r--packages/merchant-backoffice-ui/src/components/menu/index.tsx6
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx64
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx16
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx3
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx4
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx14
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx95
9 files changed, 170 insertions, 151 deletions
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx
index ad3cb0e32..080b9508e 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx
@@ -52,14 +52,18 @@ export function InputDuration<T>({
const { error, required, value: anyValue, onChange } = useField<T>(name);
let strValue = "";
- const value: Duration = anyValue
+ const value: Duration = anyValue;
if (!value) {
strValue = "";
} else if (value.d_ms === "forever") {
strValue = i18n.str`forever`;
} else {
if (value.d_ms === undefined) {
- throw Error(`assertion error: duration should have a d_ms but got '${JSON.stringify(value)}'`)
+ throw Error(
+ `assertion error: duration should have a d_ms but got '${JSON.stringify(
+ value,
+ )}'`,
+ );
}
strValue = formatDuration(
intervalToDuration({ start: 0, end: value.d_ms }),
@@ -96,7 +100,7 @@ export function InputDuration<T>({
return (
<div class="field is-horizontal">
- <div class="field-label is-normal is-flex-grow-3">
+ <div class="field-label is-normal">
<label class="label">
{label}
{tooltip && (
@@ -107,69 +111,65 @@ export function InputDuration<T>({
</label>
</div>
- <div class="is-flex-grow-3">
- <div class="field-body ">
- <div class="field">
- <div class="field has-addons">
- <p class={expand ? "control is-expanded " : "control "}>
- <input
- class="input"
- type="text"
- readonly
- value={strValue}
- placeholder={placeholder}
- onClick={() => {
- if (!readonly) setOpened(true);
- }}
- />
- {required && (
- <span class="icon has-text-danger is-right">
- <i class="mdi mdi-alert" />
- </span>
- )}
- </p>
- <div
- class="control"
+ <div class="field-body is-flex-grow-3">
+ <div class="field">
+ <div class="field has-addons">
+ <p class={expand ? "control is-expanded " : "control "}>
+ <input
+ class="input"
+ type="text"
+ readonly
+ value={strValue}
+ placeholder={placeholder}
onClick={() => {
if (!readonly) setOpened(true);
}}
- >
- <a class="button is-static">
- <span class="icon">
- <i class="mdi mdi-clock" />
- </span>
- </a>
- </div>
+ />
+ {required && (
+ <span class="icon has-text-danger is-right">
+ <i class="mdi mdi-alert" />
+ </span>
+ )}
+ </p>
+ <div
+ class="control"
+ onClick={() => {
+ if (!readonly) setOpened(true);
+ }}
+ >
+ <a class="button is-static">
+ <span class="icon">
+ <i class="mdi mdi-clock" />
+ </span>
+ </a>
</div>
- {error && <p class="help is-danger">{error}</p>}
</div>
- {withForever && (
- <span data-tooltip={i18n.str`change value to never`}>
- <button
- class="button is-info mr-3"
- onClick={() => onChange({ d_ms: "forever" } as any)}
- >
- <i18n.Translate>forever</i18n.Translate>
- </button>
- </span>
- )}
- {!readonly && !withoutClear && (
- <span data-tooltip={i18n.str`change value to empty`}>
- <button
- class="button is-info "
- onClick={() => onChange(undefined as any)}
- >
- <i18n.Translate>clear</i18n.Translate>
- </button>
- </span>
- )}
- {side}
+ {error && <p class="help is-danger">{error}</p>}
+ <span class="has-text-grey">{help}</span>
</div>
- <span>
- {help}
- </span>
- </div>
+ {withForever && (
+ <span data-tooltip={i18n.str`change value to never`}>
+ <button
+ class="button is-info mr-3"
+ onClick={() => onChange({ d_ms: "forever" } as any)}
+ >
+ <i18n.Translate>forever</i18n.Translate>
+ </button>
+ </span>
+ )}
+ {!readonly && !withoutClear && (
+ <span data-tooltip={i18n.str`change value to empty`}>
+ <button
+ class="button is-info "
+ onClick={() => onChange(undefined as any)}
+ >
+ <i18n.Translate>clear</i18n.Translate>
+ </button>
+ </span>
+ )}
+ {side}
+ </div>
{opened && (
<SimpleModal onCancel={() => setOpened(false)}>
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
index b8cd4c2d2..04bcbc2be 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
@@ -69,6 +69,7 @@ export function InputWithAddon<T>({
)}
</label>
</div>
+
<div class="field-body is-flex-grow-3">
<div class="field">
<div class="field has-addons">
diff --git a/packages/merchant-backoffice-ui/src/components/menu/index.tsx b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
index 123271f8d..baab9584c 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/index.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
@@ -28,6 +28,12 @@ function getInstanceTitle(path: string, id: string): string {
switch (path) {
case InstancePaths.settings:
return `${id}: Settings`;
+ case InstancePaths.bank_new:
+ return `${id}: Account`;
+ case InstancePaths.bank_list:
+ return `${id}: Account`;
+ case InstancePaths.bank_update:
+ return `${id}: Account`;
case InstancePaths.order_list:
return `${id}: Orders`;
case InstancePaths.order_new:
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
index d0e7a83cd..1d08c5058 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
@@ -35,7 +35,7 @@ import { ImportingAccountModal } from "../../../../components/modal/index.js";
import { undefinedIfEmpty } from "../../../../utils/table.js";
import { safeConvertURL } from "../update/UpdatePage.js";
-type Entity = TalerMerchantApi.AccountAddDetails & { repeatPassword: string };
+type Entity = TalerMerchantApi.AccountAddDetails;
interface Props {
onCreate: (d: TalerMerchantApi.AccountAddDetails) => Promise<void>;
@@ -78,13 +78,6 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
: facadeURL.hash
? i18n.str`URL should not hash param`
: undefined,
- repeatPassword: !state.credit_facade_credentials
- ? undefined
- : state.credit_facade_credentials.type === "basic" &&
- (!state.credit_facade_credentials.password ||
- state.credit_facade_credentials.password !== state.repeatPassword)
- ? i18n.str`is not the same`
- : undefined,
};
const hasErrors = Object.keys(errors).some(
@@ -119,23 +112,35 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
};
return (
<div>
- {importing && <ImportingAccountModal onCancel={()=> {setImporting(false)}} onConfirm={(ac) => {
- state.payto_uri = ac.accountURI
- const u = new URL(ac.infoURL)
- u.password = ""
- if (u.username || u.password) {
- state.credit_facade_credentials = {
- type: "basic",
- password: u.password,
- username: u.username,
- }
- state.repeatPassword = u.password
- }
- u.password = ""
- u.username = ""
- state.credit_facade_url = u.href;
- setImporting(false)
- }} />}
+ {importing && (
+ <ImportingAccountModal
+ onCancel={() => {
+ setImporting(false);
+ }}
+ onConfirm={(ac) => {
+ state.payto_uri = ac.accountURI;
+ const u = new URL(ac.infoURL);
+ // if (u.username && ac.accesToken) {
+ // state.credit_facade_credentials = {
+ // type: "bearer",
+ // token: ac.accesToken,
+ // username: u.username,
+ // };
+ // } else
+ if (u.username || u.password) {
+ state.credit_facade_credentials = {
+ type: "basic",
+ password: u.password,
+ username: u.username,
+ };
+ }
+ u.password = "";
+ u.username = "";
+ state.credit_facade_url = u.href;
+ setImporting(false);
+ }}
+ />
+ )}
<section class="section is-main-section">
<div class="columns">
<div class="column" />
@@ -152,7 +157,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
<Input<Entity>
name="credit_facade_url"
label={i18n.str`Account info URL`}
- help="https://bank.com"
+ help="https://bank.demo.taler.net/accounts/_username_/taler-revenue/"
expand
tooltip={i18n.str`From where the merchant can download information about incoming wire transfers to this account`}
/>
@@ -179,11 +184,6 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
label={i18n.str`Password`}
tooltip={i18n.str`Password to access the account information.`}
/>
- <Input
- name="repeatPassword"
- inputType="password"
- label={i18n.str`Repeat password`}
- />
</Fragment>
) : undefined}
</FormProvider>
@@ -193,7 +193,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
class="button is-info"
data-tooltip={i18n.str`Need to complete marked fields`}
onClick={() => {
- setImporting(true)
+ setImporting(true);
}}
>
<i18n.Translate>Import from bank</i18n.Translate>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
index 1a8e9bdc1..c4ba1f0f2 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
@@ -83,15 +83,6 @@ export function UpdatePage({ account, onUpdate, onBack }: Props): VNode {
? i18n.str`required`
: undefined,
- repeatPassword:
- state.credit_facade_credentials?.type !== "basic"
- ? undefined
- : !(state.credit_facade_credentials as any).repeatPassword
- ? i18n.str`required`
- : (state.credit_facade_credentials as any).repeatPassword !==
- state.credit_facade_credentials.password
- ? i18n.str`doesn't match`
- : undefined,
}),
};
@@ -158,7 +149,7 @@ export function UpdatePage({ account, onUpdate, onBack }: Props): VNode {
<Input<Entity>
name="credit_facade_url"
label={i18n.str`Account info URL`}
- help="https://bank.com"
+ help="https://bank.demo.taler.net/accounts/_username_/taler-revenue/"
expand
tooltip={i18n.str`From where the merchant can download information about incoming wire transfers to this account`}
/>
@@ -186,11 +177,6 @@ export function UpdatePage({ account, onUpdate, onBack }: Props): VNode {
label={i18n.str`Password`}
tooltip={i18n.str`Password to access the account information.`}
/>
- <Input
- name="credit_facade_credentials.repeatPassword"
- inputType="password"
- label={i18n.str`Repeat password`}
- />
</Fragment>
) : undefined}
</FormProvider>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
index fce14dcc3..4fe11bf5c 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
@@ -54,11 +54,10 @@ export default function ListTemplates({
}: Props): VNode {
const { i18n } = useTranslationContext();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { lib } = useSessionContext();
+ const { state, lib } = useSessionContext();
const result = useInstanceTemplates();
const [deleting, setDeleting] =
useState<TalerMerchantApi.TemplateEntry | null>(null);
- const { state } = useSessionContext();
if (!result) return <Loading />
if (result instanceof TalerError) {
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
index 22ad0b8d8..6738b1c6c 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx
@@ -19,12 +19,12 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
+import { TalerMerchantApi } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
import { FormProvider } from "../../../../components/form/FormProvider.js";
import { InputSelector } from "../../../../components/form/InputSelector.js";
import { CardTable } from "./Table.js";
-import { TalerMerchantApi } from "@gnu-taler/taler-util";
export interface Props {
transfers: TalerMerchantApi.TransferDetails[];
@@ -40,7 +40,7 @@ export interface Props {
onChangePayTo: (p?: string) => void;
payTo?: string;
onCreate: () => void;
- onDelete: () => void;
+ onDelete: (wid: TalerMerchantApi.TransferDetails) => void;
}
export function ListPage({
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
index b9235c669..a6c9a00f9 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
@@ -129,12 +129,6 @@ function Table({
<i18n.Translate>Credit</i18n.Translate>
</th>
<th>
- <i18n.Translate>Address</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Exchange URL</i18n.Translate>
- </th>
- <th>
<i18n.Translate>Confirmed</i18n.Translate>
</th>
<th>
@@ -150,10 +144,8 @@ function Table({
{instances.map((i) => {
return (
<tr key={i.id}>
- <td>{i.id}</td>
+ <td title={i.wtid}>{i.wtid.substring(0,16)}...</td>
<td>{i.credit_amount}</td>
- <td>{i.payto_uri}</td>
- <td>{i.exchange_url}</td>
<td>{i.confirmed ? i18n.str`yes` : i18n.str`no`}</td>
<td>{i.verified ? i18n.str`yes` : i18n.str`no`}</td>
<td>
@@ -167,13 +159,13 @@ function Table({
: i18n.str`unknown`}
</td>
<td>
- {i.verified === undefined ? (
+ {i.verified !== true ? (
<button
class="button is-danger is-small has-tooltip-left"
data-tooltip={i18n.str`delete selected transfer from the database`}
onClick={() => onDelete(i)}
>
- Delete
+ <i18n.Translate>Delete</i18n.Translate>
</button>
) : undefined}
</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
index 8b4d1f3cb..267d41711 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
@@ -19,8 +19,12 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { HttpStatusCode, TalerError, assertUnreachable } from "@gnu-taler/taler-util";
-import { VNode, h } from "preact";
+import {
+ HttpStatusCode,
+ TalerError,
+ assertUnreachable,
+} from "@gnu-taler/taler-util";
+import { Fragment, VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import { ErrorLoadingMerchant } from "../../../../components/ErrorLoadingMerchant.js";
import { Loading } from "../../../../components/exception/loading.js";
@@ -29,6 +33,10 @@ import { useInstanceTransfers } from "../../../../hooks/transfer.js";
import { LoginPage } from "../../../login/index.js";
import { ListPage } from "./ListPage.js";
import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
+import { useSessionContext } from "../../../../context/session.js";
+import { NotificationCard } from "../../../../components/menu/index.js";
+import { Notification } from "../../../../utils/types.js";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
interface Props {
onCreate: () => void;
@@ -38,25 +46,28 @@ interface Form {
payto_uri?: string;
}
-export default function ListTransfer({
- onCreate,
-}: Props): VNode {
+export default function ListTransfer({ onCreate }: Props): VNode {
const setFilter = (s?: boolean) => setForm({ ...form, verified: s });
+ const { i18n } = useTranslationContext();
+
+ const { state, lib } = useSessionContext();
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
const [position, setPosition] = useState<string | undefined>(undefined);
const instance = useInstanceBankAccounts();
- const accounts = !instance || (instance instanceof TalerError) || instance.type === "fail"
- ? []
- : instance.body.accounts.map((a) => a.payto_uri);
+ const accounts =
+ !instance || instance instanceof TalerError || instance.type === "fail"
+ ? []
+ : instance.body.accounts.map((a) => a.payto_uri);
const [form, setForm] = useState<Form>({ payto_uri: "" });
- const shoulUseDefaultAccount = accounts.length === 1
+ const shoulUseDefaultAccount = accounts.length === 1;
useEffect(() => {
if (shoulUseDefaultAccount) {
- setForm({...form, payto_uri: accounts[0]})
+ setForm({ ...form, payto_uri: accounts[0] });
}
- }, [shoulUseDefaultAccount])
+ }, [shoulUseDefaultAccount]);
const isVerifiedTransfers = form.verified === true;
const isNonVerifiedTransfers = form.verified === false;
@@ -78,7 +89,7 @@ export default function ListTransfer({
if (result.type === "fail") {
switch (result.case) {
case HttpStatusCode.Unauthorized: {
- return <LoginPage />
+ return <LoginPage />;
}
case HttpStatusCode.NotFound: {
return <NotFoundPageOrAdminCreate />;
@@ -90,23 +101,47 @@ export default function ListTransfer({
}
return (
- <ListPage
- accounts={accounts}
- transfers={result.body}
- onLoadMoreBefore={result.isFirstPage ? undefined: result.loadFirst }
- onLoadMoreAfter={result.isLastPage ? undefined : result.loadNext}
- onCreate={onCreate}
- onDelete={() => {
- null;
- }}
- onShowAll={() => setFilter(undefined)}
- onShowUnverified={() => setFilter(false)}
- onShowVerified={() => setFilter(true)}
- isAllTransfers={isAllTransfers}
- isVerifiedTransfers={isVerifiedTransfers}
- isNonVerifiedTransfers={isNonVerifiedTransfers}
- payTo={form.payto_uri}
- onChangePayTo={(p) => setForm((v) => ({ ...v, payto_uri: p }))}
- />
+ <Fragment>
+ <NotificationCard notification={notif} />
+
+ <ListPage
+ accounts={accounts}
+ transfers={result.body}
+ onLoadMoreBefore={result.isFirstPage ? undefined : result.loadFirst}
+ onLoadMoreAfter={result.isLastPage ? undefined : result.loadNext}
+ onCreate={onCreate}
+ onDelete={async (transfer) => {
+ try {
+ const resp = await lib.instance.deleteWireTransfer(state.token, transfer.wtid);
+ if (resp.type === "ok") {
+ setNotif({
+ message: i18n.str`Wire transfer "${transfer.wtid.substring(0,16)}..." has been deleted`,
+ type: "SUCCESS",
+ });
+ } else {
+ setNotif({
+ message: i18n.str`Failed to delete transfer`,
+ type: "ERROR",
+ description: resp.detail.hint,
+ });
+ }
+ } catch (error) {
+ setNotif({
+ message: i18n.str`Failed to delete transfer`,
+ type: "ERROR",
+ description: error instanceof Error ? error.message : undefined,
+ });
+ }
+ }}
+ onShowAll={() => setFilter(undefined)}
+ onShowUnverified={() => setFilter(false)}
+ onShowVerified={() => setFilter(true)}
+ isAllTransfers={isAllTransfers}
+ isVerifiedTransfers={isVerifiedTransfers}
+ isNonVerifiedTransfers={isNonVerifiedTransfers}
+ payTo={form.payto_uri}
+ onChangePayTo={(p) => setForm((v) => ({ ...v, payto_uri: p }))}
+ />
+ </Fragment>
);
}