diff options
author | Sebastian <sebasjm@gmail.com> | 2021-06-15 10:28:46 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-06-15 10:28:52 -0300 |
commit | d4bce8447350148c24a6ac0717d4d979d6fc0cfe (patch) | |
tree | 85197ac82fb3f5475a38c884fd14c632623d7eeb | |
parent | e81cf151903bea02c773aa402a91199ae5834401 (diff) | |
download | merchant-backoffice-d4bce8447350148c24a6ac0717d4d979d6fc0cfe.tar.gz merchant-backoffice-d4bce8447350148c24a6ac0717d4d979d6fc0cfe.tar.bz2 merchant-backoffice-d4bce8447350148c24a6ac0717d4d979d6fc0cfe.zip |
add tooltips to the buttons
23 files changed, 200 insertions, 122 deletions
diff --git a/packages/frontend/.storybook/preview.js b/packages/frontend/.storybook/preview.js index ab24c75..d13103a 100644 --- a/packages/frontend/.storybook/preview.js +++ b/packages/frontend/.storybook/preview.js @@ -33,7 +33,7 @@ const mockInstance = { } const mockBackend = { - url: 'http://merchant.url/', + url: 'http://merchant.url', token: 'default-token', triedToLog: false, } @@ -59,7 +59,7 @@ export const globalTypes = { }; export const decorators = [ - (Story, { globals }) => <TranslationProvider initial={globals.locale}> + (Story, { globals }) => <TranslationProvider initial='en' forceLang={globals.locale}> <Story /> </TranslationProvider>, (Story) => <ConfigContextProvider value={mockConfig}> @@ -68,7 +68,7 @@ export const decorators = [ (Story) => <InstanceContextProvider value={mockInstance}> <Story /> </InstanceContextProvider>, - (Story) => <BackendContextProvider value={mockBackend}> + (Story) => <BackendContextProvider defaultUrl={mockBackend.url}> <Story /> </BackendContextProvider>, ]; diff --git a/packages/frontend/src/components/exception/AsyncButton.tsx b/packages/frontend/src/components/exception/AsyncButton.tsx index 3dad3e0..92bab4b 100644 --- a/packages/frontend/src/components/exception/AsyncButton.tsx +++ b/packages/frontend/src/components/exception/AsyncButton.tsx @@ -28,6 +28,7 @@ type Props = { children: ComponentChildren, disabled: boolean; onClick?: () => Promise<void>; + [rest:string]: any, }; export function AsyncButton({ onClick, disabled, children, ...rest }: Props) { diff --git a/packages/frontend/src/components/form/InputArray.tsx b/packages/frontend/src/components/form/InputArray.tsx index 5314eba..13800c7 100644 --- a/packages/frontend/src/components/form/InputArray.tsx +++ b/packages/frontend/src/components/form/InputArray.tsx @@ -65,7 +65,7 @@ export function InputArray<T>({ name, readonly, placeholder, tooltip, label, hel onChange={(e): void => setCurrentValue(e.currentTarget.value)} /> </p> <p class="control"> - <button class="button is-info" disabled={!currentValue} onClick={(): void => { + <button class="button is-info has-tooltip-left" disabled={!currentValue} onClick={(): void => { const v = fromStr(currentValue) if (!isValid(v)) { setLocalError(i18n`The value ${v} is invalid for a payment url`) @@ -74,7 +74,7 @@ export function InputArray<T>({ name, readonly, placeholder, tooltip, label, hel setLocalError(null) onChange([v, ...array] as any); setCurrentValue(''); - }}><Translate>add</Translate></button> + }} data-tooltip={i18n`add element to the list`}><Translate>add</Translate></button> </p> </div> {help} diff --git a/packages/frontend/src/components/form/InputSecured.tsx b/packages/frontend/src/components/form/InputSecured.tsx index 7d8d655..7f871c7 100644 --- a/packages/frontend/src/components/form/InputSecured.tsx +++ b/packages/frontend/src/components/form/InputSecured.tsx @@ -20,7 +20,7 @@ */ import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; -import { Translate } from "../../i18n"; +import { Translate, useTranslator } from "../../i18n"; import { InputProps, useField } from "./useField"; export type Props<T> = InputProps<T>; @@ -41,6 +41,8 @@ export function InputSecured<T>({ name, readonly, placeholder, tooltip, label, h const [active, setActive] = useState(false); const [newValue, setNuewValue] = useState("") + const i18n = useTranslator() + return <Fragment> <div class="field is-horizontal"> <div class="field-label is-normal"> @@ -55,7 +57,8 @@ export function InputSecured<T>({ name, readonly, placeholder, tooltip, label, h {!active ? <Fragment> <div class="field has-addons"> - <button class="button" onClick={(): void => { setActive(!active); }} > + <button class="button" + onClick={(): void => { setActive(!active); }} > <div class="icon is-left"><i class="mdi mdi-lock-reset" /></div> <span><Translate>Manage access token</Translate></span> </button> diff --git a/packages/frontend/src/components/form/InputStock.tsx b/packages/frontend/src/components/form/InputStock.tsx index b323012..a9200cd 100644 --- a/packages/frontend/src/components/form/InputStock.tsx +++ b/packages/frontend/src/components/form/InputStock.tsx @@ -87,9 +87,13 @@ export function InputStock<T>({ name, readonly, placeholder, tooltip, label, hel <div class="field-body is-flex-grow-3"> <div class="field has-addons"> {!alreadyExist ? - <button class="button" onClick={(): void => { valueHandler({ current: 0, lost: 0, sold: 0 } as Stock as any); }} > + <button class="button" + data-tooltip={i18n`click here to configure the stock of the product, leave it as is and the backend will not control stock`} + onClick={(): void => { valueHandler({ current: 0, lost: 0, sold: 0 } as Stock as any); }} > <span><Translate>Manage stock</Translate></span> - </button> : <button class="button" disabled > + </button> : <button class="button" + data-tooltip={i18n`this product has been configured without stock control`} + disabled > <span><Translate>Infinite</Translate></span> </button> } @@ -144,7 +148,9 @@ export function InputStock<T>({ name, readonly, placeholder, tooltip, label, hel </Fragment> : <InputNumber<Entity> name="current" label={i18n`Current`} side={ - <button class="button is-danger" onClick={(): void => { valueHandler(undefined as any) }} > + <button class="button is-danger" + data-tooltip={i18n`remove stock control for this product`} + onClick={(): void => { valueHandler(undefined as any) }} > <span><Translate>without stock</Translate></span> </button> } diff --git a/packages/frontend/src/components/form/InputTaxes.tsx b/packages/frontend/src/components/form/InputTaxes.tsx index af1dddd..91bbf85 100644 --- a/packages/frontend/src/components/form/InputTaxes.tsx +++ b/packages/frontend/src/components/form/InputTaxes.tsx @@ -38,18 +38,21 @@ export function InputTaxes<T>({ name, readonly, label }: Props<keyof T>): VNode const { value: taxes, onChange, } = useField<T>(name); const [value, valueHandler] = useState<Partial<Entity>>({}) - const [errors, setErrors] = useState<FormErrors<Entity>>({}) + // const [errors, setErrors] = useState<FormErrors<Entity>>({}) + + let errors: FormErrors<Entity> = {} + + try { + schema.validateSync(value, { abortEarly: false }) + } catch (err) { + const yupErrors = err.inner as yup.ValidationError[] + errors = yupErrors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, [cur.path]: cur.message }), {}) + } + const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) const submit = useCallback((): void => { - try { - schema.validateSync(value, { abortEarly: false }) - onChange([value as any, ...taxes] as any) - valueHandler({}) - } catch (err) { - const errors = err.inner as yup.ValidationError[] - const pathMessages = errors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, [cur.path]: cur.message }), {}) - setErrors(pathMessages) - } + onChange([value as any, ...taxes] as any) + valueHandler({}) }, [value]) const i18n = useTranslator() @@ -78,10 +81,13 @@ export function InputTaxes<T>({ name, readonly, label }: Props<keyof T>): VNode <Translate>currency and value separated with colon</Translate> <b>USD:2.3</b> </Input> - <Input<Entity> name="name" label={i18n`Name`} /> + <Input<Entity> name="name" label={i18n`Description`} /> <div class="buttons is-right mt-5"> - <button class="button is-info" onClick={submit}><Translate>Add</Translate></button> + <button class="button is-info" + data-tooltip={i18n`add tax to the tax list`} + disabled={hasErrors} + onClick={submit}><Translate>Add</Translate></button> </div> </FormProvider> </InputGroup> diff --git a/packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx b/packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx index 2b98383..2bfbeda 100644 --- a/packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx +++ b/packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx @@ -35,54 +35,51 @@ export function DefaultInstanceFormFields({ readonlyId, showId }: { readonlyId?: const i18n = useTranslator(); const backend = useBackendContext(); return <Fragment> - { showId && <InputWithAddon<Entity> name="id" label={i18n`ID`} addonBefore={`${backend.url}/private/instances/`} readonly={readonlyId} tooltip={i18n`display name identification`} /> } - - <Input<Entity> name="name" label={i18n`Name`} tooltip={i18n`descriptive name`} /> - - <InputPayto<Entity> name="payto_uris" label={i18n`Account address`} help="x-taler-bank/bank.taler:5882/blogger" tooltip={i18n`where the money will be sent`} /> - - <InputCurrency<Entity> name="default_max_deposit_fee" label={i18n`Default max deposit fee`} tooltip={i18n`max deposit fee when an order has not overridden it`} /> - - <InputCurrency<Entity> name="default_max_wire_fee" label={i18n`Default max wire fee`} tooltip={i18n`max wire fee when the order has not overridden it`} /> - - <Input<Entity> name="default_wire_fee_amortization" label={i18n`Default wire fee amortization`} tooltip={i18n`max wire fee amortization when the order has not overridden it`} /> - - <InputGroup name="address" label={i18n`Address`} tooltip={i18n`physical location of merchant`}> + {showId && <InputWithAddon<Entity> name="id" + addonBefore={`${backend.url}/private/instances/`} readonly={readonlyId} + label={i18n`Identifier`} + tooltip={i18n`Name of the instance in URLs. The 'default' instance is special in that it is used to administer other instances.`} /> + } + + <Input<Entity> name="name" + label={i18n`Business name`} + tooltip={i18n`Legal name of the business represented by this instance.`} /> + + <InputPayto<Entity> name="payto_uris" + label={i18n`Bank account URI`} help="x-taler-bank/bank.taler:5882/blogger" + tooltip={i18n`URI specifying bank account for crediting revenue.`} /> + + <InputCurrency<Entity> name="default_max_deposit_fee" + label={i18n`Default max deposit fee`} + tooltip={i18n`Maximum deposit fees this merchant is willing to pay per order by default.`} /> + + <InputCurrency<Entity> name="default_max_wire_fee" + label={i18n`Default max wire fee`} + tooltip={i18n`Maximum wire fees this merchant is willing to pay per wire transfer by default.`} /> + + <Input<Entity> name="default_wire_fee_amortization" + label={i18n`Default wire fee amortization`} + tooltip={i18n`Number of orders excess wire transfer fees will be divided by to compute per order surcharge.`} /> + + <InputGroup name="address" + label={i18n`Address`} + tooltip={i18n`Physical location of the merchant.`}> <InputLocation name="address" /> </InputGroup> - <InputGroup name="jurisdiction" label={i18n`Jurisdiction`} tooltip={i18n`legal location of merchant`}> + <InputGroup name="jurisdiction" + label={i18n`Jurisdiction`} + tooltip={i18n`Jurisdiction for legal disputes with the merchant.`}> <InputLocation name="jurisdiction" /> </InputGroup> - <InputDuration<Entity> name="default_pay_delay" label={i18n`Default payment delay`} tooltip={i18n`max time to pay if the order does not override it`} /> + <InputDuration<Entity> name="default_pay_delay" + label={i18n`Default payment delay`} + tooltip={i18n`Time customers have to pay an order before the offer expires by default.`} /> - <InputDuration<Entity> name="default_wire_transfer_delay" label={i18n`Default wire transfer delay`} tooltip={i18n`min time to wait the transfer if the merchant does not override it`} /> + <InputDuration<Entity> name="default_wire_transfer_delay" + label={i18n`Default wire transfer delay`} + tooltip={i18n`Maximum time an exchange is allowed to delay wiring funds to the merchant, enabling it to aggregate smaller payments into larger wire transfers and reducing wire fees.`} /> </Fragment>; } -/* - <InputWithAddon<Entity> name="id" label={i18n`Identifier`} addonBefore={`${backend.url}/private/instances/`} readonly={!!forceId} tooltip={i18n`Name of the instance in URLs. The 'default' instance is special in that it is used to administer other instances.`} /> - - <Input<Entity> name="name" label={i18n`Business name`} tooltip={i18n`Legal name of the business represented by this instance.`} /> - - <InputPayto<Entity> name="payto_uris" label={i18n`Bank account URI`} help="x-taler-bank/bank.taler:5882/blogger" tooltip={i18n`URI specifying bank account for crediting revenue.`} /> - - <InputCurrency<Entity> name="default_max_deposit_fee" label={i18n`Default max deposit fee`} tooltip={i18n`Maximum deposit fees this merchant is willing to pay per order by default.`} /> - - <InputCurrency<Entity> name="default_max_wire_fee" label={i18n`Default max wire fee`} tooltip={i18n`Maximum wire fees this merchant is willing to pay per wire transfer by default.`} /> - - <Input<Entity> name="default_wire_fee_amortization" label={i18n`Default wire fee amortization`} tooltip={i18n`Number of orders excess wire transfer fees will be divided by to compute per order surcharge.`} /> - - <InputGroup name="address" label={i18n`Address`} tooltip={i18n`Physical location of the merchant.`}> - <InputLocation name="address" /> - </InputGroup> - - <InputGroup name="jurisdiction" label={i18n`Jurisdiction`} tooltip={i18n`Jurisdiction for legal disputes with the merchant.`}> - <InputLocation name="jurisdiction" /> - </InputGroup> - - <InputDuration<Entity> name="default_pay_delay" label={i18n`Default payment delay`} tooltip={i18n`Time customers have to pay an order before the offer expires by default.`} /> - - <InputDuration<Entity> name="default_wire_transfer_delay" label={i18n`Default wire transfer delay`} tooltip={i18n`Maximum time an exchange is allowed to delay wiring funds to the merchant, enabling it to aggregate smaller payments into larger wire transfers and reducing wire fees.`} /> -*/
\ No newline at end of file diff --git a/packages/frontend/src/components/menu/LangSelector.tsx b/packages/frontend/src/components/menu/LangSelector.tsx index bf61bd2..41d08a5 100644 --- a/packages/frontend/src/components/menu/LangSelector.tsx +++ b/packages/frontend/src/components/menu/LangSelector.tsx @@ -46,9 +46,13 @@ function getLangName(s: keyof LangsNames | string) { export function LangSelector(): VNode { const [updatingLang, setUpdatingLang] = useState(false) const { lang, changeLanguage } = useTranslationContext() + return <div class="dropdown is-active "> <div class="dropdown-trigger"> - <button class="button" aria-haspopup="true" aria-controls="dropdown-menu" onClick={() => setUpdatingLang(!updatingLang)}> + <button class="button has-tooltip-left" + data-tooltip="change language selection" + aria-haspopup="true" + aria-controls="dropdown-menu" onClick={() => setUpdatingLang(!updatingLang)}> <div class="icon is-small is-left"> <img src={langIcon} /> </div> diff --git a/packages/frontend/src/components/modal/index.tsx b/packages/frontend/src/components/modal/index.tsx index 9874a19..963dc05 100644 --- a/packages/frontend/src/components/modal/index.tsx +++ b/packages/frontend/src/components/modal/index.tsx @@ -180,25 +180,37 @@ export function SetTokenNewInstanceModal({ onCancel, onClear, onConfirm }: Updat const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) - const text = i18n`You are setting the access token for the new instance` - return <ClearConfirmModal description={text} - onCancel={onCancel} - onConfirm={!hasErrors ? () => onConfirm(form.new_token!) : undefined} - onClear={onClear} - > - <div class="columns"> - <div class="column" /> - <div class="column is-four-fifths" > - <FormProvider errors={errors} object={form} valueHandler={setValue}> - <Input<State> name="new_token" label={i18n`New access token`} tooltip={i18n`next access token to be used`} inputType="password" /> - <Input<State> name="repeat_token" label={i18n`Repeat access token`} tooltip={i18n`confirm the same access token`} inputType="password" /> - </FormProvider> - <p><Translate>Clearing the access token will mean public access to the instance</Translate></p> - </div> - <div class="column" /> + return <div class="modal is-active"> + <div class="modal-background " onClick={onCancel} /> + <div class="modal-card"> + <header class="modal-card-head"> + <p class="modal-card-title">{i18n`You are setting the access token for the new instance`}</p> + <button class="delete " aria-label="close" onClick={onCancel} /> + </header> + <section class="modal-card-body is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths" > + <FormProvider errors={errors} object={form} valueHandler={setValue}> + <Input<State> name="new_token" label={i18n`New access token`} tooltip={i18n`next access token to be used`} inputType="password" /> + <Input<State> name="repeat_token" label={i18n`Repeat access token`} tooltip={i18n`confirm the same access token`} inputType="password" /> + </FormProvider> + <p><Translate>With external authorization method no check will be done by the merchant backend</Translate></p> + </div> + <div class="column" /> + </div> + </section> + <footer class="modal-card-foot"> + {onClear && <button class="button is-danger" onClick={onClear} disabled={onClear === undefined} ><Translate>Set external authorization</Translate></button>} + <div class="buttons is-right" style={{ width: '100%' }}> + <button class="button " onClick={onCancel} ><Translate>Cancel</Translate></button> + <button class="button is-info" onClick={() => onConfirm(form.new_token!)} disabled={hasErrors} ><Translate>Set access token</Translate></button> + </div> + </footer> </div> - </ClearConfirmModal> + <button class="modal-close is-large " aria-label="close" onClick={onCancel} /> + </div> } export function LoadingModal({ onCancel }: { onCancel: () => void }): VNode { diff --git a/packages/frontend/src/components/product/ProductList.tsx b/packages/frontend/src/components/product/ProductList.tsx index 104d8e2..b1486d6 100644 --- a/packages/frontend/src/components/product/ProductList.tsx +++ b/packages/frontend/src/components/product/ProductList.tsx @@ -17,24 +17,27 @@ import { h, VNode } from "preact" import { MerchantBackend } from "../../declaration" import { multiplyPrice } from "../../utils/amount" import emptyImage from "../../assets/empty.png"; +import { Translate, useTranslator } from "../../i18n"; interface Props { list: MerchantBackend.Product[], actions?: { name: string; + tooltip: string; handler: (d: MerchantBackend.Product, index: number) => void; }[] } export function ProductList({ list, actions = [] }: Props): VNode { + const i18n = useTranslator() return <div class="table-container"> <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> <tr> - <th>image</th> - <th>description</th> - <th>quantity</th> - <th>unit price</th> - <th>total price</th> + <th><Translate>image</Translate></th> + <th><Translate>description</Translate></th> + <th><Translate>quantity</Translate></th> + <th><Translate>unit price</Translate></th> + <th><Translate>total price</Translate></th> <th /> </tr> </thead> @@ -51,9 +54,11 @@ export function ProductList({ list, actions = [] }: Props): VNode { <td >{entry.price}</td> <td >{multiplyPrice(entry.price, entry.quantity)}</td> <td class="is-actions-cell right-sticky"> - {actions.map((a,i) => { + {actions.map((a, i) => { return <div key={i} class="buttons is-right"> - <button class="button is-small is-danger jb-modal" type="button" onClick={() => a.handler(entry, index)}> + <button class="button is-small is-danger has-tooltip-left" + data-tooltip={a.tooltip} + type="button" onClick={() => a.handler(entry, index)}> {a.name} </button> </div> diff --git a/packages/frontend/src/context/backend.ts b/packages/frontend/src/context/backend.ts index 9355859..1a7cd72 100644 --- a/packages/frontend/src/context/backend.ts +++ b/packages/frontend/src/context/backend.ts @@ -45,8 +45,8 @@ const BackendContext = createContext<BackendContextType>({ updateToken: () => null, }) -export function useBackendContextState(): BackendContextType { - const [url, triedToLog, changeBackend, resetBackend] = useBackendURL(); +export function useBackendContextState(defaultUrl?:string): BackendContextType { + const [url, triedToLog, changeBackend, resetBackend] = useBackendURL(defaultUrl); const [token, _updateToken] = useBackendDefaultToken(); const updateToken = (t?:string) => { _updateToken(t) @@ -69,8 +69,8 @@ export function useBackendContextState(): BackendContextType { return { url, token, triedToLog, changeBackend, updateToken, resetBackend, clearAllTokens, addTokenCleaner: addTokenCleanerMemo } } -export const BackendContextProvider = ({children}:{children:any}):VNode => { - const value = useBackendContextState() +export const BackendContextProvider = ({children, defaultUrl}:{children:any, defaultUrl?:string}):VNode => { + const value = useBackendContextState(defaultUrl) return h(BackendContext.Provider, {value, children}); } diff --git a/packages/frontend/src/context/translation.ts b/packages/frontend/src/context/translation.ts index 02ccb70..952a1e3 100644 --- a/packages/frontend/src/context/translation.ts +++ b/packages/frontend/src/context/translation.ts @@ -20,7 +20,7 @@ */ import { createContext, h, VNode } from 'preact' -import { useContext } from 'preact/hooks' +import { useContext, useEffect } from 'preact/hooks' import { useLang } from '../hooks' import * as jedLib from "jed"; import { strings } from "../i18n/strings"; @@ -42,10 +42,16 @@ const Context = createContext<Type>(initial) interface Props { initial?: string, children: any, + forceLang?: string } -export const TranslationProvider = ({ initial, children }: Props): VNode => { +export const TranslationProvider = ({ initial, children, forceLang }: Props): VNode => { const [lang, changeLanguage] = useLang(initial) + useEffect(() => { + if (forceLang) { + changeLanguage(forceLang) + } + }) const handler = new jedLib.Jed(strings[lang]); return h(Context.Provider, { value: { lang, handler, changeLanguage }, children }); } diff --git a/packages/frontend/src/hooks/index.ts b/packages/frontend/src/hooks/index.ts index aa8f3a4..3a16de7 100644 --- a/packages/frontend/src/hooks/index.ts +++ b/packages/frontend/src/hooks/index.ts @@ -28,8 +28,8 @@ const calculateRootPath = () => { return rootPath } -export function useBackendURL(): [string, boolean, StateUpdater<string>, () => void] { - const [value, setter] = useNotNullLocalStorage('backend-url', calculateRootPath()) +export function useBackendURL(url?: string): [string, boolean, StateUpdater<string>, () => void] { + const [value, setter] = useNotNullLocalStorage('backend-url', url || calculateRootPath()) const [triedToLog, setTriedToLog] = useLocalStorage('tried-login') const checkedSetter = (v: ValueOrFunction<string>) => { diff --git a/packages/frontend/src/paths/admin/create/CreatePage.tsx b/packages/frontend/src/paths/admin/create/CreatePage.tsx index 82286de..196284e 100644 --- a/packages/frontend/src/paths/admin/create/CreatePage.tsx +++ b/packages/frontend/src/paths/admin/create/CreatePage.tsx @@ -104,7 +104,9 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode { <div class="level"> <div class="level-item has-text-centered"> <h1 class="title"> - <button class="button is-danger" onClick={() => updateIsTokenDialogActive(true)} > + <button class="button is-danger has-tooltip-bottom" + data-tooltip={i18n`change authorization configuration`} + onClick={() => updateIsTokenDialogActive(true)} > <div class="icon is-centered"><i class="mdi mdi-lock-reset" /></div> <span><Translate>Set access token</Translate></span> </button> @@ -127,8 +129,8 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode { <div class="buttons is-right mt-5"> {onBack && <button class="button" onClick={onBack}><Translate>Cancel</Translate></button>} - <AsyncButton onClick={submit} disabled={!isTokenSet && hasErrors} data-tooltip={ - hasErrors ? i18n`Need to complete fields first, check fields marked with a star` : 'confirm operation' + <AsyncButton onClick={submit} disabled={!isTokenSet || hasErrors} data-tooltip={ + hasErrors ? i18n`Need to complete fields marked with a star and choose authorization method` : 'confirm operation' }><Translate>Confirm</Translate></AsyncButton> </div> diff --git a/packages/frontend/src/paths/instance/orders/create/Create.stories.tsx b/packages/frontend/src/paths/instance/orders/create/Create.stories.tsx index 29f6842..daf48c0 100644 --- a/packages/frontend/src/paths/instance/orders/create/Create.stories.tsx +++ b/packages/frontend/src/paths/instance/orders/create/Create.stories.tsx @@ -54,9 +54,11 @@ export const Example = createExample(TestedComponent, { total_stock: -1 },{ id: 't-shirt-2', + price: 'TESTKUDOS:1', description: 'a xl size t-shirt' } as any,{ id: 't-shirt-3', + price: 'TESTKUDOS:1', 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 768a1cb..8cf73a7 100644 --- a/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx +++ b/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx @@ -238,7 +238,9 @@ export function CreatePage({ onCreate, onBack, instanceConfig, instanceInventory {inventoryList.length > 0 && <ProductList list={inventoryList.map(asProduct)} actions={[{ - name: i18n`Remove`, handler: (e) => removeProductFromTheInventoryList(e.product_id!) + name: i18n`Remove`, + tooltip: i18n`remove this product from the list`, + handler: (e) => removeProductFromTheInventoryList(e.product_id!) }]} /> } @@ -259,7 +261,9 @@ export function CreatePage({ onCreate, onBack, instanceConfig, instanceInventory {productList.length > 0 && <ProductList list={productList} actions={[{ - name: i18n`Remove`, handler: (e, index) => { + name: i18n`Remove`, + tooltip: i18n`remove this product from the list to be edited or discarded`, + handler: (e, index) => { removeFromNewProduct(index); setEditingProduct(e); } diff --git a/packages/frontend/src/paths/instance/products/create/CreatePage.tsx b/packages/frontend/src/paths/instance/products/create/CreatePage.tsx index 78c0f0d..2498d6a 100644 --- a/packages/frontend/src/paths/instance/products/create/CreatePage.tsx +++ b/packages/frontend/src/paths/instance/products/create/CreatePage.tsx @@ -24,7 +24,7 @@ import { AsyncButton } from "../../../../components/exception/AsyncButton"; import { ProductForm } from "../../../../components/product/ProductForm"; import { MerchantBackend } from "../../../../declaration"; import { useListener } from "../../../../hooks"; -import { Translate } from "../../../../i18n"; +import { Translate, useTranslator } from "../../../../i18n"; type Entity = MerchantBackend.Products.ProductAddDetail & { product_id: string} @@ -41,6 +41,8 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { return Promise.reject() }) + const i18n = useTranslator() + return <div> <section class="section is-main-section"> <div class="columns"> @@ -50,7 +52,9 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { <div class="buttons is-right mt-5"> {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} - <AsyncButton onClick={submitForm} disabled={!submitForm}><Translate>Confirm</Translate></AsyncButton> + <AsyncButton onClick={submitForm} data-tooltip={ + !submitForm ? i18n`Need to complete fields marked with a star` : 'confirm operation' + } disabled={!submitForm}><Translate>Confirm</Translate></AsyncButton> </div> </div> diff --git a/packages/frontend/src/paths/instance/products/update/UpdatePage.tsx b/packages/frontend/src/paths/instance/products/update/UpdatePage.tsx index 1dfca99..0c665be 100644 --- a/packages/frontend/src/paths/instance/products/update/UpdatePage.tsx +++ b/packages/frontend/src/paths/instance/products/update/UpdatePage.tsx @@ -24,7 +24,7 @@ import { AsyncButton } from "../../../../components/exception/AsyncButton"; import { ProductForm } from "../../../../components/product/ProductForm"; import { MerchantBackend, WithId } from "../../../../declaration"; import { useListener } from "../../../../hooks"; -import { Translate } from "../../../../i18n"; +import { Translate, useTranslator } from "../../../../i18n"; type Entity = MerchantBackend.Products.ProductDetail & { product_id: string } @@ -40,6 +40,8 @@ export function UpdatePage({ product, onUpdate, onBack }: Props): VNode { return Promise.resolve() }) + const i18n = useTranslator() + return <div> <section class="section"> <section class="hero is-hero-bar"> @@ -63,7 +65,9 @@ export function UpdatePage({ product, onUpdate, onBack }: Props): VNode { <div class="buttons is-right mt-5"> {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} - <AsyncButton onClick={submitForm} disabled={!submitForm}><Translate>Confirm</Translate></AsyncButton> + <AsyncButton onClick={submitForm} data-tooltip={ + !submitForm ? i18n`Need to complete fields marked with a star` : 'confirm operation' + } disabled={!submitForm}><Translate>Confirm</Translate></AsyncButton> </div> </div> <div class="column" /> diff --git a/packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx b/packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx index cdaf475..ff7b334 100644 --- a/packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx +++ b/packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx @@ -79,7 +79,7 @@ function ViewStep({ step, setCurrentStep, reserve, onBack, submitForm, setReserv <div class="buttons is-right mt-5"> {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} - <AsyncButton onClick={() => { + <AsyncButton class="has-tooltip-left" onClick={() => { return request<ExchangeBackend.WireResponse>(`${reserve.exchange_url}wire`).then(r => { const wireMethods = r.data.accounts.map(a => { const match = PAYTO_WIRE_METHOD_LOOKUP.exec(a.payto_uri) @@ -91,7 +91,9 @@ function ViewStep({ step, setCurrentStep, reserve, onBack, submitForm, setReserv }).catch((r: any) => { setExchangeQueryError(r.message) }) - }} disabled={hasErrors} ><Translate>Next</Translate></AsyncButton> + }} data-tooltip={ + hasErrors ? i18n`Need to complete fields marked with a star` : 'confirm operation' + } disabled={hasErrors} ><Translate>Next</Translate></AsyncButton> </div> </Fragment> } @@ -110,7 +112,9 @@ function ViewStep({ step, setCurrentStep, reserve, onBack, submitForm, setReserv </FormProvider> <div class="buttons is-right mt-5"> {onBack && <button class="button" onClick={() => setCurrentStep(Steps.EXCHANGE)} ><Translate>Back</Translate></button>} - <AsyncButton onClick={submitForm} disabled={hasErrors} ><Translate>Confirm</Translate></AsyncButton> + <AsyncButton onClick={submitForm} data-tooltip={ + hasErrors ? i18n`Need to complete fields marked with a star` : 'confirm operation' + } disabled={hasErrors} ><Translate>Confirm</Translate></AsyncButton> </div> </Fragment> diff --git a/packages/frontend/src/paths/instance/reserves/list/Table.tsx b/packages/frontend/src/paths/instance/reserves/list/Table.tsx index 243910d..5977b8b 100644 --- a/packages/frontend/src/paths/instance/reserves/list/Table.tsx +++ b/packages/frontend/src/paths/instance/reserves/list/Table.tsx @@ -69,7 +69,7 @@ export function CardTable({ instances, onCreate, onSelect, onNewTip, onDelete }: <div class="card-header-icon" aria-label="more options"> <span class="has-tooltip-left" data-tooltip={i18n`add new reserve`}> - <button class="button is-info" type="button" onClick={onCreate}> + <button class="button is-info" type="button" onClick={onCreate} > <span class="icon is-small" ><i class="mdi mdi-plus mdi-36px" /></span> </button> </span> @@ -96,6 +96,7 @@ interface TableProps { } function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode { + const i18n = useTranslator() return ( <div class="table-container"> <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> @@ -119,12 +120,16 @@ function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode { <td onClick={(): void => onSelect(i)} style={{ cursor: 'pointer' }} >{i.committed_amount}</td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <button class="button is-small is-danger jb-modal" type="button" onClick={(): void => onDelete(i)}> + <button class="button is-small is-danger" + data-tooltip={i18n`delete selected reserve from the database`} + type="button" onClick={(): void => onDelete(i)}> Delete - </button> - <button class="button is-small is-info jb-modal" type="button" onClick={(): void => onNewTip(i)}> + </button> + <button class="button is-small is-info" + data-tooltip={i18n`authorize new tip from selected reserve`} + type="button" onClick={(): void => onNewTip(i)}> New Tip - </button> + </button> </div> </td> </tr> @@ -144,6 +149,7 @@ function EmptyTable(): VNode { } function TableWithoutFund({ instances, onSelect, onDelete }: TableProps): VNode { + const i18n = useTranslator() return ( <div class="table-container"> <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> @@ -163,9 +169,11 @@ function TableWithoutFund({ instances, onSelect, onDelete }: TableProps): VNode <td onClick={(): void => onSelect(i)} style={{ cursor: 'pointer' }} >{i.merchant_initial_amount}</td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <button class="button is-small is-danger jb-modal" type="button" onClick={(): void => onDelete(i)}> + <button class="button is-small is-danger jb-modal" type="button" + data-tooltip={i18n`delete selected reserve from the database`} + onClick={(): void => onDelete(i)}> Delete - </button> + </button> </div> </td> </tr> diff --git a/packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx b/packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx index f6e3b4f..cab3b9f 100644 --- a/packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx +++ b/packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx @@ -94,7 +94,9 @@ export function CreatePage({ accounts, onCreate, onBack }: Props): VNode { <div class="buttons is-right mt-5"> {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} - <AsyncButton disabled={hasErrors} onClick={submitForm} ><Translate>Confirm</Translate></AsyncButton> + <AsyncButton disabled={hasErrors} data-tooltip={ + hasErrors ? i18n`Need to complete fields marked with a star` : 'confirm operation' + } onClick={submitForm} ><Translate>Confirm</Translate></AsyncButton> </div> </div> diff --git a/packages/frontend/src/paths/instance/transfers/list/Table.tsx b/packages/frontend/src/paths/instance/transfers/list/Table.tsx index 128e71f..f1a9c7e 100644 --- a/packages/frontend/src/paths/instance/transfers/list/Table.tsx +++ b/packages/frontend/src/paths/instance/transfers/list/Table.tsx @@ -94,7 +94,9 @@ function Table({ instances, onLoadMoreAfter, onDelete, onLoadMoreBefore, hasMore const i18n = useTranslator() return ( <div class="table-container"> - {onLoadMoreBefore && <button class="button is-fullwidth" disabled={!hasMoreBefore} onClick={onLoadMoreBefore}><Translate>load newer transfers</Translate></button>} + {onLoadMoreBefore && <button class="button is-fullwidth" + data-tooltip={i18n`load more transfers before the first one`} + disabled={!hasMoreBefore} onClick={onLoadMoreBefore}><Translate>load newer transfers</Translate></button>} <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> <tr> @@ -119,13 +121,15 @@ function Table({ instances, onLoadMoreAfter, onDelete, onLoadMoreBefore, hasMore <td>{i.verified ? i18n`yes` : i18n`no`}</td> <td>{i.execution_time ? (i.execution_time.t_ms == 'never' ? i18n`never` : format(i.execution_time.t_ms, 'yyyy/MM/dd HH:mm:ss')) : i18n`unknown`}</td> <td> - {i.verified === undefined ? <button class="button is-danger is-small" onClick={() => onDelete(i)}>Delete</button> : undefined} + {i.verified === undefined ? <button class="button is-danger is-small has-tooltip-left" + data-tooltip={i18n`delete selected transfer from the database`} + onClick={() => onDelete(i)}>Delete</button> : undefined} </td> </tr> })} </tbody> </table> - {onLoadMoreAfter && <button class="button is-fullwidth" disabled={!hasMoreAfter} onClick={onLoadMoreAfter}><Translate>load older transfers</Translate></button>} + {onLoadMoreAfter && <button class="button is-fullwidth" data-tooltip={i18n`load more transfer after the last one`} disabled={!hasMoreAfter} onClick={onLoadMoreAfter}><Translate>load older transfers</Translate></button>} </div>) } diff --git a/packages/frontend/src/paths/instance/update/UpdatePage.tsx b/packages/frontend/src/paths/instance/update/UpdatePage.tsx index 5367e5d..7612d6f 100644 --- a/packages/frontend/src/paths/instance/update/UpdatePage.tsx +++ b/packages/frontend/src/paths/instance/update/UpdatePage.tsx @@ -95,12 +95,12 @@ export function UpdatePage({ onUpdate, onChangeAuth, selected, onBack }: Props): } const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) const submit = async (): Promise<void> => { - schema.validateSync(value, { abortEarly: false }) await onUpdate(schema.cast(value)); await onBack() return Promise.resolve() } const [active, setActive] = useState(false); + const i18n = useTranslator() return <div> <section class="section"> @@ -117,7 +117,9 @@ export function UpdatePage({ onUpdate, onChangeAuth, selected, onBack }: Props): <div class="level-right"> <div class="level-item"> <h1 class="title"> - <button class="button is-danger" onClick={(): void => { setActive(!active); }} > + <button class="button is-danger" + data-tooltip={i18n`Change the authorization method use for this instance.`} + onClick={(): void => { setActive(!active); }} > <div class="icon is-left"><i class="mdi mdi-lock-reset" /></div> <span><Translate>Manage access token</Translate></span> </button> @@ -154,7 +156,9 @@ export function UpdatePage({ onUpdate, onChangeAuth, selected, onBack }: Props): <div class="buttons is-right mt-4"> <button class="button" onClick={onBack} data-tooltip="cancel operation"><Translate>Cancel</Translate></button> - <AsyncButton onClick={submit} disabled={hasErrors} ><Translate>Confirm</Translate></AsyncButton> + <AsyncButton onClick={submit} data-tooltip={ + hasErrors ? i18n`Need to complete fields marked with a star` : 'confirm operation' + } disabled={hasErrors} ><Translate>Confirm</Translate></AsyncButton> </div> </div> <div class="column" /> |