summaryrefslogtreecommitdiff
path: root/packages/frontend
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-06-14 23:59:54 -0300
committerSebastian <sebasjm@gmail.com>2021-06-15 00:04:19 -0300
commitbfe7540cc735567939a6f29e9a055486deaf887f (patch)
tree125839459a860d1711ae96c25f78101114e3a049 /packages/frontend
parentfee8423fbad552a4e7609cd4f6bc48585054576a (diff)
downloadmerchant-backoffice-bfe7540cc735567939a6f29e9a055486deaf887f.tar.gz
merchant-backoffice-bfe7540cc735567939a6f29e9a055486deaf887f.tar.bz2
merchant-backoffice-bfe7540cc735567939a6f29e9a055486deaf887f.zip
storybook samples
Diffstat (limited to 'packages/frontend')
-rw-r--r--packages/frontend/.storybook/main.js40
-rw-r--r--packages/frontend/.storybook/preview.js37
-rw-r--r--packages/frontend/src/components/form/InputSearchProduct.tsx53
-rw-r--r--packages/frontend/src/components/form/InputSecured.stories.tsx2
-rw-r--r--packages/frontend/src/components/form/InputStock.stories.tsx2
-rw-r--r--packages/frontend/src/context/fetch.ts40
-rw-r--r--packages/frontend/src/hooks/product.ts2
-rw-r--r--packages/frontend/src/index.tsx13
-rw-r--r--packages/frontend/src/paths/admin/create/Create.stories.tsx22
-rw-r--r--packages/frontend/src/paths/admin/list/View.stories.tsx2
-rw-r--r--packages/frontend/src/paths/instance/details/Details.stories.tsx60
-rw-r--r--packages/frontend/src/paths/instance/orders/create/Create.stories.tsx62
-rw-r--r--packages/frontend/src/paths/instance/orders/create/CreatePage.tsx56
-rw-r--r--packages/frontend/src/paths/instance/orders/create/InventoryProductForm.tsx7
-rw-r--r--packages/frontend/src/paths/instance/orders/create/NonInventoryProductForm.tsx3
-rw-r--r--packages/frontend/src/paths/instance/orders/create/index.tsx26
-rw-r--r--packages/frontend/src/paths/instance/orders/details/Detail.stories.tsx137
-rw-r--r--packages/frontend/src/paths/instance/orders/details/DetailPage.tsx130
-rw-r--r--packages/frontend/src/paths/instance/orders/details/Timeline.tsx2
-rw-r--r--packages/frontend/src/paths/instance/orders/list/List.stories.tsx87
-rw-r--r--packages/frontend/src/paths/instance/orders/list/Table.tsx30
-rw-r--r--packages/frontend/src/paths/instance/orders/list/index.tsx68
-rw-r--r--packages/frontend/src/paths/instance/products/create/Create.stories.tsx42
-rw-r--r--packages/frontend/src/paths/instance/products/list/List.stories.tsx58
-rw-r--r--packages/frontend/src/paths/instance/products/update/Update.stories.tsx71
-rw-r--r--packages/frontend/src/paths/instance/reserves/create/Create.stories.tsx42
-rw-r--r--packages/frontend/src/paths/instance/reserves/details/Details.stories.tsx102
-rw-r--r--packages/frontend/src/paths/instance/reserves/list/List.stories.tsx97
-rw-r--r--packages/frontend/src/paths/instance/reserves/list/Table.tsx1
-rw-r--r--packages/frontend/src/paths/instance/transfers/create/Create.stories.tsx43
-rw-r--r--packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx8
-rw-r--r--packages/frontend/src/paths/instance/transfers/create/index.tsx4
-rw-r--r--packages/frontend/src/paths/instance/transfers/list/List.stories.tsx84
-rw-r--r--packages/frontend/src/paths/instance/transfers/list/Table.tsx28
-rw-r--r--packages/frontend/src/paths/instance/transfers/list/index.tsx2
-rw-r--r--packages/frontend/src/paths/instance/update/Update.stories.tsx59
36 files changed, 1296 insertions, 226 deletions
diff --git a/packages/frontend/.storybook/main.js b/packages/frontend/.storybook/main.js
index 39d042b..f8e4bbc 100644
--- a/packages/frontend/.storybook/main.js
+++ b/packages/frontend/.storybook/main.js
@@ -30,24 +30,28 @@ module.exports = {
"@storybook/addon-a11y",
"@storybook/addon-essentials" //docs, control, actions, viewpot, toolbar, background
],
- webpackFinal: async (config, { configType }) => {
- // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
- // You can change the configuration based on that.
- // 'PRODUCTION' is used when building the static version of storybook.
- // Make whatever fine-grained changes you need
- // config.module.rules.push({
- // test: [/\.pot?$/, /\.mo$/],
- // loader: require.resolve('messageformat-po-loader'),
- // options: {
- // biDiSupport: false,
- // defaultCharset: null,
- // defaultLocale: 'en',
- // forceContext: false,
- // pluralFunction: null,
- // verbose: false
- // }
- // });
- // Return the altered config
+ // sb does not yet support new jsx transform by default
+ // https://github.com/storybookjs/storybook/issues/12881
+ // https://github.com/storybookjs/storybook/issues/12952
+ babel: async (options) => ({
+ ...options,
+ presets: [
+ ...options.presets,
+ [
+ '@babel/preset-react', {
+ runtime: 'automatic',
+ },
+ 'preset-react-jsx-transform'
+ ],
+ ],
+ }),
+ webpackFinal: (config) => {
+ // should be removed after storybook 6.3
+ // https://github.com/storybookjs/storybook/issues/12853#issuecomment-821576113
+ config.resolve.alias = {
+ react: "preact/compat",
+ "react-dom": "preact/compat",
+ };
return config;
},
} \ No newline at end of file
diff --git a/packages/frontend/.storybook/preview.js b/packages/frontend/.storybook/preview.js
index f511267..ab24c75 100644
--- a/packages/frontend/.storybook/preview.js
+++ b/packages/frontend/.storybook/preview.js
@@ -16,17 +16,31 @@
import "../src/scss/main.scss"
import { ConfigContextProvider } from '../src/context/config'
+import { InstanceContextProvider } from '../src/context/instance'
import { TranslationProvider } from '../src/context/translation'
+import { BackendContextProvider } from '../src/context/backend'
import { h } from 'preact';
const mockConfig = {
backendURL: 'http://demo.taler.net',
- currency: 'KUDOS'
+ currency: 'TESTKUDOS'
+}
+
+const mockInstance = {
+ id: 'instance-id',
+ token: 'instance-token',
+ admin: false,
+}
+
+const mockBackend = {
+ url: 'http://merchant.url/',
+ token: 'default-token',
+ triedToLog: false,
}
export const parameters = {
controls: { expanded: true },
- actions: { argTypesRegex: "^on[A-Z].*" },
+ // actions: { argTypesRegex: "^on.*" },
}
export const globalTypes = {
@@ -45,11 +59,16 @@ export const globalTypes = {
};
export const decorators = [
- (Story, { globals }) => {
-
- return <TranslationProvider initial={globals.locale}>
- <Story />
- </TranslationProvider>
- },
- (Story) => <ConfigContextProvider value={mockConfig}> <Story /> </ConfigContextProvider>
+ (Story, { globals }) => <TranslationProvider initial={globals.locale}>
+ <Story />
+ </TranslationProvider>,
+ (Story) => <ConfigContextProvider value={mockConfig}>
+ <Story />
+ </ConfigContextProvider>,
+ (Story) => <InstanceContextProvider value={mockInstance}>
+ <Story />
+ </InstanceContextProvider>,
+ (Story) => <BackendContextProvider value={mockBackend}>
+ <Story />
+ </BackendContextProvider>,
];
diff --git a/packages/frontend/src/components/form/InputSearchProduct.tsx b/packages/frontend/src/components/form/InputSearchProduct.tsx
index 43f79c9..f9f8f68 100644
--- a/packages/frontend/src/components/form/InputSearchProduct.tsx
+++ b/packages/frontend/src/components/form/InputSearchProduct.tsx
@@ -32,13 +32,14 @@ type Entity = MerchantBackend.Products.ProductDetail & WithId
export interface Props {
selected?: Entity;
onChange: (p?: Entity) => void;
+ products: (MerchantBackend.Products.ProductDetail & WithId)[],
}
interface ProductSearch {
name: string;
}
-export function InputSearchProduct({ selected, onChange }: Props): VNode {
+export function InputSearchProduct({ selected, onChange, products }: Props): VNode {
const [prodForm, setProdName] = useState<Partial<ProductSearch>>({ name: '' })
const errors: FormErrors<ProductSearch> = {
@@ -75,10 +76,14 @@ export function InputSearchProduct({ selected, onChange }: Props): VNode {
addonBefore={<span class="icon" ><i class="mdi mdi-magnify" /></span>}
>
<div>
- <ProductList name={prodForm.name} onSelect={(p) => {
- setProdName({ name: '' })
- onChange(p)
- }} />
+ <ProductList
+ name={prodForm.name}
+ list={products}
+ onSelect={(p) => {
+ setProdName({ name: '' })
+ onChange(p)
+ }}
+ />
</div>
</InputWithAddon>
@@ -89,29 +94,22 @@ export function InputSearchProduct({ selected, onChange }: Props): VNode {
interface ProductListProps {
name?: string;
onSelect: (p: MerchantBackend.Products.ProductDetail & WithId) => void;
+ list: (MerchantBackend.Products.ProductDetail & WithId)[]
}
-function ProductList({ name, onSelect }: ProductListProps) {
- const result = useInstanceProducts();
-
- const re = new RegExp(`.*${name}.*`)
-
- let products = <div />
+function ProductList({ name, onSelect, list }: ProductListProps) {
+ if (!name) {
+ /* FIXME
+ this BR is added to occupy the space that will be added when the
+ dropdown appears
+ */
+ return <div ><br /></div>
+ }
+ const filtered = list.filter(p => p.id.includes(name) || p.description.includes(name))
- if (result.loading) {
- products = <div class="dropdown-content">
- <div class="dropdown-item"><Translate>loading...</Translate></div>
- </div>
- } else if (result.ok && !!name) {
- if (!result.data.length) {
- products = <div class="dropdown-content">
- <div class="dropdown-item">
- <Translate>no products found</Translate>
- </div>
- </div>
- } else {
- const filtered = result.data.filter(p => re.test(p.id) || re.test(p.description))
- products = <div class="dropdown-content">
+ return <div class="dropdown is-active">
+ <div class="dropdown-menu" id="dropdown-menu" role="menu" style={{ minWidth: '20rem' }}>
+ <div class="dropdown-content">
{!filtered.length ?
<div class="dropdown-item" >
<Translate>no products found with that description</Translate>
@@ -136,11 +134,6 @@ function ProductList({ name, onSelect }: ProductListProps) {
))
}
</div>
- }
- }
- return <div class="dropdown is-active">
- <div class="dropdown-menu" id="dropdown-menu" role="menu" style={{ minWidth: '20rem' }}>
- {products}
</div>
</div>
}
diff --git a/packages/frontend/src/components/form/InputSecured.stories.tsx b/packages/frontend/src/components/form/InputSecured.stories.tsx
index 7314add..1990eee 100644
--- a/packages/frontend/src/components/form/InputSecured.stories.tsx
+++ b/packages/frontend/src/components/form/InputSecured.stories.tsx
@@ -25,7 +25,7 @@ import { FormProvider } from "./FormProvider";
import { InputSecured } from './InputSecured';
export default {
- title: 'Fields/InputSecured',
+ title: 'Components/Form/InputSecured',
component: InputSecured,
};
diff --git a/packages/frontend/src/components/form/InputStock.stories.tsx b/packages/frontend/src/components/form/InputStock.stories.tsx
index cc2654b..6e2275b 100644
--- a/packages/frontend/src/components/form/InputStock.stories.tsx
+++ b/packages/frontend/src/components/form/InputStock.stories.tsx
@@ -26,7 +26,7 @@ import { FormProvider } from "./FormProvider";
import { InputStock, Stock } from './InputStock'
export default {
- title: 'Fields/InputStock',
+ title: 'Components/Form/InputStock',
component: InputStock,
};
diff --git a/packages/frontend/src/context/fetch.ts b/packages/frontend/src/context/fetch.ts
new file mode 100644
index 0000000..52a4f9c
--- /dev/null
+++ b/packages/frontend/src/context/fetch.ts
@@ -0,0 +1,40 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, createContext, VNode, ComponentChildren } from 'preact'
+import { useContext } from 'preact/hooks'
+import useSWR, { trigger, useSWRInfinite, cache, mutate } from 'swr';
+
+interface Type {
+ useSWR: typeof useSWR,
+ useSWRInfinite: typeof useSWRInfinite,
+}
+
+const Context = createContext<Type>({} as any)
+
+export const useFetchContext = (): Type => useContext(Context);
+export const FetchContextProvider = ({ children }: { children: ComponentChildren }): VNode => {
+ return h(Context.Provider, { value: { useSWR, useSWRInfinite }, children });
+}
+
+export const FetchContextProviderTesting = ({ children, data }: { children: ComponentChildren, data:any }): VNode => {
+ return h(Context.Provider, { value: { useSWR: () => data, useSWRInfinite }, children });
+}
diff --git a/packages/frontend/src/hooks/product.ts b/packages/frontend/src/hooks/product.ts
index cdb1c4e..a8b33ba 100644
--- a/packages/frontend/src/hooks/product.ts
+++ b/packages/frontend/src/hooks/product.ts
@@ -16,6 +16,7 @@
import { useEffect } from 'preact/hooks';
import useSWR, { trigger, useSWRInfinite, cache, mutate } from 'swr';
import { useBackendContext } from '../context/backend';
+// import { useFetchContext } from '../context/fetch';
import { useInstanceContext } from '../context/instance';
import { MerchantBackend, WithId } from '../declaration';
import { fetcher, HttpError, HttpResponse, HttpResponseOk, mutateAll, request } from './backend';
@@ -125,6 +126,7 @@ export function useProductAPI(): ProductAPI {
export function useInstanceProducts(): HttpResponse<(MerchantBackend.Products.ProductDetail & WithId)[]> {
const { url: baseUrl, token: baseToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
+ // const { useSWR, useSWRInfinite } = useFetchContext();
const { url, token } = !admin ? {
url: baseUrl, token: baseToken
diff --git a/packages/frontend/src/index.tsx b/packages/frontend/src/index.tsx
index 5c2df2f..0a77533 100644
--- a/packages/frontend/src/index.tsx
+++ b/packages/frontend/src/index.tsx
@@ -27,6 +27,7 @@ import { Loading } from "./components/exception/loading";
import { NotificationCard, NotYetReadyAppMenu } from "./components/menu";
import { BackendContextProvider, useBackendContext } from './context/backend';
import { ConfigContextProvider } from './context/config';
+import { FetchContextProvider } from './context/fetch';
import { TranslationProvider } from './context/translation';
import { useBackendConfig } from "./hooks/backend";
import { useTranslator } from './i18n';
@@ -35,11 +36,13 @@ import "./scss/main.scss";
export default function Application(): VNode {
return (
- <BackendContextProvider>
- <TranslationProvider>
- <ApplicationStatusRoutes />
- </TranslationProvider>
- </BackendContextProvider>
+ // <FetchContextProvider>
+ <BackendContextProvider>
+ <TranslationProvider>
+ <ApplicationStatusRoutes />
+ </TranslationProvider>
+ </BackendContextProvider>
+ // </FetchContextProvider>
);
}
diff --git a/packages/frontend/src/paths/admin/create/Create.stories.tsx b/packages/frontend/src/paths/admin/create/Create.stories.tsx
index d1d8f39..c128755 100644
--- a/packages/frontend/src/paths/admin/create/Create.stories.tsx
+++ b/packages/frontend/src/paths/admin/create/Create.stories.tsx
@@ -19,20 +19,28 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { h, VNode } from 'preact';
-import { CreatePage } from './CreatePage';
+import { h, VNode, FunctionalComponent } from 'preact';
+import { CreatePage as TestedComponent } from './CreatePage';
export default {
- title: 'Instances/Create',
- component: CreatePage,
+ title: 'Pages/Instance/Create',
+ component: TestedComponent,
argTypes: {
onCreate: { action: 'onCreate' },
goBack: { action: 'goBack' },
}
};
-export const Example = (a: any): VNode => <CreatePage {...a} />;
-Example.args = {
- isLoading: false
+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 = (a: any): VNode => <CreatePage {...a} />;
+// Example.args = {
+// isLoading: false
+// }
diff --git a/packages/frontend/src/paths/admin/list/View.stories.tsx b/packages/frontend/src/paths/admin/list/View.stories.tsx
index 0d34142..3da8c2e 100644
--- a/packages/frontend/src/paths/admin/list/View.stories.tsx
+++ b/packages/frontend/src/paths/admin/list/View.stories.tsx
@@ -24,7 +24,7 @@ import { View } from './View';
export default {
- title: 'Instances/View',
+ title: 'Pages/Instance/List',
component: View,
argTypes: {
onSelect: { action: 'onSelect' },
diff --git a/packages/frontend/src/paths/instance/details/Details.stories.tsx b/packages/frontend/src/paths/instance/details/Details.stories.tsx
new file mode 100644
index 0000000..21141f7
--- /dev/null
+++ b/packages/frontend/src/paths/instance/details/Details.stories.tsx
@@ -0,0 +1,60 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { DetailPage as TestedComponent } from './DetailPage';
+
+
+export default {
+ title: 'Pages/Instance/Detail',
+ component: TestedComponent,
+ argTypes: {
+ 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
+}
+
+export const Example = createExample(TestedComponent, {
+ selected: {
+ accounts: [],
+ name: 'name',
+ auth: {method:'external'},
+ address: {},
+ jurisdiction: {},
+ default_max_deposit_fee: 'TESTKUDOS:2',
+ default_max_wire_fee: 'TESTKUDOS:1',
+ default_pay_delay: {
+ d_ms: 1000000,
+ },
+ default_wire_fee_amortization: 1,
+ default_wire_transfer_delay: {
+ d_ms: 100000,
+ },
+ merchant_pub: 'ASDWQEKASJDKSADJ'
+ }
+});
+
diff --git a/packages/frontend/src/paths/instance/orders/create/Create.stories.tsx b/packages/frontend/src/paths/instance/orders/create/Create.stories.tsx
new file mode 100644
index 0000000..29f6842
--- /dev/null
+++ b/packages/frontend/src/paths/instance/orders/create/Create.stories.tsx
@@ -0,0 +1,62 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { CreatePage as TestedComponent } from './CreatePage';
+
+
+export default {
+ title: 'Pages/Order/Create',
+ component: TestedComponent,
+ argTypes: {
+ 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
+}
+
+export const Example = createExample(TestedComponent, {
+ instanceConfig: {
+ default_max_deposit_fee: '',
+ default_max_wire_fee: '',
+ default_pay_delay: {
+ d_ms: 'forever'
+ },
+ default_wire_fee_amortization: 1
+ },
+ instanceInventory: [{
+ id: 't-shirt-1',
+ description: 'a m size t-shirt',
+ price: 'TESTKUDOS:1',
+ total_stock: -1
+ },{
+ id: 't-shirt-2',
+ description: 'a xl size t-shirt'
+ } as any,{
+ id: 't-shirt-3',
+ description: 'a s size t-shirt'
+ } as any]
+});
diff --git a/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx b/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
index ae32dac..768a1cb 100644
--- a/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
+++ b/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
@@ -31,25 +31,42 @@ import { InputGroup } from "../../../../components/form/InputGroup";
import { InputLocation } from "../../../../components/form/InputLocation";
import { ProductList } from "../../../../components/product/ProductList";
import { useConfigContext } from "../../../../context/config";
-import { MerchantBackend, WithId } from "../../../../declaration";
-import { useInstanceDetails } from "../../../../hooks/instance";
+import { Duration, MerchantBackend, WithId } from "../../../../declaration";
import { Translate, useTranslator } from "../../../../i18n";
import { OrderCreateSchema as schema } from '../../../../schemas/index';
-import { multiplyPrice, rate, subtractPrices, sumPrices } from "../../../../utils/amount";
+import { multiplyPrice, rate, sumPrices } from "../../../../utils/amount";
import { InventoryProductForm } from "./InventoryProductForm";
import { NonInventoryProductFrom } from "./NonInventoryProductForm";
interface Props {
onCreate: (d: MerchantBackend.Orders.PostOrderRequest) => void;
onBack?: () => void;
+ instanceConfig: InstanceConfig;
+ instanceInventory: (MerchantBackend.Products.ProductDetail & WithId)[],
}
+interface InstanceConfig {
+ default_max_wire_fee: string;
+ default_max_deposit_fee: string;
+ default_wire_fee_amortization: number;
+ default_pay_delay: Duration;
+}
+
+function with_defaults(config: InstanceConfig): Partial<Entity> {
+ const defaultPayDeadline = !config.default_pay_delay || config.default_pay_delay.d_ms === "forever" ?
+ undefined :
+ add(new Date(), { seconds: config.default_pay_delay.d_ms / 1000 })
-function with_defaults(): Partial<Entity> {
return {
inventoryProducts: {},
products: [],
pricing: {} as any,
- payments: {} as any,
+ payments: {
+ max_wire_fee: config.default_max_wire_fee,
+ max_fee: config.default_max_deposit_fee,
+ wire_fee_amortization: config.default_wire_fee_amortization,
+ pay_deadline: defaultPayDeadline,
+ refund_deadline: defaultPayDeadline,
+ } as Partial<Payments>,
extra: ''
};
}
@@ -86,8 +103,8 @@ interface Entity {
extra: string;
}
-export function CreatePage({ onCreate, onBack }: Props): VNode {
- const [value, valueHandler] = useState(with_defaults())
+export function CreatePage({ onCreate, onBack, instanceConfig, instanceInventory }: Props): VNode {
+ const [value, valueHandler] = useState(with_defaults(instanceConfig))
// const [errors, setErrors] = useState<FormErrors<Entity>>({})
const inventoryList = Object.values(value.inventoryProducts || {})
@@ -196,30 +213,6 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
})
}, [value.pricing?.order_price])
- const details_response = useInstanceDetails()
-
- const dmwf = !details_response.ok ? undefined : details_response.data.default_max_wire_fee;
- const dmdf = !details_response.ok ? undefined : details_response.data.default_max_deposit_fee;
- const dwfa = !details_response.ok ? undefined : details_response.data.default_wire_fee_amortization;
- const dpd = !details_response.ok ? undefined : details_response.data.default_pay_delay;
- useEffect(() => {
- if (details_response.ok) {
- valueHandler(v => {
- const defaultPayDeadline = !dpd || dpd.d_ms === "forever" ? undefined : add(new Date(), { seconds: dpd.d_ms / 1000 })
- return ({
- ...v, payments: {
- ...v.payments,
- max_wire_fee: dmwf,
- max_fee: dmdf,
- wire_fee_amortization: dwfa,
- pay_deadline: defaultPayDeadline,
- refund_deadline: defaultPayDeadline,
- }
- })
- })
- }
- }, [details_response.ok, dmwf, dmdf, dwfa, dpd])
-
const i18n = useTranslator()
return <div>
@@ -239,6 +232,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
<InventoryProductForm
currentProducts={value.inventoryProducts || {}}
onAddProduct={addProductToTheInventoryList}
+ inventory={instanceInventory}
/>
{inventoryList.length > 0 &&
diff --git a/packages/frontend/src/paths/instance/orders/create/InventoryProductForm.tsx b/packages/frontend/src/paths/instance/orders/create/InventoryProductForm.tsx
index b97b39a..0fc5338 100644
--- a/packages/frontend/src/paths/instance/orders/create/InventoryProductForm.tsx
+++ b/packages/frontend/src/paths/instance/orders/create/InventoryProductForm.tsx
@@ -29,10 +29,11 @@ type Form = {
interface Props {
currentProducts: ProductMap,
- onAddProduct: (product: MerchantBackend.Products.ProductDetail & WithId, quantity: number) => void
+ onAddProduct: (product: MerchantBackend.Products.ProductDetail & WithId, quantity: number) => void,
+ inventory: (MerchantBackend.Products.ProductDetail & WithId)[],
}
-export function InventoryProductForm({ currentProducts, onAddProduct }: Props): VNode {
+export function InventoryProductForm({ currentProducts, onAddProduct, inventory }: Props): VNode {
const initialState = { quantity: 1 }
const [state, setState] = useState<Partial<Form>>(initialState)
const [errors, setErrors] = useState<FormErrors<Form>>({})
@@ -74,7 +75,7 @@ export function InventoryProductForm({ currentProducts, onAddProduct }: Props):
}
return <FormProvider<Form> errors={errors} object={state} valueHandler={setState}>
- <InputSearchProduct selected={state.product} onChange={(p) => setState(v => ({ ...v, product: p }))} />
+ <InputSearchProduct selected={state.product} onChange={(p) => setState(v => ({ ...v, product: p }))} products={inventory} />
{ state.product && !productWithInfiniteStock && <InputNumber<Form> name="quantity" label={i18n`Quantity`} tooltip={i18n`how many products will be added`} /> }
<div class="buttons is-right mt-5">
<button class="button is-success" disabled={!state.product} onClick={submit}><Translate>Add</Translate></button>
diff --git a/packages/frontend/src/paths/instance/orders/create/NonInventoryProductForm.tsx b/packages/frontend/src/paths/instance/orders/create/NonInventoryProductForm.tsx
index 756ec23..31cf33b 100644
--- a/packages/frontend/src/paths/instance/orders/create/NonInventoryProductForm.tsx
+++ b/packages/frontend/src/paths/instance/orders/create/NonInventoryProductForm.tsx
@@ -60,12 +60,13 @@ export function NonInventoryProductFrom({ productToEdit, onAddProduct }: Props):
return Promise.reject()
})
+ const i18n = useTranslator()
return <Fragment>
<div class="buttons">
<button class="button is-success" onClick={() => setShowCreateProduct(true)} ><Translate>add new product</Translate></button>
</div>
{showCreateProduct && <ConfirmModal active
- description="alskdj alsk jdalksjd laksjd lkasjd"
+ description={i18n`Complete information of the product`}
onCancel={() => setShowCreateProduct(false)} onConfirm={submitForm}>
<ProductForm initial={productToEdit} onSubscribe={addFormSubmitter} />
</ConfirmModal>}
diff --git a/packages/frontend/src/paths/instance/orders/create/index.tsx b/packages/frontend/src/paths/instance/orders/create/index.tsx
index ee0577a..71f5b7f 100644
--- a/packages/frontend/src/paths/instance/orders/create/index.tsx
+++ b/packages/frontend/src/paths/instance/orders/create/index.tsx
@@ -21,9 +21,13 @@
import { Fragment, h, VNode } from 'preact';
import { useState } from 'preact/hooks';
+import { Loading } from '../../../../components/exception/loading';
import { NotificationCard } from '../../../../components/menu';
import { MerchantBackend } from '../../../../declaration';
+import { HttpError } from '../../../../hooks/backend';
+import { useInstanceDetails } from '../../../../hooks/instance';
import { useOrderAPI } from '../../../../hooks/order';
+import { useInstanceProducts } from '../../../../hooks/product';
import { Notification } from '../../../../utils/types';
import { CreatePage } from './CreatePage';
import { OrderCreatedSuccessfully } from './OrderCreatedSuccessfully';
@@ -35,11 +39,26 @@ export type Entity = {
interface Props {
onBack?: () => void;
onConfirm: () => void;
+ onUnauthorized: () => VNode;
+ onNotFound: () => VNode;
+ onLoadError: (error: HttpError) => VNode;
}
-export default function OrderCreate({ onConfirm, onBack }: Props): VNode {
+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()
+
+ 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)
return <Fragment>
@@ -55,6 +74,9 @@ export default function OrderCreate({ onConfirm, onBack }: Props): VNode {
description: error.message
})
})
- }} />
+ }}
+ instanceConfig={detailsResult.data}
+ instanceInventory={inventoryResult.data}
+ />
</Fragment>
} \ No newline at end of file
diff --git a/packages/frontend/src/paths/instance/orders/details/Detail.stories.tsx b/packages/frontend/src/paths/instance/orders/details/Detail.stories.tsx
new file mode 100644
index 0000000..3e689b0
--- /dev/null
+++ b/packages/frontend/src/paths/instance/orders/details/Detail.stories.tsx
@@ -0,0 +1,137 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { addDays } from 'date-fns/esm';
+import { h, VNode, FunctionalComponent } from 'preact';
+import { MerchantBackend } from '../../../../declaration';
+import { DetailPage as TestedComponent } from './DetailPage';
+
+
+export default {
+ title: 'Pages/Order/Detail',
+ component: TestedComponent,
+ argTypes: {
+ onRefund: { action: 'onRefund' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
+ const r = (args: any) => <Component {...args} />
+ r.args = props
+ return r
+}
+
+const defaultContractTerm = {
+ amount: 'TESTKUDOS:10',
+ timestamp: {
+ t_ms: new Date().getTime(),
+ },
+ auditors: [],
+ exchanges: [],
+ max_fee: 'TESTKUDOS:1',
+ max_wire_fee: 'TESTKUDOS:1',
+ merchant: {
+
+ } as any,
+ merchant_base_url: 'http://merchant.url/',
+ order_id: '2021.165-03GDFC26Y1NNG',
+ products: [],
+ summary: 'text summary',
+ wire_fee_amortization: 1,
+ wire_transfer_deadline: {
+ t_ms: 'never',
+ },
+ refund_deadline: { t_ms: 'never' },
+ merchant_pub: 'ASDASDASDSd',
+ nonce: 'QWEQWEQWE',
+ pay_deadline: {
+ t_ms: 'never',
+ },
+ wire_method: 'x-taler-bank',
+ h_wire: 'asd',
+} as MerchantBackend.ContractTerms
+
+// contract_terms: defaultContracTerm,
+export const Claimed = createExample(TestedComponent, {
+ id: '2021.165-03GDFC26Y1NNG',
+ selected: {
+ order_status: 'claimed',
+ contract_terms: defaultContractTerm
+ },
+});
+
+export const PaidNotRefundable = createExample(TestedComponent, {
+ id: '2021.165-03GDFC26Y1NNG',
+ selected: {
+ order_status: 'paid',
+ contract_terms: defaultContractTerm,
+ refunded: false,
+ deposit_total: 'TESTKUDOS:10',
+ exchange_ec: 0,
+ order_status_url: 'http://merchant.backend/status',
+ exchange_hc: 0,
+ refund_amount: 'TESTKUDOS:0',
+ refund_details: [],
+ refund_pending: false,
+ wire_details: [],
+ wire_reports: [],
+ wired: false,
+ }
+});
+
+export const PaidRefundable = createExample(TestedComponent, {
+ id: '2021.165-03GDFC26Y1NNG',
+ selected: {
+ order_status: 'paid',
+ contract_terms: {
+ ...defaultContractTerm,
+ refund_deadline: {
+ t_ms: addDays(new Date(), 2).getTime()
+ }
+ },
+ refunded: false,
+ deposit_total: 'TESTKUDOS:10',
+ exchange_ec: 0,
+ order_status_url: 'http://merchant.backend/status',
+ exchange_hc: 0,
+ refund_amount: 'TESTKUDOS:0',
+ refund_details: [],
+ refund_pending: false,
+ wire_details: [],
+ wire_reports: [],
+ wired: false,
+ }
+});
+
+export const Unpaid = createExample(TestedComponent, {
+ id: '2021.165-03GDFC26Y1NNG',
+ selected: {
+ order_status: 'unpaid',
+ order_status_url: 'http://merchant.backend/status',
+ creation_time: {
+ t_ms: new Date().getTime()
+ },
+ summary: 'text summary',
+ taler_pay_uri: 'pay uri',
+ total_amount: 'TESTKUDOS:10',
+ }
+});
diff --git a/packages/frontend/src/paths/instance/orders/details/DetailPage.tsx b/packages/frontend/src/paths/instance/orders/details/DetailPage.tsx
index bfc7c7f..b41bedf 100644
--- a/packages/frontend/src/paths/instance/orders/details/DetailPage.tsx
+++ b/packages/frontend/src/paths/instance/orders/details/DetailPage.tsx
@@ -84,31 +84,41 @@ function ContractTerms({ value }: { value: CT }) {
function ClaimedPage({ id, order }: { id: string; order: MerchantBackend.Orders.CheckPaymentClaimedResponse }) {
const events: Event[] = []
- events.push({
- when: new Date(order.contract_terms.timestamp.t_ms),
- description: 'order created',
- type: 'start'
- })
- events.push({
- when: new Date(order.contract_terms.pay_deadline.t_ms),
- description: 'pay deadline',
- type: 'deadline'
- })
- events.push({
- when: new Date(order.contract_terms.refund_deadline.t_ms),
- description: 'refund deadline',
- type: 'deadline'
- })
- events.push({
- when: new Date(order.contract_terms.wire_transfer_deadline.t_ms),
- description: 'wire deadline',
- type: 'deadline'
- })
- if (order.contract_terms.delivery_date) events.push({
- when: new Date(order.contract_terms.delivery_date?.t_ms),
- description: 'delivery',
- type: 'delivery'
- })
+ if (order.contract_terms.timestamp.t_ms !== 'never') {
+ events.push({
+ when: new Date(order.contract_terms.timestamp.t_ms),
+ description: 'order created',
+ type: 'start'
+ })
+ }
+ if (order.contract_terms.pay_deadline.t_ms !== 'never') {
+ events.push({
+ when: new Date(order.contract_terms.pay_deadline.t_ms),
+ description: 'pay deadline',
+ type: 'deadline'
+ })
+ }
+ if (order.contract_terms.refund_deadline.t_ms !== 'never') {
+ events.push({
+ when: new Date(order.contract_terms.refund_deadline.t_ms),
+ description: 'refund deadline',
+ type: 'deadline'
+ })
+ }
+ if (order.contract_terms.wire_transfer_deadline.t_ms !== 'never') {
+ events.push({
+ when: new Date(order.contract_terms.wire_transfer_deadline.t_ms),
+ description: 'wire deadline',
+ type: 'deadline'
+ })
+ }
+ if (order.contract_terms.delivery_date && order.contract_terms.delivery_date.t_ms !== 'never') {
+ events.push({
+ when: new Date(order.contract_terms.delivery_date?.t_ms),
+ description: 'delivery',
+ type: 'delivery'
+ })
+ }
const [value, valueHandler] = useState<Partial<Claimed>>(order)
const i18n = useTranslator()
@@ -141,9 +151,6 @@ function ClaimedPage({ id, order }: { id: string; order: MerchantBackend.Orders.
<div class="level-right">
<div class="level-item">
<h1 class="title">
- <button class="button is-info" onClick={() => {
- if (order.contract_terms.fulfillment_url) copyToClipboard(order.contract_terms.fulfillment_url)
- }}><Translate>copy url</Translate></button>
</h1>
</div>
</div>
@@ -196,31 +203,42 @@ function ClaimedPage({ id, order }: { id: string; order: MerchantBackend.Orders.
}
function PaidPage({ id, order, onRefund }: { id: string; order: MerchantBackend.Orders.CheckPaymentPaidResponse, onRefund: (id: string) => void }) {
const events: Event[] = []
- events.push({
- when: new Date(order.contract_terms.timestamp.t_ms),
- description: 'order created',
- type: 'start'
- })
- events.push({
- when: new Date(order.contract_terms.pay_deadline.t_ms),
- description: 'pay deadline',
- type: 'deadline'
- })
- events.push({
- when: new Date(order.contract_terms.refund_deadline.t_ms),
- description: 'refund deadline',
- type: 'deadline'
- })
- events.push({
- when: new Date(order.contract_terms.wire_transfer_deadline.t_ms),
- description: 'wire deadline',
- type: 'deadline'
- })
- if (order.contract_terms.delivery_date) events.push({
- when: new Date(order.contract_terms.delivery_date?.t_ms),
- description: 'delivery',
- type: 'delivery'
- })
+ if (order.contract_terms.timestamp.t_ms !== 'never') {
+ events.push({
+ when: new Date(order.contract_terms.timestamp.t_ms),
+ description: 'order created',
+ type: 'start'
+ })
+ }
+ if (order.contract_terms.pay_deadline.t_ms !== 'never') {
+ events.push({
+ when: new Date(order.contract_terms.pay_deadline.t_ms),
+ description: 'pay deadline',
+ type: 'deadline'
+ })
+
+ }
+ if (order.contract_terms.refund_deadline.t_ms !== 'never') {
+ events.push({
+ when: new Date(order.contract_terms.refund_deadline.t_ms),
+ description: 'refund deadline',
+ type: 'deadline'
+ })
+ }
+ if (order.contract_terms.wire_transfer_deadline.t_ms !== 'never') {
+ events.push({
+ when: new Date(order.contract_terms.wire_transfer_deadline.t_ms),
+ description: 'wire deadline',
+ type: 'deadline'
+ })
+ }
+ if (order.contract_terms.delivery_date && order.contract_terms.delivery_date.t_ms !== 'never') {
+ if (order.contract_terms.delivery_date) events.push({
+ when: new Date(order.contract_terms.delivery_date?.t_ms),
+ description: 'delivery',
+ type: 'delivery'
+ })
+ }
order.refund_details.reduce(mergeRefunds, []).forEach(e => {
events.push({
when: new Date(e.timestamp.t_ms),
@@ -306,7 +324,7 @@ function PaidPage({ id, order, onRefund }: { id: string; order: MerchantBackend.
<div class="level-item">
<h1 class="title">
<div class="buttons">
- <span class="has-tooltip-left" data-tooltip={refundable ? i18n`refund order`: i18n`not refundable`}>
+ <span class="has-tooltip-left" data-tooltip={refundable ? i18n`refund order` : i18n`not refundable`}>
<button class="button is-danger" disabled={!refundable} onClick={() => onRefund(id)}><Translate>refund</Translate></button>
</span>
</div>
@@ -342,7 +360,7 @@ function PaidPage({ id, order, onRefund }: { id: string; order: MerchantBackend.
<div class="column is-8" >
<div class="title"><Translate>Payment details</Translate></div>
<FormProvider<Paid> object={value} valueHandler={valueHandler} >
- <InputCurrency<Paid> name="deposit_total" readonly label={i18n`Deposit total`} />
+ {/* <InputCurrency<Paid> name="deposit_total" readonly label={i18n`Deposit total`} /> */}
{order.refunded && <InputCurrency<Paid> name="refund_amount" readonly label={i18n`Refunded amount`} />}
<Input<Paid> name="order_status" readonly label={i18n`Order status`} />
<TextField<Paid> name="order_status_url" label={i18n`Status URL`} >
@@ -438,7 +456,7 @@ export function DetailPage({ id, selected, onRefund, onBack }: Props): VNode {
return <Fragment>
{DetailByStatus()}
{showRefund && <RefundModal
- id={id}
+ order={selected}
onCancel={() => setShowRefund(undefined)}
onConfirm={(value) => {
onRefund(showRefund, value)
diff --git a/packages/frontend/src/paths/instance/orders/details/Timeline.tsx b/packages/frontend/src/paths/instance/orders/details/Timeline.tsx
index 16adbcb..34271a4 100644
--- a/packages/frontend/src/paths/instance/orders/details/Timeline.tsx
+++ b/packages/frontend/src/paths/instance/orders/details/Timeline.tsx
@@ -47,7 +47,7 @@ export function Timeline({ events:e }: Props) {
}
})
return <div class="timeline">
- {state.map((e,i) => {
+ {events.map((e,i) => {
return <div key={i} class="timeline-item">
{(() => {
switch (e.type) {
diff --git a/packages/frontend/src/paths/instance/orders/list/List.stories.tsx b/packages/frontend/src/paths/instance/orders/list/List.stories.tsx
new file mode 100644
index 0000000..e791f20
--- /dev/null
+++ b/packages/frontend/src/paths/instance/orders/list/List.stories.tsx
@@ -0,0 +1,87 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { CardTable as TestedComponent } from './Table';
+
+
+export default {
+ title: 'Pages/Order/List',
+ component: TestedComponent,
+ argTypes: {
+ 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
+}
+
+export const Example = createExample(TestedComponent, {
+ orders: [{
+ id: '123',
+ amount: 'TESTKUDOS:10',
+ paid: false,
+ refundable: true,
+ row_id: 1,
+ summary: 'summary',
+ timestamp: {
+ t_ms: new Date().getTime()
+ },
+ order_id: '123'
+ },{
+ id: '234',
+ amount: 'TESTKUDOS:12',
+ paid: true,
+ refundable: true,
+ row_id: 2,
+ summary: 'summary with long text, very very long text that someone want to add as a description of the order',
+ timestamp: {
+ t_ms: new Date().getTime()
+ },
+ order_id: '234'
+ },{
+ id: '456',
+ amount: 'TESTKUDOS:1',
+ paid: false,
+ refundable: false,
+ row_id: 3,
+ summary: 'summary with long text, very very long text that someone want to add as a description of the order',
+ timestamp: {
+ t_ms: new Date().getTime()
+ },
+ order_id: '456'
+ },{
+ id: '234',
+ amount: 'TESTKUDOS:12',
+ paid: false,
+ refundable: false,
+ row_id: 4,
+ summary: 'summary with long text, very very long text that someone want to add as a description of the order',
+ timestamp: {
+ t_ms: new Date().getTime()
+ },
+ order_id: '234'
+ }]
+});
diff --git a/packages/frontend/src/paths/instance/orders/list/Table.tsx b/packages/frontend/src/paths/instance/orders/list/Table.tsx
index 41c7293..02a5428 100644
--- a/packages/frontend/src/paths/instance/orders/list/Table.tsx
+++ b/packages/frontend/src/paths/instance/orders/list/Table.tsx
@@ -29,7 +29,6 @@ import { InputGroup } from "../../../../components/form/InputGroup";
import { InputSelector } from "../../../../components/form/InputSelector";
import { ConfirmModal } from "../../../../components/modal";
import { MerchantBackend, WithId } from "../../../../declaration";
-import { useOrderDetails } from "../../../../hooks/order";
import { Translate, useTranslator } from "../../../../i18n";
import { RefundSchema as RefundSchema } from "../../../../schemas";
import { mergeRefunds, subtractPrices, sumPrices } from "../../../../utils/amount";
@@ -37,8 +36,8 @@ import { AMOUNT_ZERO_REGEX } from "../../../../utils/constants";
type Entity = MerchantBackend.Orders.OrderHistoryEntry & WithId
interface Props {
- instances: Entity[];
- onRefund: (id: string, value: MerchantBackend.Orders.RefundRequest) => void;
+ orders: Entity[];
+ onRefund: (value: Entity) => void;
onCopyURL: (id: string) => void;
onCreate: () => void;
onSelect: (order: Entity) => void;
@@ -49,11 +48,9 @@ interface Props {
}
-export function CardTable({ instances, onCreate, onRefund, onCopyURL, onSelect, onLoadMoreAfter, onLoadMoreBefore, hasMoreAfter, hasMoreBefore }: Props): VNode {
+export function CardTable({ orders, onCreate, onRefund, onCopyURL, onSelect, onLoadMoreAfter, onLoadMoreBefore, hasMoreAfter, hasMoreBefore }: Props): VNode {
const [rowSelection, rowSelectionHandler] = useState<string[]>([])
- const [showRefund, setShowRefund] = useState<string | undefined>(undefined)
-
const i18n = useTranslator()
return <div class="card has-table">
@@ -74,8 +71,8 @@ export function CardTable({ instances, onCreate, onRefund, onCopyURL, onSelect,
<div class="card-content">
<div class="b-table has-pagination">
<div class="table-wrapper has-mobile-cards">
- {instances.length > 0 ?
- <Table instances={instances} onSelect={onSelect} onRefund={(order) => setShowRefund(order.id)}
+ {orders.length > 0 ?
+ <Table instances={orders} onSelect={onSelect} onRefund={onRefund}
onCopyURL={o => onCopyURL(o.id)}
rowSelection={rowSelection} rowSelectionHandler={rowSelectionHandler}
onLoadMoreAfter={onLoadMoreAfter} onLoadMoreBefore={onLoadMoreBefore}
@@ -86,14 +83,6 @@ export function CardTable({ instances, onCreate, onRefund, onCopyURL, onSelect,
</div>
</div>
</div>
- {showRefund && <RefundModal
- id={showRefund}
- onCancel={() => setShowRefund(undefined)}
- onConfirm={(value) => {
- onRefund(showRefund, value)
- setShowRefund(undefined)
- }}
- />}
</div>
}
interface TableProps {
@@ -160,12 +149,11 @@ function EmptyTable(): VNode {
interface RefundModalProps {
onCancel: () => void;
- id: string;
onConfirm: (value: MerchantBackend.Orders.RefundRequest) => void;
+ order: MerchantBackend.Orders.MerchantOrderStatusResponse
}
-export function RefundModal({ id, onCancel, onConfirm }: RefundModalProps): VNode {
- const result = useOrderDetails(id)
+export function RefundModal({ order, onCancel, onConfirm }: RefundModalProps): VNode {
type State = { mainReason?: string, description?: string, refund?: string }
const [form, setValue] = useState<State>({})
const i18n = useTranslator();
@@ -186,10 +174,10 @@ export function RefundModal({ id, onCancel, onConfirm }: RefundModalProps): VNod
}
}
- const refunds = (result.ok && result.data.order_status === 'paid' ? result.data.refund_details : [])
+ const refunds = (order.order_status === 'paid' ? order.refund_details : [])
.reduce(mergeRefunds, [])
const totalRefunded = refunds.map(r => r.amount).reduce((p, c) => sumPrices(c, p), ':0')
- const orderPrice = (result.ok && result.data.order_status === 'paid' ? result.data.contract_terms.amount : undefined)
+ const orderPrice = (order.order_status === 'paid' ? order.contract_terms.amount : undefined)
const totalRefundable = !orderPrice ? undefined : (refunds.length ? subtractPrices(orderPrice, totalRefunded) : orderPrice)
const isRefundable = totalRefundable && !AMOUNT_ZERO_REGEX.test(totalRefundable)
diff --git a/packages/frontend/src/paths/instance/orders/list/index.tsx b/packages/frontend/src/paths/instance/orders/list/index.tsx
index 8bfe23d..34d156a 100644
--- a/packages/frontend/src/paths/instance/orders/list/index.tsx
+++ b/packages/frontend/src/paths/instance/orders/list/index.tsx
@@ -25,11 +25,12 @@ import { useState } from 'preact/hooks';
import { Loading } from '../../../../components/exception/loading';
import { DatePicker } from '../../../../components/form/DatePicker';
import { NotificationCard } from '../../../../components/menu';
+import { MerchantBackend } from '../../../../declaration';
import { HttpError } from '../../../../hooks/backend';
-import { InstanceOrderFilter, useInstanceOrders, useOrderAPI } from '../../../../hooks/order';
+import { InstanceOrderFilter, useInstanceOrders, useOrderAPI, useOrderDetails } from '../../../../hooks/order';
import { Translate, useTranslator } from '../../../../i18n';
import { Notification } from '../../../../utils/types';
-import { CardTable } from './Table';
+import { CardTable, RefundModal } from './Table';
interface Props {
onUnauthorized: () => VNode;
@@ -43,6 +44,7 @@ interface Props {
export default function ({ onUnauthorized, onLoadError, onCreate, onSelect, onNotFound }: Props): VNode {
const [filter, setFilter] = useState<InstanceOrderFilter>({})
const [pickDate, setPickDate] = useState(false)
+ const [orderToBeRefunded, setOrderToBeRefunded] = useState<MerchantBackend.Orders.OrderHistoryEntry | undefined>(undefined)
const setNewDate = (date: Date) => setFilter(prev => ({ ...prev, date }))
@@ -159,26 +161,76 @@ export default function ({ onUnauthorized, onLoadError, onCreate, onSelect, onNo
dateReceiver={setNewDate}
/>
- <CardTable instances={result.data.orders.map(o => ({ ...o, id: o.order_id }))}
+ <CardTable orders={result.data.orders.map(o => ({ ...o, id: o.order_id }))}
onCreate={onCreate}
onSelect={(order) => onSelect(order.id)}
onCopyURL={(id) => getPaymentURL(id).then((resp) => copyToClipboard(resp.data))}
- onRefund={(id, value) => refundOrder(id, value)
+ onRefund={(value) => setOrderToBeRefunded(value)}
+ onLoadMoreBefore={result.loadMorePrev} hasMoreBefore={!result.isReachingStart}
+ onLoadMoreAfter={result.loadMore} hasMoreAfter={!result.isReachingEnd}
+ />
+ {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({
+ }))
+ .catch((error) => setNotif({
message: i18n`could not create the refund`,
type: "ERROR",
description: error.message
}))
+ .then(() => setOrderToBeRefunded(undefined))
}
- onLoadMoreBefore={result.loadMorePrev} hasMoreBefore={!result.isReachingStart}
- onLoadMoreAfter={result.loadMore} hasMoreAfter={!result.isReachingEnd}
- />
+ 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 />
+ }}
+ />}
</section>
}
+interface RefundProps {
+ id: string;
+ onUnauthorized: () => VNode;
+ onLoadError: (error: HttpError) => VNode;
+ onNotFound: () => VNode;
+ onCancel: () => void;
+ onConfirm: (m: MerchantBackend.Orders.RefundRequest) => void;
+}
+
+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)
+
+ return <RefundModal
+ order={result.data}
+ onCancel={onCancel}
+ onConfirm={onConfirm}
+ />
+}
+
async function copyToClipboard(text: string) {
return navigator.clipboard.writeText(text)
}
diff --git a/packages/frontend/src/paths/instance/products/create/Create.stories.tsx b/packages/frontend/src/paths/instance/products/create/Create.stories.tsx
new file mode 100644
index 0000000..1d9ea53
--- /dev/null
+++ b/packages/frontend/src/paths/instance/products/create/Create.stories.tsx
@@ -0,0 +1,42 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { CreatePage as TestedComponent } from './CreatePage';
+
+
+export default {
+ title: 'Pages/Product/Create',
+ component: TestedComponent,
+ argTypes: {
+ 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
+}
+
+export const Example = createExample(TestedComponent, {
+});
diff --git a/packages/frontend/src/paths/instance/products/list/List.stories.tsx b/packages/frontend/src/paths/instance/products/list/List.stories.tsx
new file mode 100644
index 0000000..beae83b
--- /dev/null
+++ b/packages/frontend/src/paths/instance/products/list/List.stories.tsx
@@ -0,0 +1,58 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { CardTable as TestedComponent } from './Table';
+
+
+export default {
+ title: 'Pages/Product/List',
+ component: TestedComponent,
+ argTypes: {
+ 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
+}
+
+
+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: {}
+ }]
+});
diff --git a/packages/frontend/src/paths/instance/products/update/Update.stories.tsx b/packages/frontend/src/paths/instance/products/update/Update.stories.tsx
new file mode 100644
index 0000000..3a57f7f
--- /dev/null
+++ b/packages/frontend/src/paths/instance/products/update/Update.stories.tsx
@@ -0,0 +1,71 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { UpdatePage as TestedComponent } from './UpdatePage';
+
+
+export default {
+ title: 'Pages/Product/Update',
+ component: TestedComponent,
+ argTypes: {
+ 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
+}
+
+export const WithManagedStock = createExample(TestedComponent, {
+ product: {
+ product_id: '20102-ASDAS-QWE',
+ description: 'description1',
+ description_i18n: {} as any,
+ image: '',
+ price: 'TESTKUDOS:10',
+ taxes: [],
+ total_lost: 10,
+ total_sold: 5,
+ total_stock: 15,
+ unit: 'bar',
+ address: {}
+ }
+});
+
+export const WithInfiniteStock = createExample(TestedComponent, {
+ product: {
+ product_id: '20102-ASDAS-QWE',
+ description: 'description1',
+ description_i18n: {} as any,
+ image: '',
+ price: 'TESTKUDOS:10',
+ taxes: [],
+ total_lost: 10,
+ total_sold: 5,
+ total_stock: -1,
+ unit: 'bar',
+ address: {}
+ }
+});
diff --git a/packages/frontend/src/paths/instance/reserves/create/Create.stories.tsx b/packages/frontend/src/paths/instance/reserves/create/Create.stories.tsx
new file mode 100644
index 0000000..e138770
--- /dev/null
+++ b/packages/frontend/src/paths/instance/reserves/create/Create.stories.tsx
@@ -0,0 +1,42 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { CreatePage as TestedComponent } from './CreatePage';
+
+
+export default {
+ title: 'Pages/Reserve/Create',
+ component: TestedComponent,
+ argTypes: {
+ 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
+}
+
+export const Example = createExample(TestedComponent, {
+});
diff --git a/packages/frontend/src/paths/instance/reserves/details/Details.stories.tsx b/packages/frontend/src/paths/instance/reserves/details/Details.stories.tsx
new file mode 100644
index 0000000..c62442a
--- /dev/null
+++ b/packages/frontend/src/paths/instance/reserves/details/Details.stories.tsx
@@ -0,0 +1,102 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { DetailPage as TestedComponent } from './DetailPage';
+
+
+export default {
+ title: 'Pages/Reserve/Detail',
+ component: TestedComponent,
+ argTypes: {
+ 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
+}
+
+export const Funded = createExample(TestedComponent, {
+ id:'THISISTHERESERVEID',
+ selected: {
+ active: true,
+ committed_amount: 'TESTKUDOS:10',
+ creation_time: {
+ t_ms: new Date().getTime(),
+ },
+ exchange_initial_amount: 'TESTKUDOS:10',
+ expiration_time: {
+ t_ms: new Date().getTime()
+ },
+ merchant_initial_amount: 'TESTKUDOS:10',
+ pickup_amount: 'TESTKUDOS:10',
+ payto_uri: 'payto://x-taler-bank/bank.taler:8080/account',
+ exchange_url: 'http://exchange.taler/',
+ }
+});
+
+export const NotYetFunded = createExample(TestedComponent, {
+ id:'THISISTHERESERVEID',
+ selected: {
+ active: true,
+ committed_amount: 'TESTKUDOS:10',
+ creation_time: {
+ t_ms: new Date().getTime(),
+ },
+ exchange_initial_amount: 'TESTKUDOS:0',
+ expiration_time: {
+ t_ms: new Date().getTime()
+ },
+ merchant_initial_amount: 'TESTKUDOS:10',
+ pickup_amount: 'TESTKUDOS:10',
+ payto_uri: 'payto://x-taler-bank/bank.taler:8080/account',
+ exchange_url: 'http://exchange.taler/',
+ }
+});
+
+export const FundedWithEmptyTips = createExample(TestedComponent, {
+ id:'THISISTHERESERVEID',
+ selected: {
+ active: true,
+ committed_amount: 'TESTKUDOS:10',
+ creation_time: {
+ t_ms: new Date().getTime(),
+ },
+ exchange_initial_amount: 'TESTKUDOS:10',
+ expiration_time: {
+ t_ms: new Date().getTime()
+ },
+ merchant_initial_amount: 'TESTKUDOS:10',
+ pickup_amount: 'TESTKUDOS:10',
+ payto_uri: 'payto://x-taler-bank/bank.taler:8080/account',
+ exchange_url: 'http://exchange.taler/',
+ tips:[{
+ reason: 'asdasd',
+ tip_id: '123',
+ total_amount: 'TESTKUDOS:1'
+ }]
+ }
+});
+
diff --git a/packages/frontend/src/paths/instance/reserves/list/List.stories.tsx b/packages/frontend/src/paths/instance/reserves/list/List.stories.tsx
new file mode 100644
index 0000000..b941066
--- /dev/null
+++ b/packages/frontend/src/paths/instance/reserves/list/List.stories.tsx
@@ -0,0 +1,97 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { CardTable as TestedComponent } from './Table';
+
+
+export default {
+ title: 'Pages/Reserve/List',
+ component: TestedComponent,
+ argTypes: {
+ onCreate: { action: 'onCreate' },
+ onDelete: { action: 'onDelete' },
+ onNewTip: { action: 'onNewTip' },
+ onSelect: { action: 'onSelect' },
+ },
+};
+
+function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
+ const r = (args: any) => <Component {...args} />
+ r.args = props
+ return r
+}
+
+export const AllFunded = createExample(TestedComponent, {
+ instances: [{
+ id: 'reseverId',
+ active: true,
+ committed_amount: 'TESTKUDOS:10',
+ creation_time: {
+ t_ms: new Date().getTime(),
+ },
+ exchange_initial_amount: 'TESTKUDOS:10',
+ expiration_time: {
+ t_ms: new Date().getTime()
+ },
+ merchant_initial_amount: 'TESTKUDOS:10',
+ pickup_amount: 'TESTKUDOS:10',
+ reserve_pub: 'WEQWDASDQWEASDADASDQWEQWEASDAS'
+ },{
+ id: 'reseverId2',
+ active: true,
+ committed_amount: 'TESTKUDOS:13',
+ creation_time: {
+ t_ms: new Date().getTime(),
+ },
+ exchange_initial_amount: 'TESTKUDOS:10',
+ expiration_time: {
+ t_ms: new Date().getTime()
+ },
+ merchant_initial_amount: 'TESTKUDOS:10',
+ pickup_amount: 'TESTKUDOS:10',
+ reserve_pub: 'WEQWDASDQWEASDADASDQWEQWEASDAS'
+ }]
+});
+
+export const Empty = createExample(TestedComponent, {
+ instances: []
+});
+
+
+
+export const OneNotYetFunded = createExample(TestedComponent, {
+ instances: [{
+ id: 'reseverId',
+ active: true,
+ committed_amount: 'TESTKUDOS:0',
+ creation_time: {
+ t_ms: new Date().getTime(),
+ },
+ exchange_initial_amount: 'TESTKUDOS:0',
+ expiration_time: {
+ t_ms: new Date().getTime()
+ },
+ merchant_initial_amount: 'TESTKUDOS:10',
+ pickup_amount: 'TESTKUDOS:10',
+ reserve_pub: 'WEQWDASDQWEASDADASDQWEQWEASDAS'
+ }]
+});
diff --git a/packages/frontend/src/paths/instance/reserves/list/Table.tsx b/packages/frontend/src/paths/instance/reserves/list/Table.tsx
index 6bca85b..243910d 100644
--- a/packages/frontend/src/paths/instance/reserves/list/Table.tsx
+++ b/packages/frontend/src/paths/instance/reserves/list/Table.tsx
@@ -32,7 +32,6 @@ interface Props {
onSelect: (id: Entity) => void;
onDelete: (id: Entity) => void;
onCreate: () => void;
- selected?: boolean;
}
export function CardTable({ instances, onCreate, onSelect, onNewTip, onDelete }: Props): VNode {
diff --git a/packages/frontend/src/paths/instance/transfers/create/Create.stories.tsx b/packages/frontend/src/paths/instance/transfers/create/Create.stories.tsx
new file mode 100644
index 0000000..535cb1e
--- /dev/null
+++ b/packages/frontend/src/paths/instance/transfers/create/Create.stories.tsx
@@ -0,0 +1,43 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { CreatePage as TestedComponent } from './CreatePage';
+
+
+export default {
+ title: 'Pages/Transfer/Create',
+ component: TestedComponent,
+ argTypes: {
+ 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
+}
+
+export const Example = createExample(TestedComponent, {
+ accounts: ['payto://x-taler-bank/account1','payto://x-taler-bank/account2']
+});
diff --git a/packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx b/packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx
index 861268f..f6e3b4f 100644
--- a/packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx
+++ b/packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx
@@ -39,19 +39,17 @@ type Entity = MerchantBackend.Transfers.TransferInformation
interface Props {
onCreate: (d: Entity) => Promise<void>;
onBack?: () => void;
+ accounts: string[],
}
-export function CreatePage({ onCreate, onBack }: Props): VNode {
+export function CreatePage({ accounts, onCreate, onBack }: Props): VNode {
const i18n = useTranslator()
const { currency } = useConfigContext()
- const instance = useInstanceDetails()
- const accounts = !instance.ok ? [] : instance.data.accounts.map(a => a.payto_uri)
-
const [state, setState] = useState<Partial<Entity>>({
wtid: '',
// payto_uri: ,
- exchange_url: 'http://exchange.taler:8081/',
+ // exchange_url: 'http://exchange.taler:8081/',
credit_amount: ``,
});
diff --git a/packages/frontend/src/paths/instance/transfers/create/index.tsx b/packages/frontend/src/paths/instance/transfers/create/index.tsx
index a9e56a5..d95929a 100644
--- a/packages/frontend/src/paths/instance/transfers/create/index.tsx
+++ b/packages/frontend/src/paths/instance/transfers/create/index.tsx
@@ -23,6 +23,7 @@ import { Fragment, h, VNode } from 'preact';
import { useState } from 'preact/hooks';
import { NotificationCard } from '../../../../components/menu';
import { MerchantBackend } from '../../../../declaration';
+import { useInstanceDetails } from '../../../../hooks/instance';
import { useTransferAPI } from '../../../../hooks/transfer';
import { useTranslator } from '../../../../i18n';
import { Notification } from '../../../../utils/types';
@@ -38,11 +39,14 @@ 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({
diff --git a/packages/frontend/src/paths/instance/transfers/list/List.stories.tsx b/packages/frontend/src/paths/instance/transfers/list/List.stories.tsx
new file mode 100644
index 0000000..030ebe2
--- /dev/null
+++ b/packages/frontend/src/paths/instance/transfers/list/List.stories.tsx
@@ -0,0 +1,84 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { CardTable as TestedComponent } from './Table';
+
+
+export default {
+ title: 'Pages/Transfer/List',
+ component: TestedComponent,
+ argTypes: {
+ onCreate: { action: 'onCreate' },
+ onDelete: { action: 'onDelete' },
+ onNewTip: { action: 'onNewTip' },
+ onSelect: { action: 'onSelect' },
+ },
+};
+
+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, {
+ transfers: [{
+ exchange_url: 'http://exchange.url/',
+ id: 'transferid',
+ credit_amount: 'TESTKUDOS:10',
+ payto_uri: 'payto//x-taler-bank/bank:8080/account',
+ transfer_serial_id: 123123123,
+ wtid: '!@KJELQKWEJ!L@K#!J@',
+ confirmed: true,
+ execution_time: {
+ t_ms: new Date().getTime()
+ },
+ verified: false,
+ },{
+ exchange_url: 'http://exchange.url/',
+ id: 'transferid',
+ credit_amount: 'TESTKUDOS:10',
+ payto_uri: 'payto//x-taler-bank/bank:8080/account',
+ transfer_serial_id: 123123123,
+ wtid: '!@KJELQKWEJ!L@K#!J@',
+ confirmed: true,
+ execution_time: {
+ t_ms: new Date().getTime()
+ },
+ verified: false,
+ },{
+ exchange_url: 'http://exchange.url/',
+ id: 'transferid',
+ credit_amount: 'TESTKUDOS:10',
+ payto_uri: 'payto//x-taler-bank/bank:8080/account',
+ transfer_serial_id: 123123123,
+ wtid: '!@KJELQKWEJ!L@K#!J@',
+ confirmed: true,
+ execution_time: {
+ t_ms: new Date().getTime()
+ },
+ verified: false,
+ }]
+});
+export const Empty = createExample(TestedComponent, {
+ transfers: []
+});
diff --git a/packages/frontend/src/paths/instance/transfers/list/Table.tsx b/packages/frontend/src/paths/instance/transfers/list/Table.tsx
index 3794b01..751b14e 100644
--- a/packages/frontend/src/paths/instance/transfers/list/Table.tsx
+++ b/packages/frontend/src/paths/instance/transfers/list/Table.tsx
@@ -29,36 +29,20 @@ import { Actions, buildActions } from "../../../../utils/table"
type Entity = MerchantBackend.Transfers.TransferDetails & WithId
interface Props {
- instances: Entity[];
+ transfers: Entity[];
onUpdate: (id: string) => void;
onDelete: (id: Entity) => void;
onCreate: () => void;
accounts: string[];
- selected?: boolean;
onLoadMoreBefore?: () => void;
hasMoreBefore?: boolean;
hasMoreAfter?: boolean;
onLoadMoreAfter?: () => void;
}
-export function CardTable({ instances, onCreate, onUpdate, onDelete, selected, onLoadMoreAfter, onLoadMoreBefore, hasMoreAfter, hasMoreBefore }: Props): VNode {
- const [actionQueue, actionQueueHandler] = useState<Actions<Entity>[]>([]);
+export function CardTable({ transfers, onCreate, onUpdate, onDelete, onLoadMoreAfter, onLoadMoreBefore, hasMoreAfter, hasMoreBefore }: Props): VNode {
const [rowSelection, rowSelectionHandler] = useState<string[]>([])
- useEffect(() => {
- if (actionQueue.length > 0 && !selected && actionQueue[0].type == 'DELETE') {
- onDelete(actionQueue[0].element)
- actionQueueHandler(actionQueue.slice(1))
- }
- }, [actionQueue, selected, onDelete])
-
- useEffect(() => {
- if (actionQueue.length > 0 && !selected && actionQueue[0].type == 'UPDATE') {
- onUpdate(actionQueue[0].element.id)
- actionQueueHandler(actionQueue.slice(1))
- }
- }, [actionQueue, selected, onUpdate])
-
const i18n = useTranslator()
return <div class="card has-table">
@@ -67,10 +51,6 @@ export function CardTable({ instances, onCreate, onUpdate, onDelete, selected, o
<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>
</div>
<div class="card-header-icon" aria-label="more options">
<span class="has-tooltip-left" data-tooltip={i18n`add new transfer`}>
@@ -84,8 +64,8 @@ export function CardTable({ instances, onCreate, onUpdate, onDelete, selected, o
<div class="card-content">
<div class="b-table has-pagination">
<div class="table-wrapper has-mobile-cards">
- {instances.length > 0 ?
- <Table instances={instances} onUpdate={onUpdate}
+ {transfers.length > 0 ?
+ <Table instances={transfers} onUpdate={onUpdate}
onDelete={onDelete} rowSelection={rowSelection}
rowSelectionHandler={rowSelectionHandler}
onLoadMoreAfter={onLoadMoreAfter} onLoadMoreBefore={onLoadMoreBefore}
diff --git a/packages/frontend/src/paths/instance/transfers/list/index.tsx b/packages/frontend/src/paths/instance/transfers/list/index.tsx
index 5effb5f..7ad5479 100644
--- a/packages/frontend/src/paths/instance/transfers/list/index.tsx
+++ b/packages/frontend/src/paths/instance/transfers/list/index.tsx
@@ -126,7 +126,7 @@ interface ViewProps extends Props {
}
function View({ transfers, onCreate, accounts, onLoadMoreBefore, onLoadMoreAfter }: ViewProps) {
- return <CardTable instances={transfers.map(o => ({ ...o, id: String(o.transfer_serial_id) }))}
+ return <CardTable transfers={transfers.map(o => ({ ...o, id: String(o.transfer_serial_id) }))}
accounts={accounts}
onCreate={onCreate}
onDelete={() => null}
diff --git a/packages/frontend/src/paths/instance/update/Update.stories.tsx b/packages/frontend/src/paths/instance/update/Update.stories.tsx
new file mode 100644
index 0000000..d0c18dd
--- /dev/null
+++ b/packages/frontend/src/paths/instance/update/Update.stories.tsx
@@ -0,0 +1,59 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { UpdatePage as TestedComponent } from './UpdatePage';
+
+
+export default {
+ title: 'Pages/Instance/Update',
+ component: TestedComponent,
+ argTypes: {
+ 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
+}
+
+export const Example = createExample(TestedComponent, {
+ selected: {
+ accounts: [],
+ name: 'name',
+ auth: {method:'external'},
+ address: {},
+ jurisdiction: {},
+ default_max_deposit_fee: 'TESTKUDOS:2',
+ default_max_wire_fee: 'TESTKUDOS:1',
+ default_pay_delay: {
+ d_ms: 1000000,
+ },
+ default_wire_fee_amortization: 1,
+ default_wire_transfer_delay: {
+ d_ms: 100000,
+ },
+ merchant_pub: 'ASDWQEKASJDKSADJ'
+ }
+});