diff options
author | Sebastian <sebasjm@gmail.com> | 2021-06-14 23:59:54 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-06-15 00:04:19 -0300 |
commit | bfe7540cc735567939a6f29e9a055486deaf887f (patch) | |
tree | 125839459a860d1711ae96c25f78101114e3a049 /packages/frontend | |
parent | fee8423fbad552a4e7609cd4f6bc48585054576a (diff) | |
download | merchant-backoffice-bfe7540cc735567939a6f29e9a055486deaf887f.tar.gz merchant-backoffice-bfe7540cc735567939a6f29e9a055486deaf887f.tar.bz2 merchant-backoffice-bfe7540cc735567939a6f29e9a055486deaf887f.zip |
storybook samples
Diffstat (limited to 'packages/frontend')
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' + } +}); |