summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-06-15 10:28:46 -0300
committerSebastian <sebasjm@gmail.com>2021-06-15 10:28:52 -0300
commitd4bce8447350148c24a6ac0717d4d979d6fc0cfe (patch)
tree85197ac82fb3f5475a38c884fd14c632623d7eeb
parente81cf151903bea02c773aa402a91199ae5834401 (diff)
downloadmerchant-backoffice-d4bce8447350148c24a6ac0717d4d979d6fc0cfe.tar.gz
merchant-backoffice-d4bce8447350148c24a6ac0717d4d979d6fc0cfe.tar.bz2
merchant-backoffice-d4bce8447350148c24a6ac0717d4d979d6fc0cfe.zip
add tooltips to the buttons
-rw-r--r--packages/frontend/.storybook/preview.js6
-rw-r--r--packages/frontend/src/components/exception/AsyncButton.tsx1
-rw-r--r--packages/frontend/src/components/form/InputArray.tsx4
-rw-r--r--packages/frontend/src/components/form/InputSecured.tsx7
-rw-r--r--packages/frontend/src/components/form/InputStock.tsx12
-rw-r--r--packages/frontend/src/components/form/InputTaxes.tsx30
-rw-r--r--packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx79
-rw-r--r--packages/frontend/src/components/menu/LangSelector.tsx6
-rw-r--r--packages/frontend/src/components/modal/index.tsx46
-rw-r--r--packages/frontend/src/components/product/ProductList.tsx19
-rw-r--r--packages/frontend/src/context/backend.ts8
-rw-r--r--packages/frontend/src/context/translation.ts10
-rw-r--r--packages/frontend/src/hooks/index.ts4
-rw-r--r--packages/frontend/src/paths/admin/create/CreatePage.tsx8
-rw-r--r--packages/frontend/src/paths/instance/orders/create/Create.stories.tsx2
-rw-r--r--packages/frontend/src/paths/instance/orders/create/CreatePage.tsx8
-rw-r--r--packages/frontend/src/paths/instance/products/create/CreatePage.tsx8
-rw-r--r--packages/frontend/src/paths/instance/products/update/UpdatePage.tsx8
-rw-r--r--packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx10
-rw-r--r--packages/frontend/src/paths/instance/reserves/list/Table.tsx22
-rw-r--r--packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx4
-rw-r--r--packages/frontend/src/paths/instance/transfers/list/Table.tsx10
-rw-r--r--packages/frontend/src/paths/instance/update/UpdatePage.tsx10
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" />