diff options
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths')
57 files changed, 2052 insertions, 1358 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/Create.stories.tsx index cb62b0224..052d61544 100644 --- a/packages/merchant-backoffice-ui/src/paths/admin/create/Create.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/admin/create/Create.stories.tsx @@ -15,31 +15,32 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode, FunctionalComponent } from 'preact'; +import { h, VNode, FunctionalComponent } from "preact"; import { CreatePage as TestedComponent } from "./CreatePage.js"; - export default { - title: 'Pages/Instance/Create', + title: "Pages/Instance/Create", component: TestedComponent, argTypes: { - onCreate: { action: 'onCreate' }, - goBack: { action: 'goBack' }, - } + onCreate: { action: "onCreate" }, + goBack: { action: "goBack" }, + }, }; -function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { - const r = (args: any) => <Component {...args} /> - r.args = props - return r +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props>, +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; } -export const Example = createExample(TestedComponent, { -}); +export const Example = createExample(TestedComponent, {}); // export const Example = (a: any): VNode => <CreatePage {...a} />; // Example.args = { // isLoading: false diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx index 089d4ea80..6fcabb18b 100644 --- a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx @@ -76,7 +76,7 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode { : undefinedIfEmpty( value.payto_uris.map((p) => { return !PAYTO_REGEX.test(p) ? i18n`is not valid` : undefined; - }) + }), ), default_max_deposit_fee: !value.default_max_deposit_fee ? i18n`required` @@ -115,7 +115,7 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode { }; const hasErrors = Object.keys(errors).some( - (k) => (errors as any)[k] !== undefined + (k) => (errors as any)[k] !== undefined, ); const submit = (): Promise<void> => { diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/InstanceCreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/InstanceCreatedSuccessfully.tsx index fde2410e9..c620c6482 100644 --- a/packages/merchant-backoffice-ui/src/paths/admin/create/InstanceCreatedSuccessfully.tsx +++ b/packages/merchant-backoffice-ui/src/paths/admin/create/InstanceCreatedSuccessfully.tsx @@ -14,52 +14,61 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { CreatedSuccessfully } from "../../../components/notifications/CreatedSuccessfully.js"; import { Entity } from "./index.js"; -export function InstanceCreatedSuccessfully({ entity, onConfirm }: { entity: Entity; onConfirm: () => void; }): VNode { - return <CreatedSuccessfully onConfirm={onConfirm}> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label">ID</label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input class="input" readonly value={entity.id} /> - </p> +export function InstanceCreatedSuccessfully({ + entity, + onConfirm, +}: { + entity: Entity; + onConfirm: () => void; +}): VNode { + return ( + <CreatedSuccessfully onConfirm={onConfirm}> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">ID</label> </div> - </div> - </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label">Business Name</label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input class="input" readonly value={entity.name} /> - </p> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={entity.id} /> + </p> + </div> </div> </div> - </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label">Access token</label> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Business Name</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={entity.name} /> + </p> + </div> + </div> </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - {entity.auth.method === 'external' && 'external'} - {entity.auth.method === 'token' && - <input class="input" readonly value={entity.auth.token} />} - </p> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Access token</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + {entity.auth.method === "external" && "external"} + {entity.auth.method === "token" && ( + <input class="input" readonly value={entity.auth.token} /> + )} + </p> + </div> </div> </div> - </div> - </CreatedSuccessfully>; + </CreatedSuccessfully> + ); } diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx index 8e83b75ff..ed2f3f068 100644 --- a/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx @@ -54,7 +54,7 @@ export default function Create({ onBack, onConfirm, forceId }: Props): VNode { onBack={onBack} forceId={forceId} onCreate={( - d: MerchantBackend.Instances.InstanceConfigurationMessage + d: MerchantBackend.Instances.InstanceConfigurationMessage, ) => { return createInstance(d) .then(() => { diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx b/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx index 8f2829112..546f34f3a 100644 --- a/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx +++ b/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx @@ -15,9 +15,9 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { StateUpdater, useEffect, useState } from "preact/hooks"; @@ -34,57 +34,96 @@ interface Props { setInstanceName: (s: string) => void; } -export function CardTable({ instances, onCreate, onUpdate, onPurge, setInstanceName, onDelete, selected }: Props): VNode { +export function CardTable({ + instances, + onCreate, + onUpdate, + onPurge, + setInstanceName, + onDelete, + selected, +}: Props): VNode { const [actionQueue, actionQueueHandler] = useState<Actions[]>([]); - const [rowSelection, rowSelectionHandler] = useState<string[]>([]) + const [rowSelection, rowSelectionHandler] = useState<string[]>([]); useEffect(() => { - if (actionQueue.length > 0 && !selected && actionQueue[0].type == 'DELETE') { - onDelete(actionQueue[0].element) - actionQueueHandler(actionQueue.slice(1)) + if ( + actionQueue.length > 0 && + !selected && + actionQueue[0].type == "DELETE" + ) { + onDelete(actionQueue[0].element); + actionQueueHandler(actionQueue.slice(1)); } - }, [actionQueue, selected, onDelete]) + }, [actionQueue, selected, onDelete]); useEffect(() => { - if (actionQueue.length > 0 && !selected && actionQueue[0].type == 'UPDATE') { - onUpdate(actionQueue[0].element.id) - actionQueueHandler(actionQueue.slice(1)) + if ( + actionQueue.length > 0 && + !selected && + actionQueue[0].type == "UPDATE" + ) { + onUpdate(actionQueue[0].element.id); + actionQueueHandler(actionQueue.slice(1)); } - }, [actionQueue, selected, onUpdate]) - - const i18n = useTranslator() + }, [actionQueue, selected, onUpdate]); - return <div class="card has-table"> - <header class="card-header"> - <p class="card-header-title"><span class="icon"><i class="mdi mdi-desktop-mac" /></span><Translate>Instances</Translate></p> + const i18n = useTranslator(); - <div class="card-header-icon" aria-label="more options"> + return ( + <div class="card has-table"> + <header class="card-header"> + <p class="card-header-title"> + <span class="icon"> + <i class="mdi mdi-desktop-mac" /> + </span> + <Translate>Instances</Translate> + </p> - <button class={rowSelection.length > 0 ? "button is-danger" : "is-hidden"} - type="button" onClick={(): void => actionQueueHandler(buildActions(instances, rowSelection, 'DELETE'))} > - <Translate>Delete</Translate> - </button> - </div> - <div class="card-header-icon" aria-label="more options"> - <span class="has-tooltip-left" data-tooltip={i18n`add new instance`}> - <button class="button is-info" type="button" onClick={onCreate}> - <span class="icon is-small" ><i class="mdi mdi-plus mdi-36px" /></span> + <div class="card-header-icon" aria-label="more options"> + <button + class={rowSelection.length > 0 ? "button is-danger" : "is-hidden"} + type="button" + onClick={(): void => + actionQueueHandler( + buildActions(instances, rowSelection, "DELETE"), + ) + } + > + <Translate>Delete</Translate> </button> - </span> - </div> - - </header> - <div class="card-content"> - <div class="b-table has-pagination"> - <div class="table-wrapper has-mobile-cards"> - {instances.length > 0 ? - <Table instances={instances} onPurge={onPurge} onUpdate={onUpdate} setInstanceName={setInstanceName} onDelete={onDelete} rowSelection={rowSelection} rowSelectionHandler={rowSelectionHandler} /> : - <EmptyTable /> - } + </div> + <div class="card-header-icon" aria-label="more options"> + <span class="has-tooltip-left" data-tooltip={i18n`add new instance`}> + <button class="button is-info" type="button" onClick={onCreate}> + <span class="icon is-small"> + <i class="mdi mdi-plus mdi-36px" /> + </span> + </button> + </span> + </div> + </header> + <div class="card-content"> + <div class="b-table has-pagination"> + <div class="table-wrapper has-mobile-cards"> + {instances.length > 0 ? ( + <Table + instances={instances} + onPurge={onPurge} + onUpdate={onUpdate} + setInstanceName={setInstanceName} + onDelete={onDelete} + rowSelection={rowSelection} + rowSelectionHandler={rowSelectionHandler} + /> + ) : ( + <EmptyTable /> + )} + </div> </div> </div> </div> - </div> + ); } interface TableProps { rowSelection: string[]; @@ -93,14 +132,23 @@ interface TableProps { onDelete: (id: MerchantBackend.Instances.Instance) => void; onPurge: (id: MerchantBackend.Instances.Instance) => void; rowSelectionHandler: StateUpdater<string[]>; - setInstanceName: (s:string) => void; + setInstanceName: (s: string) => void; } function toggleSelected<T>(id: T): (prev: T[]) => T[] { - return (prev: T[]): T[] => prev.indexOf(id) == -1 ? [...prev, id] : prev.filter(e => e != id) + return (prev: T[]): T[] => + prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id); } -function Table({ rowSelection, rowSelectionHandler, setInstanceName, instances, onUpdate, onDelete, onPurge }: TableProps): VNode { +function Table({ + rowSelection, + rowSelectionHandler, + setInstanceName, + instances, + onUpdate, + onDelete, + onPurge, +}: TableProps): VNode { return ( <div class="table-container"> <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> @@ -108,75 +156,127 @@ function Table({ rowSelection, rowSelectionHandler, setInstanceName, instances, <tr> <th class="is-checkbox-cell"> <label class="b-checkbox checkbox"> - <input type="checkbox" checked={rowSelection.length === instances.length} onClick={(): void => rowSelectionHandler(rowSelection.length === instances.length ? [] : instances.map(i => i.id))} /> + <input + type="checkbox" + checked={rowSelection.length === instances.length} + onClick={(): void => + rowSelectionHandler( + rowSelection.length === instances.length + ? [] + : instances.map((i) => i.id), + ) + } + /> <span class="check" /> </label> </th> - <th><Translate>ID</Translate></th> - <th><Translate>Name</Translate></th> + <th> + <Translate>ID</Translate> + </th> + <th> + <Translate>Name</Translate> + </th> <th /> </tr> </thead> <tbody> - {instances.map(i => { - return <tr key={i.id}> - <td class="is-checkbox-cell"> - <label class="b-checkbox checkbox"> - <input type="checkbox" checked={rowSelection.indexOf(i.id) != -1} onClick={(): void => rowSelectionHandler(toggleSelected(i.id))} /> - <span class="check" /> - </label> - </td> - <td><a href={`#/orders?instance=${i.id}`} onClick={(e) => { - setInstanceName(i.id); - }}>{i.id}</a></td> - <td >{i.name}</td> - <td class="is-actions-cell right-sticky"> - <div class="buttons is-right"> - <button class="button is-small is-success jb-modal" type="button" onClick={(): void => onUpdate(i.id)}> - <Translate>Edit</Translate> - </button> - {!i.deleted && - <button class="button is-small is-danger jb-modal is-outlined" type="button" onClick={(): void => onDelete(i)}> - <Translate>Delete</Translate> - </button> - } - {i.deleted && - <button class="button is-small is-danger jb-modal" type="button" onClick={(): void => onPurge(i)}> - <Translate>Purge</Translate> - </button> - } - </div> - </td> - </tr> + {instances.map((i) => { + return ( + <tr key={i.id}> + <td class="is-checkbox-cell"> + <label class="b-checkbox checkbox"> + <input + type="checkbox" + checked={rowSelection.indexOf(i.id) != -1} + onClick={(): void => + rowSelectionHandler(toggleSelected(i.id)) + } + /> + <span class="check" /> + </label> + </td> + <td> + <a + href={`#/orders?instance=${i.id}`} + onClick={(e) => { + setInstanceName(i.id); + }} + > + {i.id} + </a> + </td> + <td>{i.name}</td> + <td class="is-actions-cell right-sticky"> + <div class="buttons is-right"> + <button + class="button is-small is-success jb-modal" + type="button" + onClick={(): void => onUpdate(i.id)} + > + <Translate>Edit</Translate> + </button> + {!i.deleted && ( + <button + class="button is-small is-danger jb-modal is-outlined" + type="button" + onClick={(): void => onDelete(i)} + > + <Translate>Delete</Translate> + </button> + )} + {i.deleted && ( + <button + class="button is-small is-danger jb-modal" + type="button" + onClick={(): void => onPurge(i)} + > + <Translate>Purge</Translate> + </button> + )} + </div> + </td> + </tr> + ); })} - </tbody> </table> </div> - ) + ); } function EmptyTable(): VNode { - return <div class="content has-text-grey has-text-centered"> - <p> - <span class="icon is-large"><i class="mdi mdi-emoticon-sad mdi-48px" /></span> - </p> - <p><Translate>There is no instances yet, add more pressing the + sign</Translate></p> - </div> + return ( + <div class="content has-text-grey has-text-centered"> + <p> + <span class="icon is-large"> + <i class="mdi mdi-emoticon-sad mdi-48px" /> + </span> + </p> + <p> + <Translate> + There is no instances yet, add more pressing the + sign + </Translate> + </p> + </div> + ); } - interface Actions { element: MerchantBackend.Instances.Instance; - type: 'DELETE' | 'UPDATE'; + type: "DELETE" | "UPDATE"; } function notEmpty<TValue>(value: TValue | null | undefined): value is TValue { return value !== null && value !== undefined; } -function buildActions(instances: MerchantBackend.Instances.Instance[], selected: string[], action: 'DELETE'): Actions[] { - return selected.map(id => instances.find(i => i.id === id)) +function buildActions( + instances: MerchantBackend.Instances.Instance[], + selected: string[], + action: "DELETE", +): Actions[] { + return selected + .map((id) => instances.find((i) => i.id === id)) .filter(notEmpty) - .map(id => ({ element: id, type: action })) + .map((id) => ({ element: id, type: action })); } diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/View.stories.tsx b/packages/merchant-backoffice-ui/src/paths/admin/list/View.stories.tsx index 87d68232a..e0f5d5430 100644 --- a/packages/merchant-backoffice-ui/src/paths/admin/list/View.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/admin/list/View.stories.tsx @@ -15,68 +15,76 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h } from 'preact'; +import { h } from "preact"; import { View } from "./View.js"; - export default { - title: 'Pages/Instance/List', + title: "Pages/Instance/List", component: View, argTypes: { - onSelect: { action: 'onSelect' }, + onSelect: { action: "onSelect" }, }, }; export const Empty = (a: any) => <View {...a} />; Empty.args = { - instances: [] -} + instances: [], +}; export const WithDefaultInstance = (a: any) => <View {...a} />; WithDefaultInstance.args = { - instances: [{ - id: 'default', - name: 'the default instance', - merchant_pub: 'abcdef', - payment_targets: [] - }] -} + instances: [ + { + id: "default", + name: "the default instance", + merchant_pub: "abcdef", + payment_targets: [], + }, + ], +}; export const WithFiveInstance = (a: any) => <View {...a} />; WithFiveInstance.args = { - instances: [{ - id: 'first', - name: 'the first instance', - merchant_pub: 'abcdefgh', - payment_targets: ['asd'] - }, { - id: 'second', - name: 'the second instance', - merchant_pub: 'zxczxcz', - payment_targets: ['asd'] - }, { - id: 'third', - name: 'the third instance', - merchant_pub: 'QWEQWEWQE', - payment_targets: ['asd'] - }, { - id: 'other', - name: 'the other instance', - merchant_pub: 'FHJHGJGHJ', - payment_targets: ['asd'] - }, { - id: 'another', - name: 'the another instance', - merchant_pub: 'abcd3423423efgh', - payment_targets: ['asd'] - }, { - id: 'last', - name: 'last instance', - merchant_pub: 'zxcvvbnm', - payment_targets: ['pay-to', 'asd'] - }] -} + instances: [ + { + id: "first", + name: "the first instance", + merchant_pub: "abcdefgh", + payment_targets: ["asd"], + }, + { + id: "second", + name: "the second instance", + merchant_pub: "zxczxcz", + payment_targets: ["asd"], + }, + { + id: "third", + name: "the third instance", + merchant_pub: "QWEQWEWQE", + payment_targets: ["asd"], + }, + { + id: "other", + name: "the other instance", + merchant_pub: "FHJHGJGHJ", + payment_targets: ["asd"], + }, + { + id: "another", + name: "the another instance", + merchant_pub: "abcd3423423efgh", + payment_targets: ["asd"], + }, + { + id: "last", + name: "last instance", + merchant_pub: "zxcvvbnm", + payment_targets: ["pay-to", "asd"], + }, + ], +}; diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx index 2af9dea71..5180c671c 100644 --- a/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx +++ b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx @@ -15,14 +15,14 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { MerchantBackend } from "../../../declaration.js"; import { CardTable as CardTableActive } from "./TableActive.js"; -import { useState } from 'preact/hooks'; +import { useState } from "preact/hooks"; import { Translate, useTranslator } from "../../../i18n/index.js"; interface Props { @@ -35,46 +35,78 @@ interface Props { setInstanceName: (s: string) => void; } -export function View({ instances, onCreate, onDelete, onPurge, onUpdate, setInstanceName, selected }: Props): VNode { +export function View({ + instances, + onCreate, + onDelete, + onPurge, + onUpdate, + setInstanceName, + selected, +}: Props): VNode { const [show, setShow] = useState<"active" | "deleted" | null>("active"); - const showIsActive = show === 'active' ? "is-active" : '' - const showIsDeleted = show === 'deleted' ? "is-active" : '' - const showAll = show === null ? "is-active" : '' - const i18n = useTranslator() + const showIsActive = show === "active" ? "is-active" : ""; + const showIsDeleted = show === "deleted" ? "is-active" : ""; + const showAll = show === null ? "is-active" : ""; + const i18n = useTranslator(); - const showingInstances = showIsDeleted ? - instances.filter(i => i.deleted) : (showIsActive ? - instances.filter(i => !i.deleted) : - instances) - - return <div id="app"> + const showingInstances = showIsDeleted + ? instances.filter((i) => i.deleted) + : showIsActive + ? instances.filter((i) => !i.deleted) + : instances; - <section class="section is-main-section"> - <div class="columns"> - <div class="column is-two-thirds"> - <div class="tabs" style={{ overflow: 'inherit' }}> - <ul> - <li class={showIsActive}> - <div class="has-tooltip-right" data-tooltip={i18n`Only show active instances`}> - <a onClick={() => setShow("active")}><Translate>Active</Translate></a> - </div> - </li> - <li class={showIsDeleted}> - <div class="has-tooltip-right" data-tooltip={i18n`Only show deleted instances`}> - <a onClick={() => setShow("deleted")}><Translate>Deleted</Translate></a> - </div> - </li> - <li class={showAll}> - <div class="has-tooltip-right" data-tooltip={i18n`Show all instances`}> - <a onClick={() => setShow(null)}><Translate>All</Translate></a> - </div> - </li> - </ul> + return ( + <div id="app"> + <section class="section is-main-section"> + <div class="columns"> + <div class="column is-two-thirds"> + <div class="tabs" style={{ overflow: "inherit" }}> + <ul> + <li class={showIsActive}> + <div + class="has-tooltip-right" + data-tooltip={i18n`Only show active instances`} + > + <a onClick={() => setShow("active")}> + <Translate>Active</Translate> + </a> + </div> + </li> + <li class={showIsDeleted}> + <div + class="has-tooltip-right" + data-tooltip={i18n`Only show deleted instances`} + > + <a onClick={() => setShow("deleted")}> + <Translate>Deleted</Translate> + </a> + </div> + </li> + <li class={showAll}> + <div + class="has-tooltip-right" + data-tooltip={i18n`Show all instances`} + > + <a onClick={() => setShow(null)}> + <Translate>All</Translate> + </a> + </div> + </li> + </ul> + </div> </div> </div> - </div> - <CardTableActive instances={showingInstances} onDelete={onDelete} onPurge={onPurge} setInstanceName={setInstanceName} onUpdate={onUpdate} selected={selected} onCreate={onCreate} /> - </section> - - </div > + <CardTableActive + instances={showingInstances} + onDelete={onDelete} + onPurge={onPurge} + setInstanceName={setInstanceName} + onUpdate={onUpdate} + selected={selected} + onCreate={onCreate} + /> + </section> + </div> + ); } diff --git a/packages/merchant-backoffice-ui/src/paths/index.stories.ts b/packages/merchant-backoffice-ui/src/paths/index.stories.ts index b3811fd4f..ecf28445b 100644 --- a/packages/merchant-backoffice-ui/src/paths/index.stories.ts +++ b/packages/merchant-backoffice-ui/src/paths/index.stories.ts @@ -1,2 +1,2 @@ export * as a1 from "./admin/create/Create.stories.js"; -export * as a2 from "./instance/details/Details.stories.js";
\ No newline at end of file +export * as a2 from "./instance/details/Details.stories.js"; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx index 01e7771cf..59aa7a1d2 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx @@ -15,9 +15,9 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; @@ -33,55 +33,57 @@ interface Props { selected: MerchantBackend.Instances.QueryInstancesResponse; } -function convert(from: MerchantBackend.Instances.QueryInstancesResponse): Entity { - const { accounts, ...rest } = from - const payto_uris = accounts.filter(a => a.active).map(a => a.payto_uri) +function convert( + from: MerchantBackend.Instances.QueryInstancesResponse, +): Entity { + const { accounts, ...rest } = from; + const payto_uris = accounts.filter((a) => a.active).map((a) => a.payto_uri); const defaults = { default_wire_fee_amortization: 1, default_pay_delay: { d_us: 1000 * 60 * 60 }, //one hour default_wire_transfer_delay: { d_us: 1000 * 60 * 60 * 2 }, //two hours - } + }; return { ...defaults, ...rest, payto_uris }; } export function DetailPage({ selected }: Props): VNode { - const [value, valueHandler] = useState<Partial<Entity>>(convert(selected)) + const [value, valueHandler] = useState<Partial<Entity>>(convert(selected)); + + const i18n = useTranslator(); - const i18n = useTranslator() - - return <div> - <section class="hero is-hero-bar"> - <div class="hero-body"> - <div class="level"> - <div class="level-left"> - <div class="level-item"> - <h1 class="title"> - Here goes the instance description - </h1> + return ( + <div> + <section class="hero is-hero-bar"> + <div class="hero-body"> + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <h1 class="title">Here goes the instance description</h1> + </div> + </div> + <div class="level-right" style="display: none;"> + <div class="level-item" /> </div> - </div> - <div class="level-right" style="display: none;"> - <div class="level-item" /> </div> </div> - </div> - </section> - - <section class="section is-main-section"> - <div class="columns"> - <div class="column" /> - <div class="column is-6"> - <FormProvider<Entity> object={value} valueHandler={valueHandler} > + </section> - <Input<Entity> name="name" readonly label={i18n`Name`} /> - <Input<Entity> name="payto_uris" readonly label={i18n`Account address`} /> - - </FormProvider> + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-6"> + <FormProvider<Entity> object={value} valueHandler={valueHandler}> + <Input<Entity> name="name" readonly label={i18n`Name`} /> + <Input<Entity> + name="payto_uris" + readonly + label={i18n`Account address`} + /> + </FormProvider> + </div> + <div class="column" /> </div> - <div class="column" /> - </div> - </section> - - </div> - -}
\ No newline at end of file + </section> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/details/Details.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/details/Details.stories.tsx index f7772895f..9ac1c4381 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/details/Details.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/details/Details.stories.tsx @@ -33,7 +33,7 @@ export default { function createExample<Props>( Component: FunctionalComponent<Props>, - props: Partial<Props> + props: Partial<Props>, ) { const r = (args: any) => <Component {...args} />; r.args = props; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/details/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/details/index.tsx index 09b692e00..49b64262b 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/details/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/details/index.tsx @@ -30,36 +30,44 @@ interface Props { onDelete: () => void; } -export default function Detail({ onUpdate, onLoadError, onUnauthorized, onDelete, onNotFound }: Props): VNode { - const { id } = useInstanceContext() - const result = useInstanceDetails() - const [deleting, setDeleting] = useState<boolean>(false) +export default function Detail({ + onUpdate, + onLoadError, + onUnauthorized, + onDelete, + onNotFound, +}: Props): VNode { + const { id } = useInstanceContext(); + const result = useInstanceDetails(); + const [deleting, setDeleting] = useState<boolean>(false); - const { deleteInstance } = useInstanceAPI() + const { deleteInstance } = useInstanceAPI(); - if (result.clientError && result.isUnauthorized) return onUnauthorized() - if (result.clientError && result.isNotfound) return onNotFound() - if (result.loading) return <Loading /> - if (!result.ok) return onLoadError(result) + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); - return <Fragment> - <DetailPage - selected={result.data} - onUpdate={onUpdate} - onDelete={() => setDeleting(true)} - /> - {deleting && <DeleteModal - element={{ name: result.data.name, id }} - onCancel={() => setDeleting(false)} - onConfirm={async (): Promise<void> => { - try { - await deleteInstance() - onDelete() - } catch (error) { - } - setDeleting(false) - }} - />} - - </Fragment> -}
\ No newline at end of file + return ( + <Fragment> + <DetailPage + selected={result.data} + onUpdate={onUpdate} + onDelete={() => setDeleting(true)} + /> + {deleting && ( + <DeleteModal + element={{ name: result.data.name, id }} + onCancel={() => setDeleting(false)} + onConfirm={async (): Promise<void> => { + try { + await deleteInstance(); + onDelete(); + } catch (error) {} + setDeleting(false); + }} + /> + )} + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx index 4ce2eb43d..5f8dbbdd9 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx @@ -33,7 +33,7 @@ export default { function createExample<Props>( Component: FunctionalComponent<Props>, - props: Partial<Props> + props: Partial<Props>, ) { const r = (args: any) => <Component {...args} />; r.args = props; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx index 379c5eab5..145df717d 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx @@ -166,7 +166,7 @@ export function CreatePage({ : value.payments.wire_transfer_deadline && isBefore( value.payments.wire_transfer_deadline, - value.payments.refund_deadline + value.payments.refund_deadline, ) ? i18n`wire transfer deadline cannot be before refund deadline` : undefined, @@ -177,7 +177,7 @@ export function CreatePage({ : value.payments.wire_transfer_deadline && isBefore( value.payments.wire_transfer_deadline, - value.payments.pay_deadline + value.payments.pay_deadline, ) ? i18n`wire transfer deadline cannot be before pay deadline` : undefined, @@ -189,7 +189,7 @@ export function CreatePage({ ? i18n`should have a refund deadline` : !isAfter( value.payments.refund_deadline, - value.payments.auto_refund_deadline + value.payments.auto_refund_deadline, ) ? i18n`auto refund cannot be after refund deadline` : undefined, @@ -203,7 +203,7 @@ export function CreatePage({ }), }; const hasErrors = Object.keys(errors).some( - (k) => (errors as any)[k] !== undefined + (k) => (errors as any)[k] !== undefined, ); const submit = (): void => { @@ -225,7 +225,7 @@ export function CreatePage({ wire_transfer_deadline: value.payments.wire_transfer_deadline ? { t_s: Math.floor( - value.payments.wire_transfer_deadline.getTime() / 1000 + value.payments.wire_transfer_deadline.getTime() / 1000, ), } : undefined, @@ -237,7 +237,7 @@ export function CreatePage({ auto_refund: value.payments.auto_refund_deadline ? { d_us: Math.floor( - value.payments.auto_refund_deadline.getTime() * 1000 + value.payments.auto_refund_deadline.getTime() * 1000, ), } : undefined, @@ -264,7 +264,7 @@ export function CreatePage({ const addProductToTheInventoryList = ( product: MerchantBackend.Products.ProductDetail & WithId, - quantity: number + quantity: number, ) => { valueHandler((v) => { const inventoryProducts = { ...v.inventoryProducts }; @@ -332,13 +332,13 @@ export function CreatePage({ const discountOrRise = rate( value.pricing?.order_price || `${config.currency}:0`, - totalAsString + totalAsString, ); const minAgeByProducts = allProducts.reduce( (cur, prev) => !prev.minimum_age || cur > prev.minimum_age ? cur : prev.minimum_age, - 0 + 0, ); return ( <div> @@ -415,7 +415,7 @@ export function CreatePage({ discountOrRise > 0 && (discountOrRise < 1 ? `discount of %${Math.round( - (1 - discountOrRise) * 100 + (1 - discountOrRise) * 100, )}` : `rise of %${Math.round((discountOrRise - 1) * 100)}`) } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx index bd63ca371..6d3ac311a 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx @@ -26,64 +26,89 @@ interface Props { onCreateAnother?: () => void; } -export function OrderCreatedSuccessfully({ entity, onConfirm, onCreateAnother }: Props): VNode { - const { getPaymentURL } = useOrderAPI() - const [url, setURL] = useState<string | undefined>(undefined) +export function OrderCreatedSuccessfully({ + entity, + onConfirm, + onCreateAnother, +}: Props): VNode { + const { getPaymentURL } = useOrderAPI(); + const [url, setURL] = useState<string | undefined>(undefined); useEffect(() => { - getPaymentURL(entity.response.order_id).then(response => { - setURL(response.data) - }) - }, [getPaymentURL, entity.response.order_id]) + getPaymentURL(entity.response.order_id).then((response) => { + setURL(response.data); + }); + }, [getPaymentURL, entity.response.order_id]); - return <CreatedSuccessfully onConfirm={onConfirm} onCreateAnother={onCreateAnother}> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"><Translate>Amount</Translate></label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input class="input" readonly value={entity.request.order.amount} /> - </p> + return ( + <CreatedSuccessfully + onConfirm={onConfirm} + onCreateAnother={onCreateAnother} + > + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <Translate>Amount</Translate> + </label> </div> - </div> - </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"><Translate>Summary</Translate></label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input class="input" readonly value={entity.request.order.summary} /> - </p> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input + class="input" + readonly + value={entity.request.order.amount} + /> + </p> + </div> </div> </div> - </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"><Translate>Order ID</Translate></label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input class="input" readonly value={entity.response.order_id} /> - </p> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <Translate>Summary</Translate> + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input + class="input" + readonly + value={entity.request.order.summary} + /> + </p> + </div> </div> </div> - </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"><Translate>Payment URL</Translate></label> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <Translate>Order ID</Translate> + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={entity.response.order_id} /> + </p> + </div> + </div> </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input class="input" readonly value={url} /> - </p> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <Translate>Payment URL</Translate> + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={url} /> + </p> + </div> </div> </div> - </div> - </CreatedSuccessfully>; + </CreatedSuccessfully> + ); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx index feb75aa25..95232da92 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx @@ -15,12 +15,12 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { Fragment, h, VNode } from 'preact'; -import { useState } from 'preact/hooks'; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; import { Loading } from "../../../../components/exception/loading.js"; import { NotificationCard } from "../../../../components/menu/index.js"; import { MerchantBackend } from "../../../../declaration.js"; @@ -33,9 +33,9 @@ import { CreatePage } from "./CreatePage.js"; import { OrderCreatedSuccessfully } from "./OrderCreatedSuccessfully.js"; export type Entity = { - request: MerchantBackend.Orders.PostOrderRequest, - response: MerchantBackend.Orders.PostOrderResponse -} + request: MerchantBackend.Orders.PostOrderRequest; + response: MerchantBackend.Orders.PostOrderResponse; +}; interface Props { onBack?: () => void; onConfirm: () => void; @@ -43,40 +43,53 @@ interface Props { onNotFound: () => VNode; onLoadError: (error: HttpError) => VNode; } -export default function OrderCreate({ onConfirm, onBack, onLoadError, onNotFound, onUnauthorized }: Props): VNode { - const { createOrder } = useOrderAPI() - const [notif, setNotif] = useState<Notification | undefined>(undefined) +export default function OrderCreate({ + onConfirm, + onBack, + onLoadError, + onNotFound, + onUnauthorized, +}: Props): VNode { + const { createOrder } = useOrderAPI(); + const [notif, setNotif] = useState<Notification | undefined>(undefined); - const detailsResult = useInstanceDetails() - const inventoryResult = useInstanceProducts() + const detailsResult = useInstanceDetails(); + const inventoryResult = useInstanceProducts(); - if (detailsResult.clientError && detailsResult.isUnauthorized) return onUnauthorized() - if (detailsResult.clientError && detailsResult.isNotfound) return onNotFound() - if (detailsResult.loading) return <Loading /> - if (!detailsResult.ok) return onLoadError(detailsResult) + if (detailsResult.clientError && detailsResult.isUnauthorized) + return onUnauthorized(); + if (detailsResult.clientError && detailsResult.isNotfound) + return onNotFound(); + if (detailsResult.loading) return <Loading />; + if (!detailsResult.ok) return onLoadError(detailsResult); - if (inventoryResult.clientError && inventoryResult.isUnauthorized) return onUnauthorized() - if (inventoryResult.clientError && inventoryResult.isNotfound) return onNotFound() - if (inventoryResult.loading) return <Loading /> - if (!inventoryResult.ok) return onLoadError(inventoryResult) + if (inventoryResult.clientError && inventoryResult.isUnauthorized) + return onUnauthorized(); + if (inventoryResult.clientError && inventoryResult.isNotfound) + return onNotFound(); + if (inventoryResult.loading) return <Loading />; + if (!inventoryResult.ok) return onLoadError(inventoryResult); - return <Fragment> - - <NotificationCard notification={notif} /> + return ( + <Fragment> + <NotificationCard notification={notif} /> - <CreatePage - onBack={onBack} - onCreate={(request: MerchantBackend.Orders.PostOrderRequest) => { - createOrder(request).then(onConfirm).catch((error) => { - setNotif({ - message: 'could not create order', - type: "ERROR", - description: error.message - }) - }) - }} - instanceConfig={detailsResult.data} - instanceInventory={inventoryResult.data} + <CreatePage + onBack={onBack} + onCreate={(request: MerchantBackend.Orders.PostOrderRequest) => { + createOrder(request) + .then(onConfirm) + .catch((error) => { + setNotif({ + message: "could not create order", + type: "ERROR", + description: error.message, + }); + }); + }} + instanceConfig={detailsResult.data} + instanceInventory={inventoryResult.data} /> - </Fragment> + </Fragment> + ); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx index 878ee7bde..e430ede56 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx @@ -35,7 +35,7 @@ export default { function createExample<Props>( Component: FunctionalComponent<Props>, - props: Partial<Props> + props: Partial<Props>, ) { const r = (args: any) => <Component {...args} />; r.args = props; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx index 2074eeb32..e8927dd70 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx @@ -248,7 +248,7 @@ function ClaimedPage({ </b>{" "} {format( new Date(order.contract_terms.timestamp.t_s * 1000), - "yyyy-MM-dd HH:mm:ss" + "yyyy-MM-dd HH:mm:ss", )} </p> </div> @@ -519,7 +519,7 @@ function PaidPage({ <p> {format( new Date(order.contract_terms.timestamp.t_s * 1000), - "yyyy/MM/dd HH:mm:ss" + "yyyy/MM/dd HH:mm:ss", )} </p> </div> @@ -669,7 +669,7 @@ function UnpaidPage({ ? "never" : format( new Date(order.creation_time.t_s * 1000), - "yyyy-MM-dd HH:mm:ss" + "yyyy-MM-dd HH:mm:ss", )} </p> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx index dbf1b685a..4633688ba 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx @@ -32,36 +32,49 @@ export interface Props { onLoadError: (error: HttpError) => VNode; } -export default function Update({ oid, onBack, onLoadError, onNotFound, onUnauthorized }: Props): VNode { +export default function Update({ + oid, + onBack, + onLoadError, + onNotFound, + onUnauthorized, +}: Props): VNode { const { refundOrder } = useOrderAPI(); - const result = useOrderDetails(oid) - const [notif, setNotif] = useState<Notification | undefined>(undefined) + const result = useOrderDetails(oid); + const [notif, setNotif] = useState<Notification | undefined>(undefined); - const i18n = useTranslator() + const i18n = useTranslator(); - if (result.clientError && result.isUnauthorized) return onUnauthorized() - if (result.clientError && result.isNotfound) return onNotFound() - if (result.loading) return <Loading /> - if (!result.ok) return onLoadError(result) + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); - return <Fragment> + return ( + <Fragment> + <NotificationCard notification={notif} /> - <NotificationCard notification={notif} /> - - <DetailPage - onBack={onBack} - id={oid} - onRefund={(id, value) => refundOrder(id, value) - .then(() => setNotif({ - message: i18n`refund created successfully`, - type: "SUCCESS" - })).catch((error) => setNotif({ - message: i18n`could not create the refund`, - type: "ERROR", - description: error.message - })) - } - selected={result.data} - /> - </Fragment> -}
\ No newline at end of file + <DetailPage + onBack={onBack} + id={oid} + onRefund={(id, value) => + refundOrder(id, value) + .then(() => + setNotif({ + message: i18n`refund created successfully`, + type: "SUCCESS", + }), + ) + .catch((error) => + setNotif({ + message: i18n`could not create the refund`, + type: "ERROR", + description: error.message, + }), + ) + } + selected={result.data} + /> + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx index 8cddd7fd6..156c577f4 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx @@ -43,7 +43,7 @@ export default { function createExample<Props>( Component: FunctionalComponent<Props>, - props: Partial<Props> + props: Partial<Props>, ) { const r = (args: any) => <Component {...args} />; r.args = props; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx index 60be23c21..bca90e352 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx @@ -15,33 +15,33 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { format } from 'date-fns'; -import { h, VNode } from 'preact'; -import { useState } from 'preact/hooks'; +import { format } from "date-fns"; +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; import { DatePicker } from "../../../../components/picker/DatePicker.js"; import { MerchantBackend, WithId } from "../../../../declaration.js"; -import { Translate, useTranslator } from '../../../../i18n/index.js'; +import { Translate, useTranslator } from "../../../../i18n/index.js"; import { CardTable } from "./Table.js"; export interface ListPageProps { - errorOrderId: string | undefined, + errorOrderId: string | undefined; - onShowAll: () => void, - onShowPaid: () => void, - onShowRefunded: () => void, - onShowNotWired: () => void, + onShowAll: () => void; + onShowPaid: () => void; + onShowRefunded: () => void; + onShowNotWired: () => void; onCopyURL: (id: string) => void; - isAllActive: string, - isPaidActive: string, - isRefundedActive: string, - isNotWiredActive: string, + isAllActive: string; + isPaidActive: string; + isRefundedActive: string; + isNotWiredActive: string; - jumpToDate?: Date, - onSelectDate: (date?: Date) => void, + jumpToDate?: Date; + onSelectDate: (date?: Date) => void; orders: (MerchantBackend.Orders.OrderHistoryEntry & WithId)[]; onLoadMoreBefore?: () => void; @@ -55,92 +55,168 @@ export interface ListPageProps { onCreate: () => void; } -export function ListPage({ orders, errorOrderId, isAllActive, onSelectOrder, onRefundOrder, onSearchOrderById, jumpToDate, onCopyURL, onShowAll, onShowPaid, onShowRefunded, onShowNotWired, onSelectDate, isPaidActive, isRefundedActive, isNotWiredActive, onCreate }: ListPageProps): VNode { +export function ListPage({ + orders, + errorOrderId, + isAllActive, + onSelectOrder, + onRefundOrder, + onSearchOrderById, + jumpToDate, + onCopyURL, + onShowAll, + onShowPaid, + onShowRefunded, + onShowNotWired, + onSelectDate, + isPaidActive, + isRefundedActive, + isNotWiredActive, + onCreate, +}: ListPageProps): VNode { const i18n = useTranslator(); const dateTooltip = i18n`select date to show nearby orders`; const [pickDate, setPickDate] = useState(false); - const [orderId, setOrderId] = useState<string>(''); - - return <section class="section is-main-section"> + const [orderId, setOrderId] = useState<string>(""); - <div class="level"> - <div class="level-left"> - <div class="level-item"> - <div class="field has-addons"> - <div class="control"> - <input class={errorOrderId ? "input is-danger" : "input"} type="text" value={orderId} onChange={e => setOrderId(e.currentTarget.value)} placeholder={i18n`order id`} /> - {errorOrderId && <p class="help is-danger">{errorOrderId}</p>} + return ( + <section class="section is-main-section"> + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <div class="field has-addons"> + <div class="control"> + <input + class={errorOrderId ? "input is-danger" : "input"} + type="text" + value={orderId} + onChange={(e) => setOrderId(e.currentTarget.value)} + placeholder={i18n`order id`} + /> + {errorOrderId && <p class="help is-danger">{errorOrderId}</p>} + </div> + <span + class="has-tooltip-bottom" + data-tooltip={i18n`jump to order with the given order ID`} + > + <button + class="button" + onClick={(e) => onSearchOrderById(orderId)} + > + <span class="icon"> + <i class="mdi mdi-arrow-right" /> + </span> + </button> + </span> </div> - <span class="has-tooltip-bottom" data-tooltip={i18n`jump to order with the given order ID`}> - <button class="button" onClick={(e) => onSearchOrderById(orderId)}> - <span class="icon"><i class="mdi mdi-arrow-right" /></span> - </button> - </span> </div> </div> </div> - </div> - <div class="columns"> - <div class="column is-two-thirds"> - <div class="tabs" style={{overflow:'inherit'}}> - <ul> - <li class={isAllActive}> - <div class="has-tooltip-right" data-tooltip={i18n`remove all filters`}> - <a onClick={onShowAll}><Translate>All</Translate></a> - </div> - </li> - <li class={isPaidActive}> - <div class="has-tooltip-right" data-tooltip={i18n`only show paid orders`}> - <a onClick={onShowPaid}><Translate>Paid</Translate></a> - </div> - </li> - <li class={isRefundedActive}> - <div class="has-tooltip-right" data-tooltip={i18n`only show orders with refunds`}> - <a onClick={onShowRefunded}><Translate>Refunded</Translate></a> + <div class="columns"> + <div class="column is-two-thirds"> + <div class="tabs" style={{ overflow: "inherit" }}> + <ul> + <li class={isAllActive}> + <div + class="has-tooltip-right" + data-tooltip={i18n`remove all filters`} + > + <a onClick={onShowAll}> + <Translate>All</Translate> + </a> + </div> + </li> + <li class={isPaidActive}> + <div + class="has-tooltip-right" + data-tooltip={i18n`only show paid orders`} + > + <a onClick={onShowPaid}> + <Translate>Paid</Translate> + </a> + </div> + </li> + <li class={isRefundedActive}> + <div + class="has-tooltip-right" + data-tooltip={i18n`only show orders with refunds`} + > + <a onClick={onShowRefunded}> + <Translate>Refunded</Translate> + </a> + </div> + </li> + <li class={isNotWiredActive}> + <div + class="has-tooltip-left" + data-tooltip={i18n`only show orders where customers paid, but wire payments from payment provider are still pending`} + > + <a onClick={onShowNotWired}> + <Translate>Not wired</Translate> + </a> + </div> + </li> + </ul> + </div> + </div> + <div class="column "> + <div class="buttons is-right"> + <div class="field has-addons"> + {jumpToDate && ( + <div class="control"> + <a class="button" onClick={() => onSelectDate(undefined)}> + <span class="icon" data-tooltip={i18n`clear date filter`}> + <i class="mdi mdi-close" /> + </span> + </a> + </div> + )} + <div class="control"> + <span class="has-tooltip-top" data-tooltip={dateTooltip}> + <input + class="input" + type="text" + readonly + value={!jumpToDate ? "" : format(jumpToDate, "yyyy/MM/dd")} + placeholder={i18n`date (YYYY/MM/DD)`} + onClick={() => { + setPickDate(true); + }} + /> + </span> </div> - </li> - <li class={isNotWiredActive}> - <div class="has-tooltip-left" data-tooltip={i18n`only show orders where customers paid, but wire payments from payment provider are still pending`}> - <a onClick={onShowNotWired}><Translate>Not wired</Translate></a> + <div class="control"> + <span class="has-tooltip-left" data-tooltip={dateTooltip}> + <a + class="button" + onClick={() => { + setPickDate(true); + }} + > + <span class="icon"> + <i class="mdi mdi-calendar" /> + </span> + </a> + </span> </div> - </li> - </ul> - </div> - </div> - <div class="column "> - <div class="buttons is-right"> - <div class="field has-addons"> - {jumpToDate && <div class="control"> - <a class="button" onClick={() => onSelectDate(undefined)}> - <span class="icon" data-tooltip={i18n`clear date filter`}><i class="mdi mdi-close" /></span> - </a> - </div>} - <div class="control"> - <span class="has-tooltip-top" data-tooltip={dateTooltip}> - <input class="input" type="text" readonly value={!jumpToDate ? '' : format(jumpToDate, 'yyyy/MM/dd')} placeholder={i18n`date (YYYY/MM/DD)`} onClick={() => { setPickDate(true); }} /> - </span> - </div> - <div class="control"> - <span class="has-tooltip-left" data-tooltip={dateTooltip}> - <a class="button" onClick={() => { setPickDate(true); }}> - <span class="icon"><i class="mdi mdi-calendar" /></span> - </a> - </span> </div> </div> </div> </div> - </div> - <DatePicker - opened={pickDate} - closeFunction={() => setPickDate(false)} - dateReceiver={onSelectDate} /> + <DatePicker + opened={pickDate} + closeFunction={() => setPickDate(false)} + dateReceiver={onSelectDate} + /> - <CardTable orders={orders} - onCreate={onCreate} - onCopyURL={onCopyURL} - onSelect={onSelectOrder} - onRefund={onRefundOrder} /> - </section>; + <CardTable + orders={orders} + onCreate={onCreate} + onCopyURL={onCopyURL} + onSelect={onSelectOrder} + onRefund={onRefundOrder} + /> + </section> + ); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx index 924d09682..a1ec8d291 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx @@ -173,7 +173,7 @@ function Table({ ? "never" : format( new Date(i.timestamp.t_s * 1000), - "yyyy/MM/dd HH:mm:ss" + "yyyy/MM/dd HH:mm:ss", )} </td> <td @@ -268,7 +268,7 @@ export function RefundModal({ .map((r) => r.amount) .reduce( (p, c) => Amounts.add(p, Amounts.parseOrThrow(c)).amount, - Amounts.zeroOfCurrency(config.currency) + Amounts.zeroOfCurrency(config.currency), ); const orderPrice = order.order_status === "paid" @@ -298,7 +298,7 @@ export function RefundModal({ : undefined, }; const hasErrors = Object.keys(errors).some( - (k) => (errors as any)[k] !== undefined + (k) => (errors as any)[k] !== undefined, ); const validateAndConfirm = () => { @@ -306,7 +306,7 @@ export function RefundModal({ if (!form.refund) return; onConfirm({ refund: Amounts.stringify( - Amounts.add(Amounts.parse(form.refund)!, totalRefunded).amount + Amounts.add(Amounts.parse(form.refund)!, totalRefunded).amount, ), reason: form.description === undefined @@ -358,7 +358,7 @@ export function RefundModal({ ? "never" : format( new Date(r.timestamp.t_s * 1000), - "yyyy-MM-dd HH:mm:ss" + "yyyy-MM-dd HH:mm:ss", )} </td> <td>{r.amount}</td> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx index a033e7b3a..b5fe7611c 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx @@ -15,18 +15,23 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode, Fragment } from 'preact'; -import { useState } from 'preact/hooks'; +import { h, VNode, Fragment } from "preact"; +import { useState } from "preact/hooks"; import { Loading } from "../../../../components/exception/loading.js"; import { NotificationCard } from "../../../../components/menu/index.js"; import { MerchantBackend, WithId } from "../../../../declaration.js"; import { HttpError } from "../../../../hooks/backend.js"; -import { InstanceOrderFilter, useInstanceOrders, useOrderAPI, useOrderDetails } from "../../../../hooks/order.js"; -import { useTranslator } from '../../../../i18n/index.js'; +import { + InstanceOrderFilter, + useInstanceOrders, + useOrderAPI, + useOrderDetails, +} from "../../../../hooks/order.js"; +import { useTranslator } from "../../../../i18n/index.js"; import { Notification } from "../../../../utils/types.js"; import { RefundModal } from "./Table.js"; import { ListPage } from "./ListPage.js"; @@ -39,107 +44,133 @@ interface Props { onCreate: () => void; } -export default function ({ onUnauthorized, onLoadError, onCreate, onSelect, onNotFound }: Props): VNode { - const [filter, setFilter] = useState<InstanceOrderFilter>({}) - const [orderToBeRefunded, setOrderToBeRefunded] = useState<MerchantBackend.Orders.OrderHistoryEntry | undefined>(undefined) - - const setNewDate = (date?: Date) => setFilter(prev => ({ ...prev, date })) - - const result = useInstanceOrders(filter, setNewDate) - const { refundOrder, getPaymentURL } = useOrderAPI() - - const [notif, setNotif] = useState<Notification | undefined>(undefined) - - if (result.clientError && result.isUnauthorized) return onUnauthorized() - if (result.clientError && result.isNotfound) return onNotFound() - if (result.loading) return <Loading /> - if (!result.ok) return onLoadError(result) - - const isPaidActive = filter.paid === 'yes' ? "is-active" : '' - const isRefundedActive = filter.refunded === 'yes' ? "is-active" : '' - const isNotWiredActive = filter.wired === 'no' ? "is-active" : '' - const isAllActive = filter.paid === undefined && filter.refunded === undefined && filter.wired === undefined ? 'is-active' : '' - - const i18n = useTranslator() - const [errorOrderId, setErrorOrderId] = useState<string | undefined>(undefined) +export default function ({ + onUnauthorized, + onLoadError, + onCreate, + onSelect, + onNotFound, +}: Props): VNode { + const [filter, setFilter] = useState<InstanceOrderFilter>({}); + const [orderToBeRefunded, setOrderToBeRefunded] = useState< + MerchantBackend.Orders.OrderHistoryEntry | undefined + >(undefined); + + const setNewDate = (date?: Date) => setFilter((prev) => ({ ...prev, date })); + + const result = useInstanceOrders(filter, setNewDate); + const { refundOrder, getPaymentURL } = useOrderAPI(); + + const [notif, setNotif] = useState<Notification | undefined>(undefined); + + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); + + const isPaidActive = filter.paid === "yes" ? "is-active" : ""; + const isRefundedActive = filter.refunded === "yes" ? "is-active" : ""; + const isNotWiredActive = filter.wired === "no" ? "is-active" : ""; + const isAllActive = + filter.paid === undefined && + filter.refunded === undefined && + filter.wired === undefined + ? "is-active" + : ""; + + const i18n = useTranslator(); + const [errorOrderId, setErrorOrderId] = useState<string | undefined>( + undefined, + ); async function testIfOrderExistAndSelect(orderId: string) { if (!orderId) { - setErrorOrderId(i18n`Enter an order id`) + setErrorOrderId(i18n`Enter an order id`); return; } try { - await getPaymentURL(orderId) - onSelect(orderId) - setErrorOrderId(undefined) + await getPaymentURL(orderId); + onSelect(orderId); + setErrorOrderId(undefined); } catch { - setErrorOrderId(i18n`order not found`) + setErrorOrderId(i18n`order not found`); } } - return <Fragment> - <NotificationCard notification={notif} /> - - <ListPage - orders={result.data.orders.map(o => ({ ...o, id: o.order_id }))} - onLoadMoreBefore={result.loadMorePrev} hasMoreBefore={!result.isReachingStart} - onLoadMoreAfter={result.loadMore} hasMoreAfter={!result.isReachingEnd} - - onSelectOrder={(order) => onSelect(order.id)} - onRefundOrder={(value) => setOrderToBeRefunded(value)} - - errorOrderId={errorOrderId} - isAllActive={isAllActive} - isNotWiredActive={isNotWiredActive} - isPaidActive={isPaidActive} - isRefundedActive={isRefundedActive} - jumpToDate={filter.date} - onCopyURL={(id) => getPaymentURL(id).then((resp) => copyToClipboard(resp.data))} - - onCreate={onCreate} - onSearchOrderById={testIfOrderExistAndSelect} - onSelectDate={setNewDate} - onShowAll={() => setFilter({})} - onShowPaid={() => setFilter({ paid: 'yes' })} - onShowRefunded={() => setFilter({ refunded: 'yes' })} - onShowNotWired={() => setFilter({ wired: 'no' })} - - /> - - {orderToBeRefunded && <RefundModalForTable - id={orderToBeRefunded.order_id} - onCancel={() => setOrderToBeRefunded(undefined)} - onConfirm={(value) => refundOrder(orderToBeRefunded.order_id, value) - .then(() => setNotif({ - message: i18n`refund created successfully`, - type: "SUCCESS" - })) - .catch((error) => setNotif({ - message: i18n`could not create the refund`, - type: "ERROR", - description: error.message - })) - .then(() => setOrderToBeRefunded(undefined))} - onLoadError={(error) => { - setNotif({ - message: i18n`could not create the refund`, - type: "ERROR", - description: error.message - }); - setOrderToBeRefunded(undefined); - return <div />; - }} - onUnauthorized={onUnauthorized} - onNotFound={() => { - setNotif({ - message: i18n`could not get the order to refund`, - type: "ERROR", - // description: error.message - }); - setOrderToBeRefunded(undefined); - return <div />; - }} />} - </Fragment> + return ( + <Fragment> + <NotificationCard notification={notif} /> + + <ListPage + orders={result.data.orders.map((o) => ({ ...o, id: o.order_id }))} + onLoadMoreBefore={result.loadMorePrev} + hasMoreBefore={!result.isReachingStart} + onLoadMoreAfter={result.loadMore} + hasMoreAfter={!result.isReachingEnd} + onSelectOrder={(order) => onSelect(order.id)} + onRefundOrder={(value) => setOrderToBeRefunded(value)} + errorOrderId={errorOrderId} + isAllActive={isAllActive} + isNotWiredActive={isNotWiredActive} + isPaidActive={isPaidActive} + isRefundedActive={isRefundedActive} + jumpToDate={filter.date} + onCopyURL={(id) => + getPaymentURL(id).then((resp) => copyToClipboard(resp.data)) + } + onCreate={onCreate} + onSearchOrderById={testIfOrderExistAndSelect} + onSelectDate={setNewDate} + onShowAll={() => setFilter({})} + onShowPaid={() => setFilter({ paid: "yes" })} + onShowRefunded={() => setFilter({ refunded: "yes" })} + onShowNotWired={() => setFilter({ wired: "no" })} + /> + + {orderToBeRefunded && ( + <RefundModalForTable + id={orderToBeRefunded.order_id} + onCancel={() => setOrderToBeRefunded(undefined)} + onConfirm={(value) => + refundOrder(orderToBeRefunded.order_id, value) + .then(() => + setNotif({ + message: i18n`refund created successfully`, + type: "SUCCESS", + }), + ) + .catch((error) => + setNotif({ + message: i18n`could not create the refund`, + type: "ERROR", + description: error.message, + }), + ) + .then(() => setOrderToBeRefunded(undefined)) + } + onLoadError={(error) => { + setNotif({ + message: i18n`could not create the refund`, + type: "ERROR", + description: error.message, + }); + setOrderToBeRefunded(undefined); + return <div />; + }} + onUnauthorized={onUnauthorized} + onNotFound={() => { + setNotif({ + message: i18n`could not get the order to refund`, + type: "ERROR", + // description: error.message + }); + setOrderToBeRefunded(undefined); + return <div />; + }} + /> + )} + </Fragment> + ); } interface RefundProps { @@ -151,21 +182,30 @@ interface RefundProps { onConfirm: (m: MerchantBackend.Orders.RefundRequest) => void; } -function RefundModalForTable({ id, onUnauthorized, onLoadError, onNotFound, onConfirm, onCancel }: RefundProps) { +function RefundModalForTable({ + id, + onUnauthorized, + onLoadError, + onNotFound, + onConfirm, + onCancel, +}: RefundProps) { const result = useOrderDetails(id); - if (result.clientError && result.isUnauthorized) return onUnauthorized() - if (result.clientError && result.isNotfound) return onNotFound() - if (result.loading) return <Loading /> - if (!result.ok) return onLoadError(result) + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); - return <RefundModal - order={result.data} - onCancel={onCancel} - onConfirm={onConfirm} - /> + return ( + <RefundModal + order={result.data} + onCancel={onCancel} + onConfirm={onConfirm} + /> + ); } async function copyToClipboard(text: string) { - return navigator.clipboard.writeText(text) + return navigator.clipboard.writeText(text); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/create/Create.stories.tsx index b5e0ff9c7..2fc0819bb 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/create/Create.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/create/Create.stories.tsx @@ -15,28 +15,29 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode, FunctionalComponent } from 'preact'; +import { h, VNode, FunctionalComponent } from "preact"; import { CreatePage as TestedComponent } from "./CreatePage.js"; - export default { - title: 'Pages/Product/Create', + title: "Pages/Product/Create", component: TestedComponent, argTypes: { - onCreate: { action: 'onCreate' }, - onBack: { action: 'onBack' }, + onCreate: { action: "onCreate" }, + onBack: { action: "onBack" }, }, }; -function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { - const r = (args: any) => <Component {...args} /> - r.args = props - return r +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props>, +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; } -export const Example = createExample(TestedComponent, { -}); +export const Example = createExample(TestedComponent, {}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx index 3b475cb82..f6d7000ef 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx @@ -15,9 +15,9 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { AsyncButton } from "../../../../components/exception/AsyncButton.js"; @@ -26,40 +26,55 @@ import { MerchantBackend } from "../../../../declaration.js"; import { useListener } from "../../../../hooks/listener.js"; import { Translate, useTranslator } from "../../../../i18n/index.js"; -type Entity = MerchantBackend.Products.ProductAddDetail & { product_id: string} +type Entity = MerchantBackend.Products.ProductAddDetail & { + product_id: string; +}; interface Props { onCreate: (d: Entity) => Promise<void>; onBack?: () => void; } - export function CreatePage({ onCreate, onBack }: Props): VNode { + const [submitForm, addFormSubmitter] = useListener<Entity | undefined>( + (result) => { + if (result) return onCreate(result); + return Promise.reject(); + }, + ); - const [submitForm, addFormSubmitter] = useListener<Entity | undefined>((result) => { - if (result) return onCreate(result) - return Promise.reject() - }) - - const i18n = useTranslator() + const i18n = useTranslator(); - return <div> - <section class="section is-main-section"> - <div class="columns"> - <div class="column" /> - <div class="column is-four-fifths"> - <ProductForm onSubscribe={addFormSubmitter} /> + return ( + <div> + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + <ProductForm onSubscribe={addFormSubmitter} /> - <div class="buttons is-right mt-5"> - {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} - <AsyncButton onClick={submitForm} data-tooltip={ - !submitForm ? i18n`Need to complete marked fields` : 'confirm operation' - } disabled={!submitForm}><Translate>Confirm</Translate></AsyncButton> + <div class="buttons is-right mt-5"> + {onBack && ( + <button class="button" onClick={onBack}> + <Translate>Cancel</Translate> + </button> + )} + <AsyncButton + onClick={submitForm} + data-tooltip={ + !submitForm + ? i18n`Need to complete marked fields` + : "confirm operation" + } + disabled={!submitForm} + > + <Translate>Confirm</Translate> + </AsyncButton> + </div> </div> - + <div class="column" /> </div> - <div class="column" /> - </div> - </section> - </div> -}
\ No newline at end of file + </section> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatedSuccessfully.tsx index e9880b73a..6b02430cc 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatedSuccessfully.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatedSuccessfully.tsx @@ -24,44 +24,49 @@ interface Props { onCreateAnother?: () => void; } -export function CreatedSuccessfully({ entity, onConfirm, onCreateAnother }: Props): VNode { - - return <Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label">Image</label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <img src={entity.image} style={{ width: 200, height: 200 }} /> - </p> +export function CreatedSuccessfully({ + entity, + onConfirm, + onCreateAnother, +}: Props): VNode { + return ( + <Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Image</label> </div> - </div> - </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label">Description</label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <textarea class="input" readonly value={entity.description} /> - </p> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <img src={entity.image} style={{ width: 200, height: 200 }} /> + </p> + </div> </div> </div> - </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label">Price</label> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Description</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <textarea class="input" readonly value={entity.description} /> + </p> + </div> + </div> </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input class="input" readonly value={entity.price} /> - </p> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Price</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={entity.price} /> + </p> + </div> </div> </div> - </div> - </Template>; + </Template> + ); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/create/index.tsx index 90b4d01a6..62ecaf512 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/create/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/create/index.tsx @@ -15,41 +15,46 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { Fragment, h, VNode } from 'preact'; -import { useState } from 'preact/hooks'; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; import { NotificationCard } from "../../../../components/menu/index.js"; import { MerchantBackend } from "../../../../declaration.js"; import { useProductAPI } from "../../../../hooks/product.js"; -import { useTranslator } from '../../../../i18n/index.js'; +import { useTranslator } from "../../../../i18n/index.js"; import { Notification } from "../../../../utils/types.js"; import { CreatePage } from "./CreatePage.js"; -export type Entity = MerchantBackend.Products.ProductAddDetail +export type Entity = MerchantBackend.Products.ProductAddDetail; interface Props { onBack?: () => void; onConfirm: () => void; } export default function CreateProduct({ onConfirm, onBack }: Props): VNode { - const { createProduct } = useProductAPI() - const [notif, setNotif] = useState<Notification | undefined>(undefined) - const i18n = useTranslator() - - return <Fragment> - <NotificationCard notification={notif} /> - <CreatePage - onBack={onBack} - onCreate={(request: MerchantBackend.Products.ProductAddDetail) => { - return createProduct(request).then(() => onConfirm()).catch((error) => { - setNotif({ - message: i18n`could not create product`, - type: "ERROR", - description: error.message - }) - }) - }} /> - </Fragment> -}
\ No newline at end of file + const { createProduct } = useProductAPI(); + const [notif, setNotif] = useState<Notification | undefined>(undefined); + const i18n = useTranslator(); + + return ( + <Fragment> + <NotificationCard notification={notif} /> + <CreatePage + onBack={onBack} + onCreate={(request: MerchantBackend.Products.ProductAddDetail) => { + return createProduct(request) + .then(() => onConfirm()) + .catch((error) => { + setNotif({ + message: i18n`could not create product`, + type: "ERROR", + description: error.message, + }); + }); + }} + /> + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/List.stories.tsx index ac22960b2..c2c4d548c 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/List.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/List.stories.tsx @@ -15,44 +15,47 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode, FunctionalComponent } from 'preact'; +import { h, VNode, FunctionalComponent } from "preact"; import { CardTable as TestedComponent } from "./Table.js"; - export default { - title: 'Pages/Product/List', + title: "Pages/Product/List", component: TestedComponent, argTypes: { - onCreate: { action: 'onCreate' }, - onSelect: { action: 'onSelect' }, - onDelete: { action: 'onDelete' }, - onUpdate: { action: 'onUpdate' }, + onCreate: { action: "onCreate" }, + onSelect: { action: "onSelect" }, + onDelete: { action: "onDelete" }, + onUpdate: { action: "onUpdate" }, }, }; -function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { - const r = (args: any) => <Component {...args} /> - r.args = props - return r +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props>, +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; } - export const Example = createExample(TestedComponent, { - instances: [{ - id: 'orderid', - description: 'description1', - description_i18n: {} as any, - image: '', - price: 'TESTKUDOS:10', - taxes: [], - total_lost: 10, - total_sold: 5, - total_stock: 15, - unit: 'bar', - address: {} - }] + instances: [ + { + id: "orderid", + description: "description1", + description_i18n: {} as any, + image: "", + price: "TESTKUDOS:10", + taxes: [], + total_lost: 10, + total_sold: 5, + total_stock: 15, + unit: "bar", + address: {}, + }, + ], }); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx index dfa66fcf4..515b36895 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx @@ -41,7 +41,7 @@ interface Props { onSelect: (product: Entity) => void; onUpdate: ( id: string, - data: MerchantBackend.Products.ProductPatchDetail + data: MerchantBackend.Products.ProductPatchDetail, ) => Promise<void>; onCreate: () => void; selected?: boolean; @@ -55,7 +55,7 @@ export function CardTable({ onDelete, }: Props): VNode { const [rowSelection, rowSelectionHandler] = useState<string | undefined>( - undefined + undefined, ); const i18n = useTranslator(); return ( @@ -107,7 +107,7 @@ interface TableProps { onSelect: (id: Entity) => void; onUpdate: ( id: string, - data: MerchantBackend.Products.ProductPatchDetail + data: MerchantBackend.Products.ProductPatchDetail, ) => Promise<void>; onDelete: (id: Entity) => void; rowSelectionHandler: StateUpdater<string | undefined>; @@ -159,7 +159,7 @@ function Table({ ? "never" : `restock at ${format( new Date(i.next_restock.t_s * 1000), - "yyyy/MM/dd" + "yyyy/MM/dd", )}`; let stockInfo: ComponentChildren = ""; if (i.total_stock < 0) { @@ -277,7 +277,7 @@ function Table({ product={i} onUpdate={(prod) => onUpdate(i.id, prod).then((r) => - rowSelectionHandler(undefined) + rowSelectionHandler(undefined), ) } onCancel={() => rowSelectionHandler(undefined)} @@ -297,7 +297,7 @@ function Table({ interface FastProductUpdateFormProps { product: Entity; onUpdate: ( - data: MerchantBackend.Products.ProductPatchDetail + data: MerchantBackend.Products.ProductPatchDetail, ) => Promise<void>; onCancel: () => void; } @@ -381,7 +381,7 @@ function FastProductWithManagedStockUpdateForm({ }; const hasErrors = Object.keys(errors).some( - (k) => (errors as any)[k] !== undefined + (k) => (errors as any)[k] !== undefined, ); const i18n = useTranslator(); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx index 9f1d077ac..7e9118d24 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx @@ -15,18 +15,21 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode } from 'preact'; -import { useState } from 'preact/hooks'; +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; import { Loading } from "../../../../components/exception/loading.js"; import { NotificationCard } from "../../../../components/menu/index.js"; import { MerchantBackend, WithId } from "../../../../declaration.js"; import { HttpError } from "../../../../hooks/backend.js"; -import { useInstanceProducts, useProductAPI } from "../../../../hooks/product.js"; -import { useTranslator } from '../../../../i18n/index.js'; +import { + useInstanceProducts, + useProductAPI, +} from "../../../../hooks/product.js"; +import { useTranslator } from "../../../../i18n/index.js"; import { Notification } from "../../../../utils/types.js"; import { CardTable } from "./Table.js"; @@ -37,44 +40,65 @@ interface Props { onSelect: (id: string) => void; onLoadError: (e: HttpError) => VNode; } -export default function ProductList({ onUnauthorized, onLoadError, onCreate, onSelect, onNotFound }: Props): VNode { - const result = useInstanceProducts() - const { deleteProduct, updateProduct } = useProductAPI() - const [notif, setNotif] = useState<Notification | undefined>(undefined) +export default function ProductList({ + onUnauthorized, + onLoadError, + onCreate, + onSelect, + onNotFound, +}: Props): VNode { + const result = useInstanceProducts(); + const { deleteProduct, updateProduct } = useProductAPI(); + const [notif, setNotif] = useState<Notification | undefined>(undefined); - const i18n = useTranslator() + const i18n = useTranslator(); - if (result.clientError && result.isUnauthorized) return onUnauthorized() - if (result.clientError && result.isNotfound) return onNotFound() - if (result.loading) return <Loading /> - if (!result.ok) return onLoadError(result) + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); - return <section class="section is-main-section"> - <NotificationCard notification={notif} /> + return ( + <section class="section is-main-section"> + <NotificationCard notification={notif} /> - <CardTable instances={result.data} - onCreate={onCreate} - onUpdate={(id, prod) => updateProduct(id, prod) - .then(() => setNotif({ - message: i18n`product updated successfully`, - type: "SUCCESS" - })).catch((error) => setNotif({ - message: i18n`could not update the product`, - type: "ERROR", - description: error.message - })) - } - onSelect={(product) => onSelect(product.id)} - onDelete={(prod: (MerchantBackend.Products.ProductDetail & WithId)) => deleteProduct(prod.id) - .then(() => setNotif({ - message: i18n`product delete successfully`, - type: "SUCCESS" - })).catch((error) => setNotif({ - message: i18n`could not delete the product`, - type: "ERROR", - description: error.message - })) - } - /> - </section> -}
\ No newline at end of file + <CardTable + instances={result.data} + onCreate={onCreate} + onUpdate={(id, prod) => + updateProduct(id, prod) + .then(() => + setNotif({ + message: i18n`product updated successfully`, + type: "SUCCESS", + }), + ) + .catch((error) => + setNotif({ + message: i18n`could not update the product`, + type: "ERROR", + description: error.message, + }), + ) + } + onSelect={(product) => onSelect(product.id)} + onDelete={(prod: MerchantBackend.Products.ProductDetail & WithId) => + deleteProduct(prod.id) + .then(() => + setNotif({ + message: i18n`product delete successfully`, + type: "SUCCESS", + }), + ) + .catch((error) => + setNotif({ + message: i18n`could not delete the product`, + type: "ERROR", + description: error.message, + }), + ) + } + /> + </section> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/update/Update.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/update/Update.stories.tsx index 95dd1f5cc..a85b13b8b 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/update/Update.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/update/Update.stories.tsx @@ -15,57 +15,59 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode, FunctionalComponent } from 'preact'; +import { h, VNode, FunctionalComponent } from "preact"; import { UpdatePage as TestedComponent } from "./UpdatePage.js"; - export default { - title: 'Pages/Product/Update', + title: "Pages/Product/Update", component: TestedComponent, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { - const r = (args: any) => <Component {...args} /> - r.args = props - return r +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props>, +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; } export const WithManagedStock = createExample(TestedComponent, { product: { - product_id: '20102-ASDAS-QWE', - description: 'description1', + product_id: "20102-ASDAS-QWE", + description: "description1", description_i18n: {} as any, - image: '', - price: 'TESTKUDOS:10', + image: "", + price: "TESTKUDOS:10", taxes: [], total_lost: 10, total_sold: 5, total_stock: 15, - unit: 'bar', - address: {} - } + unit: "bar", + address: {}, + }, }); export const WithInfiniteStock = createExample(TestedComponent, { product: { - product_id: '20102-ASDAS-QWE', - description: 'description1', + product_id: "20102-ASDAS-QWE", + description: "description1", description_i18n: {} as any, - image: '', - price: 'TESTKUDOS:10', + image: "", + price: "TESTKUDOS:10", taxes: [], total_lost: 10, total_sold: 5, total_stock: -1, - unit: 'bar', - address: {} - } + unit: "bar", + address: {}, + }, }); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx index 54fef6003..841c0222f 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx @@ -15,9 +15,9 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { AsyncButton } from "../../../../components/exception/AsyncButton.js"; @@ -26,7 +26,7 @@ import { MerchantBackend, WithId } from "../../../../declaration.js"; import { useListener } from "../../../../hooks/listener.js"; import { Translate, useTranslator } from "../../../../i18n/index.js"; -type Entity = MerchantBackend.Products.ProductDetail & { product_id: string } +type Entity = MerchantBackend.Products.ProductDetail & { product_id: string }; interface Props { onUpdate: (d: Entity) => Promise<void>; @@ -35,43 +35,65 @@ interface Props { } export function UpdatePage({ product, onUpdate, onBack }: Props): VNode { - const [submitForm, addFormSubmitter] = useListener<Entity | undefined>((result) => { - if (result) return onUpdate(result) - return Promise.resolve() - }) - - const i18n = useTranslator() + const [submitForm, addFormSubmitter] = useListener<Entity | undefined>( + (result) => { + if (result) return onUpdate(result); + return Promise.resolve(); + }, + ); - return <div> - <section class="section"> - <section class="hero is-hero-bar"> - <div class="hero-body"> + const i18n = useTranslator(); - <div class="level"> - <div class="level-left"> - <div class="level-item"> - <span class="is-size-4"><Translate>Product id:</Translate><b>{product.product_id}</b></span> + return ( + <div> + <section class="section"> + <section class="hero is-hero-bar"> + <div class="hero-body"> + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <span class="is-size-4"> + <Translate>Product id:</Translate> + <b>{product.product_id}</b> + </span> + </div> </div> </div> </div> - </div> - </section> - <hr /> + </section> + <hr /> - <div class="columns"> - <div class="column" /> - <div class="column is-four-fifths"> - <ProductForm initial={product} onSubscribe={addFormSubmitter} alreadyExist /> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + <ProductForm + initial={product} + onSubscribe={addFormSubmitter} + alreadyExist + /> - <div class="buttons is-right mt-5"> - {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} - <AsyncButton onClick={submitForm} data-tooltip={ - !submitForm ? i18n`Need to complete marked fields` : 'confirm operation' - } disabled={!submitForm}><Translate>Confirm</Translate></AsyncButton> + <div class="buttons is-right mt-5"> + {onBack && ( + <button class="button" onClick={onBack}> + <Translate>Cancel</Translate> + </button> + )} + <AsyncButton + onClick={submitForm} + data-tooltip={ + !submitForm + ? i18n`Need to complete marked fields` + : "confirm operation" + } + disabled={!submitForm} + > + <Translate>Confirm</Translate> + </AsyncButton> + </div> </div> + <div class="column" /> </div> - <div class="column" /> - </div> - </section> - </div> -}
\ No newline at end of file + </section> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx index c32424348..3988fc9f0 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx @@ -15,22 +15,22 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { Fragment, h, VNode } from 'preact'; -import { useState } from 'preact/hooks'; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; import { Loading } from "../../../../components/exception/loading.js"; import { NotificationCard } from "../../../../components/menu/index.js"; import { MerchantBackend } from "../../../../declaration.js"; import { HttpError } from "../../../../hooks/backend.js"; import { useProductAPI, useProductDetails } from "../../../../hooks/product.js"; -import { useTranslator } from '../../../../i18n/index.js'; +import { useTranslator } from "../../../../i18n/index.js"; import { Notification } from "../../../../utils/types.js"; import { UpdatePage } from "./UpdatePage.js"; -export type Entity = MerchantBackend.Products.ProductAddDetail +export type Entity = MerchantBackend.Products.ProductAddDetail; interface Props { onBack?: () => void; onConfirm: () => void; @@ -39,33 +39,43 @@ interface Props { onLoadError: (e: HttpError) => VNode; pid: string; } -export default function UpdateProduct({ pid, onConfirm, onBack, onUnauthorized, onNotFound, onLoadError }: Props): VNode { - const { updateProduct } = useProductAPI() - const result = useProductDetails(pid) - const [notif, setNotif] = useState<Notification | undefined>(undefined) +export default function UpdateProduct({ + pid, + onConfirm, + onBack, + onUnauthorized, + onNotFound, + onLoadError, +}: Props): VNode { + const { updateProduct } = useProductAPI(); + const result = useProductDetails(pid); + const [notif, setNotif] = useState<Notification | undefined>(undefined); - const i18n = useTranslator() + const i18n = useTranslator(); - if (result.clientError && result.isUnauthorized) return onUnauthorized() - if (result.clientError && result.isNotfound) return onNotFound() - if (result.loading) return <Loading /> - if (!result.ok) return onLoadError(result) + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); - return <Fragment> - <NotificationCard notification={notif} /> - <UpdatePage - product={{ ...result.data, product_id: pid }} - onBack={onBack} - onUpdate={(data) => { - return updateProduct(pid, data) - .then(onConfirm) - .catch((error) => { - setNotif({ - message: i18n`could not create product`, - type: "ERROR", - description: error.message - }) - }) - }} /> - </Fragment> -}
\ No newline at end of file + return ( + <Fragment> + <NotificationCard notification={notif} /> + <UpdatePage + product={{ ...result.data, product_id: pid }} + onBack={onBack} + onUpdate={(data) => { + return updateProduct(pid, data) + .then(onConfirm) + .catch((error) => { + setNotif({ + message: i18n`could not create product`, + type: "ERROR", + description: error.message, + }); + }); + }} + /> + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/Create.stories.tsx index 2f7f25b09..5542c028a 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/Create.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/Create.stories.tsx @@ -15,28 +15,29 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode, FunctionalComponent } from 'preact'; +import { h, VNode, FunctionalComponent } from "preact"; import { CreatePage as TestedComponent } from "./CreatePage.js"; - export default { - title: 'Pages/Reserve/Create', + title: "Pages/Reserve/Create", component: TestedComponent, argTypes: { - onCreate: { action: 'onCreate' }, - onBack: { action: 'onBack' }, + onCreate: { action: "onCreate" }, + onBack: { action: "onBack" }, }, }; -function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { - const r = (args: any) => <Component {...args} /> - r.args = props - return r +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props>, +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; } -export const Example = createExample(TestedComponent, { -}); +export const Example = createExample(TestedComponent, {}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx index 4910f9345..2c3e963b8 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx @@ -15,154 +15,252 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { Fragment, h, VNode } from "preact"; import { StateUpdater, useEffect, useState } from "preact/hooks"; -import { FormErrors, FormProvider } from "../../../../components/form/FormProvider.js"; +import { + FormErrors, + FormProvider, +} from "../../../../components/form/FormProvider.js"; import { Input } from "../../../../components/form/Input.js"; import { InputCurrency } from "../../../../components/form/InputCurrency.js"; import { ExchangeBackend, MerchantBackend } from "../../../../declaration.js"; import { Translate, useTranslator } from "../../../../i18n/index.js"; import { AsyncButton } from "../../../../components/exception/AsyncButton.js"; -import { canonicalizeBaseUrl, ExchangeKeysJson } from "@gnu-taler/taler-util" -import { PAYTO_WIRE_METHOD_LOOKUP, URL_REGEX } from "../../../../utils/constants.js"; +import { canonicalizeBaseUrl, ExchangeKeysJson } from "@gnu-taler/taler-util"; +import { + PAYTO_WIRE_METHOD_LOOKUP, + URL_REGEX, +} from "../../../../utils/constants.js"; import { request } from "../../../../hooks/backend.js"; import { InputSelector } from "../../../../components/form/InputSelector.js"; -type Entity = MerchantBackend.Tips.ReserveCreateRequest +type Entity = MerchantBackend.Tips.ReserveCreateRequest; interface Props { onCreate: (d: Entity) => Promise<void>; onBack?: () => void; } - enum Steps { EXCHANGE, WIRE_METHOD, } interface ViewProps { - step: Steps, + step: Steps; setCurrentStep: (s: Steps) => void; reserve: Partial<Entity>; onBack?: () => void; submitForm: () => Promise<void>; setReserve: StateUpdater<Partial<Entity>>; } -function ViewStep({ step, setCurrentStep, reserve, onBack, submitForm, setReserve }: ViewProps): VNode { - const i18n = useTranslator() - const [wireMethods, setWireMethods] = useState<Array<string>>([]) - const [exchangeQueryError, setExchangeQueryError] = useState<string | undefined>(undefined) +function ViewStep({ + step, + setCurrentStep, + reserve, + onBack, + submitForm, + setReserve, +}: ViewProps): VNode { + const i18n = useTranslator(); + const [wireMethods, setWireMethods] = useState<Array<string>>([]); + const [exchangeQueryError, setExchangeQueryError] = useState< + string | undefined + >(undefined); useEffect(() => { - setExchangeQueryError(undefined) - }, [reserve.exchange_url]) + setExchangeQueryError(undefined); + }, [reserve.exchange_url]); switch (step) { case Steps.EXCHANGE: { const errors: FormErrors<Entity> = { - initial_balance: !reserve.initial_balance ? 'cannot be empty' : !(parseInt(reserve.initial_balance.split(':')[1], 10) > 0) ? i18n`it should be greater than 0` : undefined, - exchange_url: !reserve.exchange_url ? i18n`cannot be empty` : !URL_REGEX.test(reserve.exchange_url) ? i18n`must be a valid URL` : !exchangeQueryError ? undefined : exchangeQueryError, - } - - const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) - - return <Fragment> - <FormProvider<Entity> object={reserve} errors={errors} valueHandler={setReserve}> - <InputCurrency<Entity> name="initial_balance" label={i18n`Initial balance`} tooltip={i18n`balance prior to deposit`} /> - <Input<Entity> name="exchange_url" label={i18n`Exchange URL`} tooltip={i18n`URL of exchange`} /> - </FormProvider> - - <div class="buttons is-right mt-5"> - {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} - <AsyncButton class="has-tooltip-left" onClick={() => { - return request<ExchangeBackend.WireResponse>(`${reserve.exchange_url}wire`).then(r => { - const wireMethods = r.data.accounts.map(a => { - const match = PAYTO_WIRE_METHOD_LOOKUP.exec(a.payto_uri) - return match && match[1] || '' - }) - setWireMethods(wireMethods) - setCurrentStep(Steps.WIRE_METHOD) - return - }).catch((r: any) => { - setExchangeQueryError(r.message) - }) - }} data-tooltip={ - hasErrors ? i18n`Need to complete marked fields` : 'confirm operation' - } disabled={hasErrors} ><Translate>Next</Translate></AsyncButton> - </div> - </Fragment> + initial_balance: !reserve.initial_balance + ? "cannot be empty" + : !(parseInt(reserve.initial_balance.split(":")[1], 10) > 0) + ? i18n`it should be greater than 0` + : undefined, + exchange_url: !reserve.exchange_url + ? i18n`cannot be empty` + : !URL_REGEX.test(reserve.exchange_url) + ? i18n`must be a valid URL` + : !exchangeQueryError + ? undefined + : exchangeQueryError, + }; + + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined, + ); + + return ( + <Fragment> + <FormProvider<Entity> + object={reserve} + errors={errors} + valueHandler={setReserve} + > + <InputCurrency<Entity> + name="initial_balance" + label={i18n`Initial balance`} + tooltip={i18n`balance prior to deposit`} + /> + <Input<Entity> + name="exchange_url" + label={i18n`Exchange URL`} + tooltip={i18n`URL of exchange`} + /> + </FormProvider> + + <div class="buttons is-right mt-5"> + {onBack && ( + <button class="button" onClick={onBack}> + <Translate>Cancel</Translate> + </button> + )} + <AsyncButton + class="has-tooltip-left" + onClick={() => { + return request<ExchangeBackend.WireResponse>( + `${reserve.exchange_url}wire`, + ) + .then((r) => { + const wireMethods = r.data.accounts.map((a) => { + const match = PAYTO_WIRE_METHOD_LOOKUP.exec(a.payto_uri); + return (match && match[1]) || ""; + }); + setWireMethods(wireMethods); + setCurrentStep(Steps.WIRE_METHOD); + return; + }) + .catch((r: any) => { + setExchangeQueryError(r.message); + }); + }} + data-tooltip={ + hasErrors + ? i18n`Need to complete marked fields` + : "confirm operation" + } + disabled={hasErrors} + > + <Translate>Next</Translate> + </AsyncButton> + </div> + </Fragment> + ); } case Steps.WIRE_METHOD: { const errors: FormErrors<Entity> = { wire_method: !reserve.wire_method ? i18n`cannot be empty` : undefined, - } - - const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) - return <Fragment> - <FormProvider<Entity> object={reserve} errors={errors} valueHandler={setReserve}> - <InputCurrency<Entity> name="initial_balance" label={i18n`Initial balance`} tooltip={i18n`balance prior to deposit`} readonly /> - <Input<Entity> name="exchange_url" label={i18n`Exchange URL`} tooltip={i18n`URL of exchange`} readonly /> - <InputSelector<Entity> name="wire_method" label={i18n`Wire method`} tooltip={i18n`method to use for wire transfer`} values={wireMethods} placeholder={i18n`Select one wire method`} /> - </FormProvider> - <div class="buttons is-right mt-5"> - {onBack && <button class="button" onClick={() => setCurrentStep(Steps.EXCHANGE)} ><Translate>Back</Translate></button>} - <AsyncButton onClick={submitForm} data-tooltip={ - hasErrors ? i18n`Need to complete marked fields` : 'confirm operation' - } disabled={hasErrors} ><Translate>Confirm</Translate></AsyncButton> - </div> - </Fragment> + }; + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined, + ); + return ( + <Fragment> + <FormProvider<Entity> + object={reserve} + errors={errors} + valueHandler={setReserve} + > + <InputCurrency<Entity> + name="initial_balance" + label={i18n`Initial balance`} + tooltip={i18n`balance prior to deposit`} + readonly + /> + <Input<Entity> + name="exchange_url" + label={i18n`Exchange URL`} + tooltip={i18n`URL of exchange`} + readonly + /> + <InputSelector<Entity> + name="wire_method" + label={i18n`Wire method`} + tooltip={i18n`method to use for wire transfer`} + values={wireMethods} + placeholder={i18n`Select one wire method`} + /> + </FormProvider> + <div class="buttons is-right mt-5"> + {onBack && ( + <button + class="button" + onClick={() => setCurrentStep(Steps.EXCHANGE)} + > + <Translate>Back</Translate> + </button> + )} + <AsyncButton + onClick={submitForm} + data-tooltip={ + hasErrors + ? i18n`Need to complete marked fields` + : "confirm operation" + } + disabled={hasErrors} + > + <Translate>Confirm</Translate> + </AsyncButton> + </div> + </Fragment> + ); } } } export function CreatePage({ onCreate, onBack }: Props): VNode { - const [reserve, setReserve] = useState<Partial<Entity>>({}) - + const [reserve, setReserve] = useState<Partial<Entity>>({}); const submitForm = () => { - return onCreate(reserve as Entity) - } + return onCreate(reserve as Entity); + }; - const [currentStep, setCurrentStep] = useState(Steps.EXCHANGE) - - - return <div> - <section class="section is-main-section"> - <div class="columns"> - <div class="column" /> - <div class="column is-four-fifths"> - - <div class="tabs is-toggle is-fullwidth is-small"> - <ul> - <li class={currentStep === Steps.EXCHANGE ? "is-active" : ""}> - <a style={{ cursor: 'initial' }}> - <span>Step 1: Specify exchange</span> - </a> - </li> - <li class={currentStep === Steps.WIRE_METHOD ? "is-active" : ""}> - <a style={{ cursor: 'initial' }}> - <span>Step 2: Select wire method</span> - </a> - </li> - </ul> - </div> + const [currentStep, setCurrentStep] = useState(Steps.EXCHANGE); + + return ( + <div> + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + <div class="tabs is-toggle is-fullwidth is-small"> + <ul> + <li class={currentStep === Steps.EXCHANGE ? "is-active" : ""}> + <a style={{ cursor: "initial" }}> + <span>Step 1: Specify exchange</span> + </a> + </li> + <li + class={currentStep === Steps.WIRE_METHOD ? "is-active" : ""} + > + <a style={{ cursor: "initial" }}> + <span>Step 2: Select wire method</span> + </a> + </li> + </ul> + </div> - <ViewStep step={currentStep} reserve={reserve} - setCurrentStep={setCurrentStep} - setReserve={setReserve} - submitForm={submitForm} - onBack={onBack} - /> + <ViewStep + step={currentStep} + reserve={reserve} + setCurrentStep={setCurrentStep} + setReserve={setReserve} + submitForm={submitForm} + onBack={onBack} + /> + </div> + <div class="column" /> </div> - <div class="column" /> - </div> - </section> - </div> + </section> + </div> + ); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.stories.tsx index 453147cdf..1d848a033 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.stories.tsx @@ -15,39 +15,41 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode, FunctionalComponent } from 'preact'; +import { h, VNode, FunctionalComponent } from "preact"; import { CreatedSuccessfully as TestedComponent } from "./CreatedSuccessfully.js"; - export default { - title: 'Pages/Reserve/CreatedSuccessfully', + title: "Pages/Reserve/CreatedSuccessfully", component: TestedComponent, argTypes: { - onCreate: { action: 'onCreate' }, - onBack: { action: 'onBack' }, + onCreate: { action: "onCreate" }, + onBack: { action: "onBack" }, }, }; -function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { - const r = (args: any) => <Component {...args} /> - r.args = props - return r +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props>, +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; } export const Example = createExample(TestedComponent, { entity: { request: { - exchange_url: 'http://exchange.taler/', - initial_balance: 'TESTKUDOS:1', - wire_method: 'x-taler-bank', + exchange_url: "http://exchange.taler/", + initial_balance: "TESTKUDOS:1", + wire_method: "x-taler-bank", }, response: { - payto_uri: 'payto://x-taler-bank/bank.taler:8080/exchange_account', - reserve_pub: 'WEQWDASDQWEASDADASDQWEQWEASDAS' - } - } + payto_uri: "payto://x-taler-bank/bank.taler:8080/exchange_account", + reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS", + }, + }, }); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx index 3da8beff8..9bb228e1f 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx @@ -20,7 +20,10 @@ import { MerchantBackend } from "../../../../declaration.js"; import { Translate } from "../../../../i18n/index.js"; import { QR } from "../../../../components/exception/QR.js"; -type Entity = { request: MerchantBackend.Tips.ReserveCreateRequest, response: MerchantBackend.Tips.ReserveCreateConfirmation }; +type Entity = { + request: MerchantBackend.Tips.ReserveCreateRequest; + response: MerchantBackend.Tips.ReserveCreateConfirmation; +}; interface Props { entity: Entity; @@ -28,52 +31,77 @@ interface Props { onCreateAnother?: () => void; } -export function CreatedSuccessfully({ entity, onConfirm, onCreateAnother }: Props): VNode { - const link = `${entity.response.payto_uri}?message=${entity.response.reserve_pub}&amount=${entity.request.initial_balance}` +export function CreatedSuccessfully({ + entity, + onConfirm, + onCreateAnother, +}: Props): VNode { + const link = `${entity.response.payto_uri}?message=${entity.response.reserve_pub}&amount=${entity.request.initial_balance}`; - return <Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label">Amount</label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input readonly class="input" value={entity.request.initial_balance} /> - </p> + return ( + <Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Amount</label> </div> - </div> - </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label">Exchange bank account</label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input readonly class="input" value={entity.response.payto_uri} /> - </p> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input + readonly + class="input" + value={entity.request.initial_balance} + /> + </p> + </div> </div> </div> - </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label">Wire transfer subject</label> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Exchange bank account</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input readonly class="input" value={entity.response.payto_uri} /> + </p> + </div> + </div> </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input class="input" readonly value={entity.response.reserve_pub} /> - </p> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Wire transfer subject</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input + class="input" + readonly + value={entity.response.reserve_pub} + /> + </p> + </div> </div> </div> - </div> - <p class="is-size-5"><Translate>To complete the setup of the reserve, you must now initiate a wire transfer using the given wire transfer subject and crediting the specified amount to the indicated account of the exchange.</Translate></p> - <p class="is-size-5"><Translate>If your system supports RFC 8905, you can do this by opening this URI:</Translate></p> - <pre> - <a target="_blank" rel="noreferrer" href={link}>{link}</a> - </pre> - <QR text={link} /> - </Template>; + <p class="is-size-5"> + <Translate> + To complete the setup of the reserve, you must now initiate a wire + transfer using the given wire transfer subject and crediting the + specified amount to the indicated account of the exchange. + </Translate> + </p> + <p class="is-size-5"> + <Translate> + If your system supports RFC 8905, you can do this by opening this URI: + </Translate> + </p> + <pre> + <a target="_blank" rel="noreferrer" href={link}> + {link} + </a> + </pre> + <QR text={link} /> + </Template> + ); } - diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx index 689cdaaf5..b0b291859 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx @@ -29,7 +29,10 @@ import { Input } from "../../../../components/form/Input.js"; import { InputCurrency } from "../../../../components/form/InputCurrency.js"; import { InputDate } from "../../../../components/form/InputDate.js"; import { TextField } from "../../../../components/form/TextField.js"; -import { ContinueModal, SimpleModal } from "../../../../components/modal/index.js"; +import { + ContinueModal, + SimpleModal, +} from "../../../../components/modal/index.js"; import { MerchantBackend } from "../../../../declaration.js"; import { useTipDetails } from "../../../../hooks/reserves.js"; import { Translate, useTranslator } from "../../../../i18n/index.js"; @@ -47,7 +50,7 @@ interface Props { export function DetailPage({ id, selected, onBack }: Props): VNode { const i18n = useTranslator(); const didExchangeAckTransfer = Amounts.isNonZero( - Amounts.parseOrThrow(selected.exchange_initial_amount) + Amounts.parseOrThrow(selected.exchange_initial_amount), ); const link = `${selected.payto_uri}?message=${id}&amount=${selected.merchant_initial_amount}`; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx index fbf3e4fa4..cd1318922 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx @@ -33,7 +33,7 @@ export default { function createExample<Props>( Component: FunctionalComponent<Props>, - props: Partial<Props> + props: Partial<Props>, ) { const r = (args: any) => <Component {...args} />; r.args = props; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx index d31310cc9..360d39aba 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx @@ -74,7 +74,7 @@ export function TipInfo({ id, amount, entity }: Props): VNode { ? "never" : format( entity.expiration.t_s * 1000, - "yyyy/MM/dd HH:mm:ss" + "yyyy/MM/dd HH:mm:ss", ) } /> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx index 24bd011e2..5200abedf 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx @@ -15,21 +15,27 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; -import { FormErrors, FormProvider } from "../../../../components/form/FormProvider.js"; +import { + FormErrors, + FormProvider, +} from "../../../../components/form/FormProvider.js"; import { Input } from "../../../../components/form/Input.js"; import { InputCurrency } from "../../../../components/form/InputCurrency.js"; -import { ConfirmModal, ContinueModal } from "../../../../components/modal/index.js"; +import { + ConfirmModal, + ContinueModal, +} from "../../../../components/modal/index.js"; import { MerchantBackend } from "../../../../declaration.js"; import { useTranslator } from "../../../../i18n/index.js"; import { AuthorizeTipSchema } from "../../../../schemas/index.js"; import { CreatedSuccessfully } from "./CreatedSuccessfully.js"; -import * as yup from 'yup'; +import * as yup from "yup"; interface AuthorizeTipModalProps { onCancel: () => void; @@ -40,46 +46,79 @@ interface AuthorizeTipModalProps { }; } -export function AuthorizeTipModal({ onCancel, onConfirm, tipAuthorized }: AuthorizeTipModalProps): VNode { +export function AuthorizeTipModal({ + onCancel, + onConfirm, + tipAuthorized, +}: AuthorizeTipModalProps): VNode { // const result = useOrderDetails(id) - type State = MerchantBackend.Tips.TipCreateRequest - const [form, setValue] = useState<Partial<State>>({}) + type State = MerchantBackend.Tips.TipCreateRequest; + const [form, setValue] = useState<Partial<State>>({}); const i18n = useTranslator(); // const [errors, setErrors] = useState<FormErrors<State>>({}) - let errors: FormErrors<State> = {} + let errors: FormErrors<State> = {}; try { - AuthorizeTipSchema.validateSync(form, { abortEarly: false }) + AuthorizeTipSchema.validateSync(form, { abortEarly: false }); } catch (err) { if (err instanceof yup.ValidationError) { - const yupErrors = err.inner as any[] - errors = yupErrors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, [cur.path]: cur.message }), {}) + const yupErrors = err.inner as any[]; + errors = yupErrors.reduce( + (prev, cur) => + !cur.path ? prev : { ...prev, [cur.path]: cur.message }, + {}, + ); } } - const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined, + ); const validateAndConfirm = () => { - onConfirm(form as State) - } + onConfirm(form as State); + }; if (tipAuthorized) { - return <ContinueModal description="tip" active onConfirm={onCancel}> - <CreatedSuccessfully - entity={tipAuthorized.response} - request={tipAuthorized.request} - onConfirm={onCancel} - /> - </ContinueModal> + return ( + <ContinueModal description="tip" active onConfirm={onCancel}> + <CreatedSuccessfully + entity={tipAuthorized.response} + request={tipAuthorized.request} + onConfirm={onCancel} + /> + </ContinueModal> + ); } - return <ConfirmModal description="tip" active onCancel={onCancel} disabled={hasErrors} onConfirm={validateAndConfirm}> - - <FormProvider<State> errors={errors} object={form} valueHandler={setValue} > - <InputCurrency<State> name="amount" label={i18n`Amount`} tooltip={i18n`amount of tip`} /> - <Input<State> name="justification" label={i18n`Justification`} inputType="multiline" tooltip={i18n`reason for the tip`} /> - <Input<State> name="next_url" label={i18n`URL after tip`} tooltip={i18n`URL to visit after tip payment`} /> - </FormProvider> - - </ConfirmModal> + return ( + <ConfirmModal + description="tip" + active + onCancel={onCancel} + disabled={hasErrors} + onConfirm={validateAndConfirm} + > + <FormProvider<State> + errors={errors} + object={form} + valueHandler={setValue} + > + <InputCurrency<State> + name="amount" + label={i18n`Amount`} + tooltip={i18n`amount of tip`} + /> + <Input<State> + name="justification" + label={i18n`Justification`} + inputType="multiline" + tooltip={i18n`reason for the tip`} + /> + <Input<State> + name="next_url" + label={i18n`URL after tip`} + tooltip={i18n`URL to visit after tip payment`} + /> + </FormProvider> + </ConfirmModal> + ); } - - diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx index 62f6ac538..643651b52 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx @@ -87,7 +87,7 @@ export function CreatedSuccessfully({ ? "never" : format( entity.tip_expiration.t_s * 1000, - "yyyy/MM/dd HH:mm:ss" + "yyyy/MM/dd HH:mm:ss", ) } /> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx index db4f3c51e..fe305f4fd 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx @@ -35,7 +35,7 @@ export default { function createExample<Props>( Component: FunctionalComponent<Props>, - props: Partial<Props> + props: Partial<Props>, ) { const r = (args: any) => <Component {...args} />; r.args = props; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx index f9efad91e..86b79d1dd 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx @@ -171,7 +171,7 @@ function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode { ? "never" : format( i.expiration_time.t_s * 1000, - "yyyy/MM/dd HH:mm:ss" + "yyyy/MM/dd HH:mm:ss", )} </td> <td @@ -282,7 +282,7 @@ function TableWithoutFund({ ? "never" : format( i.expiration_time.t_s * 1000, - "yyyy/MM/dd HH:mm:ss" + "yyyy/MM/dd HH:mm:ss", )} </td> <td diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx index 680589eed..182b3f72c 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx @@ -59,7 +59,7 @@ export default function ListTips({ const [notif, setNotif] = useState<Notification | undefined>(undefined); const i18n = useTranslator(); const [reserveForTip, setReserveForTip] = useState<string | undefined>( - undefined + undefined, ); const [tipAuthorized, setTipAuthorized] = useState< TipConfirmation | undefined @@ -85,7 +85,7 @@ export default function ListTips({ try { const response = await authorizeTipReserve( reserveForTip, - request + request, ); setTipAuthorized({ request, diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/Create.stories.tsx index d4fbaa901..64b67335c 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/Create.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/Create.stories.tsx @@ -15,29 +15,31 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode, FunctionalComponent } from 'preact'; +import { h, VNode, FunctionalComponent } from "preact"; import { CreatePage as TestedComponent } from "./CreatePage.js"; - export default { - title: 'Pages/Transfer/Create', + title: "Pages/Transfer/Create", component: TestedComponent, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { - const r = (args: any) => <Component {...args} /> - r.args = props - return r +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props>, +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; } export const Example = createExample(TestedComponent, { - accounts: ['payto://x-taler-bank/account1','payto://x-taler-bank/account2'] + accounts: ["payto://x-taler-bank/account1", "payto://x-taler-bank/account2"], }); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx index 4b4a079d3..5b041df7c 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx @@ -15,90 +15,132 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { AsyncButton } from "../../../../components/exception/AsyncButton.js"; -import { FormErrors, FormProvider } from "../../../../components/form/FormProvider.js"; +import { + FormErrors, + FormProvider, +} from "../../../../components/form/FormProvider.js"; import { Input } from "../../../../components/form/Input.js"; import { InputCurrency } from "../../../../components/form/InputCurrency.js"; import { InputSelector } from "../../../../components/form/InputSelector.js"; import { useConfigContext } from "../../../../context/config.js"; import { MerchantBackend } from "../../../../declaration.js"; import { Translate, useTranslator } from "../../../../i18n/index.js"; -import { CROCKFORD_BASE32_REGEX, URL_REGEX } from "../../../../utils/constants.js"; +import { + CROCKFORD_BASE32_REGEX, + URL_REGEX, +} from "../../../../utils/constants.js"; -type Entity = MerchantBackend.Transfers.TransferInformation +type Entity = MerchantBackend.Transfers.TransferInformation; interface Props { onCreate: (d: Entity) => Promise<void>; onBack?: () => void; - accounts: string[], + accounts: string[]; } export function CreatePage({ accounts, onCreate, onBack }: Props): VNode { - const i18n = useTranslator() - const { currency } = useConfigContext() + const i18n = useTranslator(); + const { currency } = useConfigContext(); const [state, setState] = useState<Partial<Entity>>({ - wtid: '', + wtid: "", // payto_uri: , // exchange_url: 'http://exchange.taler:8081/', credit_amount: ``, }); const errors: FormErrors<Entity> = { - wtid: !state.wtid ? i18n`cannot be empty` : - (!CROCKFORD_BASE32_REGEX.test(state.wtid) ? i18n`check the id, does not look valid` : - (state.wtid.length !== 52 ? i18n`should have 52 characters, current ${state.wtid.length}` : - undefined)), + wtid: !state.wtid + ? i18n`cannot be empty` + : !CROCKFORD_BASE32_REGEX.test(state.wtid) + ? i18n`check the id, does not look valid` + : state.wtid.length !== 52 + ? i18n`should have 52 characters, current ${state.wtid.length}` + : undefined, payto_uri: !state.payto_uri ? i18n`cannot be empty` : undefined, credit_amount: !state.credit_amount ? i18n`cannot be empty` : undefined, - exchange_url: !state.exchange_url ? i18n`cannot be empty` : - (!URL_REGEX.test(state.exchange_url) ? i18n`URL doesn't have the right format` : undefined), - } + exchange_url: !state.exchange_url + ? i18n`cannot be empty` + : !URL_REGEX.test(state.exchange_url) + ? i18n`URL doesn't have the right format` + : undefined, + }; - const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined, + ); const submitForm = () => { - if (hasErrors) return Promise.reject() - return onCreate(state as any) - } - - return <div> - <section class="section is-main-section"> - <div class="columns"> - <div class="column" /> - <div class="column is-four-fifths"> - - <FormProvider object={state} valueHandler={setState} errors={errors}> - <InputSelector name="payto_uri" label={i18n`Credited bank account`} - values={accounts} - placeholder={i18n`Select one account`} - tooltip={i18n`Bank account of the merchant where the payment was received`} - /> - <Input<Entity> name="wtid" label={i18n`Wire transfer ID`} help="" tooltip={i18n`unique identifier of the wire transfer used by the exchange, must be 52 characters long`} /> - <Input<Entity> name="exchange_url" - label={i18n`Exchange URL`} - tooltip={i18n`Base URL of the exchange that made the transfer, should have been in the wire transfer subject`} - help="http://exchange.taler:8081/" /> - <InputCurrency<Entity> name="credit_amount" label={i18n`Amount credited`} tooltip={i18n`Actual amount that was wired to the merchant's bank account`} /> - - </FormProvider> - - <div class="buttons is-right mt-5"> - {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} - <AsyncButton disabled={hasErrors} data-tooltip={ - hasErrors ? i18n`Need to complete marked fields` : 'confirm operation' - } onClick={submitForm} ><Translate>Confirm</Translate></AsyncButton> + if (hasErrors) return Promise.reject(); + return onCreate(state as any); + }; + + return ( + <div> + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + <FormProvider + object={state} + valueHandler={setState} + errors={errors} + > + <InputSelector + name="payto_uri" + label={i18n`Credited bank account`} + values={accounts} + placeholder={i18n`Select one account`} + tooltip={i18n`Bank account of the merchant where the payment was received`} + /> + <Input<Entity> + name="wtid" + label={i18n`Wire transfer ID`} + help="" + tooltip={i18n`unique identifier of the wire transfer used by the exchange, must be 52 characters long`} + /> + <Input<Entity> + name="exchange_url" + label={i18n`Exchange URL`} + tooltip={i18n`Base URL of the exchange that made the transfer, should have been in the wire transfer subject`} + help="http://exchange.taler:8081/" + /> + <InputCurrency<Entity> + name="credit_amount" + label={i18n`Amount credited`} + tooltip={i18n`Actual amount that was wired to the merchant's bank account`} + /> + </FormProvider> + + <div class="buttons is-right mt-5"> + {onBack && ( + <button class="button" onClick={onBack}> + <Translate>Cancel</Translate> + </button> + )} + <AsyncButton + disabled={hasErrors} + data-tooltip={ + hasErrors + ? i18n`Need to complete marked fields` + : "confirm operation" + } + onClick={submitForm} + > + <Translate>Confirm</Translate> + </AsyncButton> + </div> </div> - + <div class="column" /> </div> - <div class="column" /> - </div> - </section> - </div> + </section> + </div> + ); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx index eb48aaf83..db01a57b6 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx @@ -15,46 +15,53 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { Fragment, h, VNode } from 'preact'; -import { useState } from 'preact/hooks'; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; import { NotificationCard } from "../../../../components/menu/index.js"; import { MerchantBackend } from "../../../../declaration.js"; import { useInstanceDetails } from "../../../../hooks/instance.js"; import { useTransferAPI } from "../../../../hooks/transfer.js"; -import { useTranslator } from '../../../../i18n/index.js'; +import { useTranslator } from "../../../../i18n/index.js"; import { Notification } from "../../../../utils/types.js"; import { CreatePage } from "./CreatePage.js"; -export type Entity = MerchantBackend.Transfers.TransferInformation +export type Entity = MerchantBackend.Transfers.TransferInformation; interface Props { onBack?: () => void; onConfirm: () => void; } -export default function CreateTransfer({onConfirm, onBack}:Props): VNode { - const { informTransfer } = useTransferAPI() - const [notif, setNotif] = useState<Notification | undefined>(undefined) - const i18n = useTranslator() - const instance = useInstanceDetails() - const accounts = !instance.ok ? [] : instance.data.accounts.map(a => a.payto_uri) - - return <> - <NotificationCard notification={notif} /> - <CreatePage - onBack={onBack} - accounts={accounts} - onCreate={(request: MerchantBackend.Transfers.TransferInformation) => { - return informTransfer(request).then(() => onConfirm()).catch((error) => { - setNotif({ - message: i18n`could not inform transfer`, - type: "ERROR", - description: error.message - }) - }) - }} /> - </> -}
\ No newline at end of file +export default function CreateTransfer({ onConfirm, onBack }: Props): VNode { + const { informTransfer } = useTransferAPI(); + const [notif, setNotif] = useState<Notification | undefined>(undefined); + const i18n = useTranslator(); + const instance = useInstanceDetails(); + const accounts = !instance.ok + ? [] + : instance.data.accounts.map((a) => a.payto_uri); + + return ( + <> + <NotificationCard notification={notif} /> + <CreatePage + onBack={onBack} + accounts={accounts} + onCreate={(request: MerchantBackend.Transfers.TransferInformation) => { + return informTransfer(request) + .then(() => onConfirm()) + .catch((error) => { + setNotif({ + message: i18n`could not inform transfer`, + type: "ERROR", + description: error.message, + }); + }); + }} + /> + </> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/List.stories.tsx index 42fcdd733..92b3f9853 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/List.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/List.stories.tsx @@ -39,7 +39,7 @@ export default { function createExample<Props>( Component: FunctionalComponent<Props>, - props: Partial<Props> + props: Partial<Props>, ) { const r = (args: any) => <Component {...args} />; r.args = props; 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 539f1ae34..cad989980 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 @@ -15,15 +15,15 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode } from 'preact'; +import { h, VNode } from "preact"; import { FormProvider } from "../../../../components/form/FormProvider.js"; import { InputSelector } from "../../../../components/form/InputSelector.js"; import { MerchantBackend } from "../../../../declaration.js"; -import { Translate, useTranslator } from '../../../../i18n/index.js'; +import { Translate, useTranslator } from "../../../../i18n/index.js"; import { CardTable } from "./Table.js"; export interface Props { @@ -43,47 +43,92 @@ export interface Props { onDelete: () => void; } -export function ListPage({ payTo, onChangePayTo, transfers, onCreate, onDelete, accounts, onLoadMoreBefore, onLoadMoreAfter, isAllTransfers, isNonVerifiedTransfers, isVerifiedTransfers, onShowAll, onShowUnverified, onShowVerified }: Props): VNode { - const form = { payto_uri: payTo } +export function ListPage({ + payTo, + onChangePayTo, + transfers, + onCreate, + onDelete, + accounts, + onLoadMoreBefore, + onLoadMoreAfter, + isAllTransfers, + isNonVerifiedTransfers, + isVerifiedTransfers, + onShowAll, + onShowUnverified, + onShowVerified, +}: Props): VNode { + const form = { payto_uri: payTo }; const i18n = useTranslator(); - return <section class="section is-main-section"> - <div class="columns"> - <div class="column" /> - <div class="column is-10"> - <FormProvider object={form} valueHandler={(updater) => onChangePayTo(updater(form).payto_uri)}> - <InputSelector name="payto_uri" label={i18n`Address`} - values={accounts} - placeholder={i18n`Select one account`} - tooltip={i18n`filter by account address`} /> - </FormProvider> + return ( + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-10"> + <FormProvider + object={form} + valueHandler={(updater) => onChangePayTo(updater(form).payto_uri)} + > + <InputSelector + name="payto_uri" + label={i18n`Address`} + values={accounts} + placeholder={i18n`Select one account`} + tooltip={i18n`filter by account address`} + /> + </FormProvider> + </div> + <div class="column" /> + </div> + <div class="tabs"> + <ul> + <li class={isAllTransfers ? "is-active" : ""}> + <div + class="has-tooltip-right" + data-tooltip={i18n`remove all filters`} + > + <a onClick={onShowAll}> + <Translate>All</Translate> + </a> + </div> + </li> + <li class={isVerifiedTransfers ? "is-active" : ""}> + <div + class="has-tooltip-right" + data-tooltip={i18n`only show wire transfers confirmed by the merchant`} + > + <a onClick={onShowVerified}> + <Translate>Verified</Translate> + </a> + </div> + </li> + <li class={isNonVerifiedTransfers ? "is-active" : ""}> + <div + class="has-tooltip-right" + data-tooltip={i18n`only show wire transfers claimed by the exchange`} + > + <a onClick={onShowUnverified}> + <Translate>Unverified</Translate> + </a> + </div> + </li> + </ul> </div> - <div class="column" /> - </div> - <div class="tabs"> - <ul> - <li class={isAllTransfers ? 'is-active' : ''}> - <div class="has-tooltip-right" data-tooltip={i18n`remove all filters`}> - <a onClick={onShowAll}><Translate>All</Translate></a> - </div> - </li> - <li class={isVerifiedTransfers ? 'is-active' : ''}> - <div class="has-tooltip-right" data-tooltip={i18n`only show wire transfers confirmed by the merchant`}> - <a onClick={onShowVerified}><Translate>Verified</Translate></a> - </div> - </li> - <li class={isNonVerifiedTransfers ? 'is-active' : ''}> - <div class="has-tooltip-right" data-tooltip={i18n`only show wire transfers claimed by the exchange`}> - <a onClick={onShowUnverified}><Translate>Unverified</Translate></a> - </div> - </li> - </ul> - </div> - <CardTable transfers={transfers.map(o => ({ ...o, id: String(o.transfer_serial_id) }))} - accounts={accounts} - onCreate={onCreate} - onDelete={onDelete} - onLoadMoreBefore={onLoadMoreBefore} hasMoreBefore={!onLoadMoreBefore} - onLoadMoreAfter={onLoadMoreAfter} hasMoreAfter={!onLoadMoreAfter} /> - </section>; + <CardTable + transfers={transfers.map((o) => ({ + ...o, + id: String(o.transfer_serial_id), + }))} + accounts={accounts} + onCreate={onCreate} + onDelete={onDelete} + onLoadMoreBefore={onLoadMoreBefore} + hasMoreBefore={!onLoadMoreBefore} + onLoadMoreAfter={onLoadMoreAfter} + hasMoreAfter={!onLoadMoreAfter} + /> + </section> + ); } 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 0f9b87732..2341fb80a 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 @@ -173,7 +173,7 @@ function Table({ ? i18n`never` : format( i.execution_time.t_s * 1000, - "yyyy/MM/dd HH:mm:ss" + "yyyy/MM/dd HH:mm:ss", ) : i18n`unknown`} </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 439e81f9f..242380fbc 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 @@ -15,12 +15,12 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode } from 'preact'; -import { useState } from 'preact/hooks'; +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; import { Loading } from "../../../../components/exception/loading.js"; import { MerchantBackend } from "../../../../declaration.js"; import { HttpError } from "../../../../hooks/backend.js"; @@ -35,51 +35,65 @@ interface Props { onCreate: () => void; } interface Form { - verified?: 'yes' | 'no'; + verified?: "yes" | "no"; payto_uri?: string; } -export default function ListTransfer({ onUnauthorized, onLoadError, onCreate, onNotFound }: Props): VNode { - const [form, setForm] = useState<Form>({ payto_uri: '' }) - const setFilter = (s?: 'yes' | 'no') => setForm({ ...form, verified: s }) - - const [position, setPosition] = useState<string | undefined>(undefined) +export default function ListTransfer({ + onUnauthorized, + onLoadError, + onCreate, + onNotFound, +}: Props): VNode { + const [form, setForm] = useState<Form>({ payto_uri: "" }); + const setFilter = (s?: "yes" | "no") => setForm({ ...form, verified: s }); - const instance = useInstanceDetails() - const accounts = !instance.ok ? [] : instance.data.accounts.map(a => a.payto_uri) + const [position, setPosition] = useState<string | undefined>(undefined); - const isVerifiedTransfers = form.verified === 'yes' - const isNonVerifiedTransfers = form.verified === 'no' - const isAllTransfers = form.verified === undefined + const instance = useInstanceDetails(); + const accounts = !instance.ok + ? [] + : instance.data.accounts.map((a) => a.payto_uri); - const result = useInstanceTransfers({ - position, - payto_uri: form.payto_uri === '' ? undefined : form.payto_uri, - verified: form.verified, - }, (id) => setPosition(id)) + const isVerifiedTransfers = form.verified === "yes"; + const isNonVerifiedTransfers = form.verified === "no"; + const isAllTransfers = form.verified === undefined; - if (result.clientError && result.isUnauthorized) return onUnauthorized() - if (result.clientError && result.isNotfound) return onNotFound() - if (result.loading) return <Loading /> - if (!result.ok) return onLoadError(result) + const result = useInstanceTransfers( + { + position, + payto_uri: form.payto_uri === "" ? undefined : form.payto_uri, + verified: form.verified, + }, + (id) => setPosition(id), + ); - return <ListPage - accounts={accounts} - transfers={result.data.transfers} - onLoadMoreBefore={result.isReachingStart ? result.loadMorePrev : undefined} - onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined} - onCreate={onCreate} - onDelete={() => {null}} - // position={position} setPosition={setPosition} - onShowAll={() => setFilter(undefined)} - onShowUnverified={() => setFilter('no')} - onShowVerified={() => setFilter('yes')} - isAllTransfers={isAllTransfers} - isVerifiedTransfers={isVerifiedTransfers} - isNonVerifiedTransfers={isNonVerifiedTransfers} - payTo={form.payto_uri} - onChangePayTo={(p) => setForm(v => ({ ...v, payto_uri: p }))} - /> + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); + return ( + <ListPage + accounts={accounts} + transfers={result.data.transfers} + onLoadMoreBefore={ + result.isReachingStart ? result.loadMorePrev : undefined + } + onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined} + onCreate={onCreate} + onDelete={() => { + null; + }} + // position={position} setPosition={setPosition} + onShowAll={() => setFilter(undefined)} + onShowUnverified={() => setFilter("no")} + onShowVerified={() => setFilter("yes")} + isAllTransfers={isAllTransfers} + isVerifiedTransfers={isVerifiedTransfers} + isNonVerifiedTransfers={isNonVerifiedTransfers} + payTo={form.payto_uri} + onChangePayTo={(p) => setForm((v) => ({ ...v, payto_uri: p }))} + /> + ); } - diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/update/index.tsx index cc9cd8afc..84cc95e72 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/update/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/update/index.tsx @@ -15,12 +15,12 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode } from 'preact'; +import { h, VNode } from "preact"; -export default function UpdateTransfer():VNode { - return <div>order transfer page</div> -}
\ No newline at end of file +export default function UpdateTransfer(): VNode { + return <div>order transfer page</div>; +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx index fa9163ac6..58b8d87ea 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx @@ -33,7 +33,7 @@ export default { function createExample<Props>( Component: FunctionalComponent<Props>, - props: Partial<Props> + props: Partial<Props>, ) { const r = (args: any) => <Component {...args} />; r.args = props; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx index 833592fcb..d7acdf023 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx @@ -44,7 +44,7 @@ type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & { interface Props { onUpdate: (d: Entity) => void; onChangeAuth: ( - d: MerchantBackend.Instances.InstanceAuthConfigurationMessage + d: MerchantBackend.Instances.InstanceAuthConfigurationMessage, ) => Promise<void>; selected: MerchantBackend.Instances.QueryInstancesResponse; isLoading: boolean; @@ -52,7 +52,7 @@ interface Props { } function convert( - from: MerchantBackend.Instances.QueryInstancesResponse + from: MerchantBackend.Instances.QueryInstancesResponse, ): Entity { const { accounts, ...rest } = from; const payto_uris = accounts.filter((a) => a.active).map((a) => a.payto_uri); @@ -105,7 +105,7 @@ export function UpdatePage({ : undefinedIfEmpty( value.payto_uris.map((p) => { return !PAYTO_REGEX.test(p) ? i18n`is not valid` : undefined; - }) + }), ), default_max_deposit_fee: !value.default_max_deposit_fee ? i18n`required` @@ -144,7 +144,7 @@ export function UpdatePage({ }; const hasErrors = Object.keys(errors).some( - (k) => (errors as any)[k] !== undefined + (k) => (errors as any)[k] !== undefined, ); const submit = async (): Promise<void> => { await onUpdate(value as Entity); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx index 78d470b88..480274e66 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx @@ -48,7 +48,7 @@ export default function Update(props: Props): VNode { export function AdminUpdate(props: Props & { instanceId: string }): VNode { const { updateInstance, clearToken, setNewToken } = useManagementAPI( - props.instanceId + props.instanceId, ); const result = useManagedInstanceDetails(props.instanceId); return CommonUpdate(props, result, updateInstance, clearToken, setNewToken); @@ -66,7 +66,7 @@ function CommonUpdate( result: HttpResponse<MerchantBackend.Instances.QueryInstancesResponse>, updateInstance: any, clearToken: any, - setNewToken: any + setNewToken: any, ): VNode { const { changeToken } = useInstanceContext(); const [notif, setNotif] = useState<Notification | undefined>(undefined); @@ -85,7 +85,7 @@ function CommonUpdate( isLoading={false} selected={result.data} onUpdate={( - d: MerchantBackend.Instances.InstanceReconfigurationMessage + d: MerchantBackend.Instances.InstanceReconfigurationMessage, ): Promise<void> => { return updateInstance(d) .then(onConfirm) @@ -94,11 +94,11 @@ function CommonUpdate( message: i18n`Failed to create instance`, type: "ERROR", description: error.message, - }) + }), ); }} onChangeAuth={( - d: MerchantBackend.Instances.InstanceAuthConfigurationMessage + d: MerchantBackend.Instances.InstanceAuthConfigurationMessage, ): Promise<void> => { const apiCall = d.method === "external" ? clearToken() : setNewToken(d.token!); diff --git a/packages/merchant-backoffice-ui/src/paths/login/index.tsx b/packages/merchant-backoffice-ui/src/paths/login/index.tsx index 6784e493a..caa63c714 100644 --- a/packages/merchant-backoffice-ui/src/paths/login/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/login/index.tsx @@ -14,10 +14,10 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ - /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { h, VNode } from "preact"; import { LoginModal } from "../../components/exception/login.js"; @@ -25,5 +25,5 @@ interface Props { onConfirm: (url: string, token?: string) => void; } export default function LoginPage({ onConfirm }: Props): VNode { - return <LoginModal onConfirm={onConfirm} /> -}
\ No newline at end of file + return <LoginModal onConfirm={onConfirm} />; +} diff --git a/packages/merchant-backoffice-ui/src/paths/notfound/index.tsx b/packages/merchant-backoffice-ui/src/paths/notfound/index.tsx index fb468a411..452908154 100644 --- a/packages/merchant-backoffice-ui/src/paths/notfound/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/notfound/index.tsx @@ -14,23 +14,22 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ - /** +/** * * @author Sebastian Javier Marchano (sebasjm) */ -import { h, VNode } from 'preact'; -import { Link } from 'preact-router/match'; +import { h, VNode } from "preact"; +import { Link } from "preact-router/match"; export default function NotFoundPage(): VNode { - return ( - <div> - <h1>Error 404</h1> - <p>That page doesn't exist.</p> - <Link href="/"> - <h4>Back to Home</h4> - </Link> - </div> - ); + return ( + <div> + <h1>Error 404</h1> + <p>That page doesn't exist.</p> + <Link href="/"> + <h4>Back to Home</h4> + </Link> + </div> + ); } - |