commit 112b0dc323371d11360c7ee3cf6655b0bd66c8d3 parent e224380d3fa7bde5453ca80ab87be0202d800cc3 Author: Sebastian <sebasjm@taler-systems.com> Date: Fri, 27 Feb 2026 09:32:22 -0300 fix #11007 Diffstat:
84 files changed, 1407 insertions(+), 1504 deletions(-)
diff --git a/packages/merchant-backoffice-ui/src/components/Tooltip.tsx b/packages/merchant-backoffice-ui/src/components/Tooltip.tsx @@ -0,0 +1,86 @@ +/* + This file is part of GNU Taler + (C) 2021-2024 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 { TranslatedString } from "@gnu-taler/taler-util"; +import { ComponentChildren, h, VNode } from "preact"; +import { useEffect, useRef } from "preact/hooks"; + +/** + * Css tooltip finds the way to be outside of the screen. + * + * This js implementation adds the --pos-x css variable that + * can be used with the translate css-function to move the + * tooltip to the left but not too much and not too little. + * + * transform: translateX(var(--pos-x)); + * + * Assumed tooltip size 200px + * + * @param param0 + * @returns + */ +export function Tooltip({ + text, + children, +}: { + text: TranslatedString; + children: ComponentChildren; +}): VNode { + const iRef = useRef<HTMLElement>(null); + const screenPadding = 8; + const TOOLTIP_CSS_WIDTH = 200; // as defined in css + const desiredMoveLeft = TOOLTIP_CSS_WIDTH / 2; + + useEffect(() => { + const el = iRef.current; + if (!el) return; + const maxWidth = window.innerWidth; + + const { x } = el.getBoundingClientRect(); + + const maxMoveLeft = x - screenPadding; + const minMoveLeft = maxWidth - screenPadding - x; + + const leftOverflow = Math.max(desiredMoveLeft - maxMoveLeft, 0); + const rightOverflow = Math.max(desiredMoveLeft - minMoveLeft, 0); + + const xPos = desiredMoveLeft - leftOverflow + rightOverflow; + + el.style.setProperty("--pos-x", `-${xPos}px`); + // dev mode + el.style.setProperty("--debug-max-move-left", String(maxMoveLeft)); + el.style.setProperty("--debug-min-move-left", String(minMoveLeft)); + el.style.setProperty("--debug-left-overflow", String(leftOverflow)); + el.style.setProperty("--debug-right-overflow", String(rightOverflow)); + + el.style.setProperty("--debug-x", String(x)); + el.style.setProperty("--debug-width", String(maxWidth)); + }); + return ( + <span + ref={iRef} + class="has-tooltip-custom " + onTouchStart={() => {}} + data-tooltip={text} + > + {children} + </span> + ); +} diff --git a/packages/merchant-backoffice-ui/src/components/form/Input.tsx b/packages/merchant-backoffice-ui/src/components/form/Input.tsx @@ -19,7 +19,8 @@ * @author Sebastian Javier Marchano (sebasjm) */ import { ComponentChildren, h, VNode } from "preact"; -import { useField, InputProps } from "./useField.js"; +import { InputProps, useField } from "./useField.js"; +import { Tooltip } from "../Tooltip.js"; interface Props<T> extends InputProps<T> { inputType?: "text" | "number" | "multiline" | "password"; @@ -92,13 +93,9 @@ export function Input<T>({ </span> )} {tooltip && ( - <span - class="icon has-tooltip-right" - onTouchStart={() => {}} - data-tooltip={tooltip} - > - <i class="mdi mdi-information" /> - </span> + <Tooltip text={tooltip}> + <i class="icon mdi mdi-information" /> + </Tooltip> )} </label> </div> diff --git a/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx b/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx @@ -19,10 +19,11 @@ * @author Sebastian Javier Marchano (sebasjm) */ import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { Fragment, h, VNode } from "preact"; +import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { InputProps, useField } from "./useField.js"; import { DropdownList } from "./InputSearchOnList.js"; +import { Tooltip } from "../Tooltip.js"; const TALER_SCREEN_ID = 6; @@ -73,9 +74,9 @@ export function InputArray<T>({ </span> )} {tooltip && ( - <span class="icon has-tooltip-right" onTouchStart={() => {}} data-tooltip={tooltip} > - <i class="mdi mdi-information" /> - </span> + <Tooltip text={tooltip}> + <i class="icon mdi mdi-information" /> + </Tooltip> )} </label> </div> @@ -109,21 +110,22 @@ export function InputArray<T>({ </p> {getSuggestion ? undefined : ( <p class="control"> - <button - type="button" - class="button is-info has-tooltip-left" - disabled={!currentValue} - onClick={(): void => { - const v = fromStr(currentValue); - if (!unique || array.indexOf(v) === -1) { - onChange([v, ...array] as T[keyof T]); - } - setCurrentValue(""); - }} - onTouchStart={() => {}} data-tooltip={i18n.str`Add element to the list`} - > - <i18n.Translate>Add</i18n.Translate> - </button> + <Tooltip text={i18n.str`Add element to the list`}> + <button + type="button" + class="button is-info" + disabled={!currentValue} + onClick={(): void => { + const v = fromStr(currentValue); + if (!unique || array.indexOf(v) === -1) { + onChange([v, ...array] as T[keyof T]); + } + setCurrentValue(""); + }} + > + <i18n.Translate>Add</i18n.Translate> + </button> + </Tooltip> </p> )} </div> diff --git a/packages/merchant-backoffice-ui/src/components/form/InputBoolean.tsx b/packages/merchant-backoffice-ui/src/components/form/InputBoolean.tsx @@ -20,6 +20,7 @@ */ import { h, VNode } from "preact"; import { InputProps, useField } from "./useField.js"; +import { Tooltip } from "../Tooltip.js"; interface Props<T> extends InputProps<T> { name: T; @@ -58,11 +59,7 @@ export function InputBoolean<T>({ <div class="field-label is-normal"> <label class="label"> {label} - {tooltip && ( - <span class="icon has-tooltip-right" onTouchStart={() => {}} data-tooltip={tooltip} > - <i class="mdi mdi-information" /> - </span> - )} + {tooltip && <Tooltip text={tooltip} ><i class="icon mdi mdi-information" /></Tooltip>} </label> </div> <div class="field-body is-flex-grow-3"> diff --git a/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx b/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx @@ -29,6 +29,7 @@ import { datetimeFormatForPreferences, usePreference, } from "../../hooks/preference.js"; +import { Tooltip } from "../Tooltip.js"; const TALER_SCREEN_ID = 7; @@ -65,7 +66,7 @@ export function InputDate<T>({ strValue = ""; } else if (value instanceof Date) { // FIXME: how this can be possible? remove it! - throw Error("date not allowed, file a bug.") + throw Error("date not allowed, file a bug."); } else if (value.t_s) { strValue = value.t_s === "never" @@ -87,9 +88,9 @@ export function InputDate<T>({ </span> )} {tooltip && ( - <span class="icon has-tooltip-right" onTouchStart={() => {}} data-tooltip={tooltip} > - <i class="mdi mdi-information" /> - </span> + <Tooltip text={tooltip}> + <i class="icon mdi mdi-information" /> + </Tooltip> )} </label> </div> @@ -122,9 +123,7 @@ export function InputDate<T>({ }} > <a class="button is-static"> - <span class="icon"> - <i class="mdi mdi-calendar" /> - </span> + <i class="icon mdi mdi-calendar" /> </a> </div> </div> @@ -136,7 +135,7 @@ export function InputDate<T>({ </div> {!readonly && !withoutClear && ( - <span onTouchStart={() => {}} data-tooltip={i18n.str`Change value to empty`}> + <Tooltip text={i18n.str`Change value to empty`}> <button type="button" class="button is-info mr-3" @@ -144,10 +143,10 @@ export function InputDate<T>({ > <i18n.Translate>Clear</i18n.Translate> </button> - </span> + </Tooltip> )} {withNever && ( - <span onTouchStart={() => {}} data-tooltip={i18n.str`Change value to never`}> + <Tooltip text={i18n.str`Change value to never`}> <button type="button" class="button is-info" @@ -155,7 +154,7 @@ export function InputDate<T>({ > <i18n.Translate>Never</i18n.Translate> </button> - </span> + </Tooltip> )} {side} </div> diff --git a/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx @@ -18,7 +18,12 @@ * * @author Sebastian Javier Marchano (sebasjm) */ -import { Duration, InternationalizationAPI, TalerProtocolDuration, TranslatedString } from "@gnu-taler/taler-util"; +import { + Duration, + InternationalizationAPI, + TalerProtocolDuration, + TranslatedString, +} from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { formatDuration, intervalToDuration } from "date-fns"; import { ComponentChildren, h, VNode } from "preact"; @@ -26,6 +31,7 @@ import { useState } from "preact/hooks"; import { SimpleModal } from "../modal/index.js"; import { DurationPicker } from "../picker/DurationPicker.js"; import { InputProps, useField } from "./useField.js"; +import { Tooltip } from "../Tooltip.js"; const TALER_SCREEN_ID = 8; @@ -38,49 +44,49 @@ export interface Props<T> extends InputProps<T> { useProtocolDuration?: boolean; } -export function durationToString(i18n:InternationalizationAPI, d: Duration | undefined): TranslatedString { - if (!d) return "" as TranslatedString - if (d.d_ms === "forever") return i18n.str`Forever` - if (d.d_ms === undefined) { - throw Error( - `assertion error: duration should have a d_ms but got '${JSON.stringify( - d, - )}'`, - ); - } - return formatDuration( - intervalToDuration({ start: 0, end: d.d_ms }), - { - locale: { - formatDistance: (name, value) => { - switch (name) { - case "xMonths": - return i18n.str`${value}M`; - case "xYears": - return i18n.str`${value}Y`; - case "xDays": - return i18n.str`${value}d`; - case "xHours": - return i18n.str`${value}h`; - case "xMinutes": - return i18n.str`${value}min`; - case "xSeconds": - return i18n.str`${value}sec`; - default: - throw Error("not reached"); - } - }, - localize: { - day: () => "s", - month: () => "m", - ordinalNumber: () => "th", - dayPeriod: () => "p", - quarter: () => "w", - era: () => "e", - }, - }, +export function durationToString( + i18n: InternationalizationAPI, + d: Duration | undefined, +): TranslatedString { + if (!d) return "" as TranslatedString; + if (d.d_ms === "forever") return i18n.str`Forever`; + if (d.d_ms === undefined) { + throw Error( + `assertion error: duration should have a d_ms but got '${JSON.stringify( + d, + )}'`, + ); + } + return formatDuration(intervalToDuration({ start: 0, end: d.d_ms }), { + locale: { + formatDistance: (name, value) => { + switch (name) { + case "xMonths": + return i18n.str`${value}M`; + case "xYears": + return i18n.str`${value}Y`; + case "xDays": + return i18n.str`${value}d`; + case "xHours": + return i18n.str`${value}h`; + case "xMinutes": + return i18n.str`${value}min`; + case "xSeconds": + return i18n.str`${value}sec`; + default: + throw Error("not reached"); + } }, - ) as TranslatedString + localize: { + day: () => "s", + month: () => "m", + ordinalNumber: () => "th", + dayPeriod: () => "p", + quarter: () => "w", + era: () => "e", + }, + }, + }) as TranslatedString; } export function InputDuration<T>({ @@ -104,7 +110,7 @@ export function InputDuration<T>({ anyValue && anyValue.d_us !== undefined ? Duration.fromTalerProtocolDuration(anyValue) : anyValue; - const strValue = durationToString(i18n, value) + const strValue = durationToString(i18n, value); return ( <div class="field is-horizontal"> @@ -117,9 +123,9 @@ export function InputDuration<T>({ </span> )} {tooltip && ( - <span class="icon" data-tooltip={tooltip} onTouchStart={() => {}}> - <i class="mdi mdi-information" /> - </span> + <Tooltip text={tooltip}> + <i class="icon mdi mdi-information" /> + </Tooltip> )} </label> </div> @@ -146,9 +152,7 @@ export function InputDuration<T>({ }} > <a class="button is-static"> - <span class="icon"> - <i class="mdi mdi-clock" /> - </span> + <i class="icon mdi mdi-clock" /> </a> </div> </div> @@ -161,7 +165,7 @@ export function InputDuration<T>({ </div> {withForever && ( - <span onTouchStart={() => {}} data-tooltip={i18n.str`Change the value to never`}> + <Tooltip text={i18n.str`Change the value to never`}> <button type="button" class="button is-info mr-3" @@ -169,10 +173,10 @@ export function InputDuration<T>({ > <i18n.Translate>Forever</i18n.Translate> </button> - </span> + </Tooltip> )} {!readonly && !withoutClear && ( - <span onTouchStart={() => {}} data-tooltip={i18n.str`Change the value to empty`}> + <Tooltip text={i18n.str`Change the value to empty`}> <button type="button" class="button is-info mr-3" @@ -180,7 +184,7 @@ export function InputDuration<T>({ > <i18n.Translate>Clear</i18n.Translate> </button> - </span> + </Tooltip> )} {side} </div> diff --git a/packages/merchant-backoffice-ui/src/components/form/InputGroup.tsx b/packages/merchant-backoffice-ui/src/components/form/InputGroup.tsx @@ -21,12 +21,14 @@ import { ComponentChildren, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { useGroupField } from "./useGroupField.js"; +import { Tooltip } from "../Tooltip.js"; +import { TranslatedString } from "@gnu-taler/taler-util"; export interface Props<T> { name: T; children: ComponentChildren; label: ComponentChildren; - tooltip?: ComponentChildren; + tooltip?: TranslatedString; alternative?: ComponentChildren; fixed?: boolean; initialActive?: boolean; @@ -46,20 +48,20 @@ export function InputGroup<T>({ return ( <div class="card"> - <header class="card-header" style={{cursor:"pointer"}} onClick={(): void => setActive(!active)}> + <header + class="card-header" + style={{ cursor: "pointer" }} + onClick={(): void => setActive(!active)} + > <p class="card-header-title"> {label} {tooltip && ( - <span class="icon has-tooltip-right" onTouchStart={() => {}} data-tooltip={tooltip}> - <i class="mdi mdi-information" /> - </span> + <Tooltip text={tooltip}> + <i class="icon mdi mdi-information" /> + </Tooltip> )} {group?.hasError && ( - <span - class="has-text-danger" - onTouchStart={() => {}} data-tooltip={tooltip} - style={{ marginLeft: 5 }} - > + <span class="has-text-danger" style={{ marginLeft: 5 }}> * </span> )} @@ -71,13 +73,11 @@ export function InputGroup<T>({ aria-label="more options" onClick={(): void => setActive(!active)} > - <span class="icon"> - {active ? ( - <i class="mdi mdi-arrow-up" /> - ) : ( - <i class="mdi mdi-arrow-down" /> - )} - </span> + {active ? ( + <i class="icon mdi mdi-arrow-up" /> + ) : ( + <i class="icon mdi mdi-arrow-down" /> + )} </button> )} </header> diff --git a/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx b/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx @@ -23,6 +23,7 @@ import { ComponentChildren, h, VNode } from "preact"; import { useRef, useState } from "preact/hooks"; import { MAX_IMAGE_SIZE as MAX_IMAGE_UPLOAD_SIZE } from "../../utils/constants.js"; import { InputProps, useField } from "./useField.js"; +import { Tooltip } from "../Tooltip.js"; const TALER_SCREEN_ID = 9; @@ -54,9 +55,9 @@ export function InputImage<T>({ <label class="label"> {label} {tooltip && ( - <span class="icon has-tooltip-right" onTouchStart={() => {}} data-tooltip={tooltip}> - <i class="mdi mdi-information" /> - </span> + <Tooltip text={tooltip}> + <i class="icon mdi mdi-information" /> + </Tooltip> )} </label> </div> @@ -84,12 +85,12 @@ export function InputImage<T>({ } if (fileList[0].size > MAX_IMAGE_UPLOAD_SIZE) { setSizeError(true); - normalizeImagesize(fileList[0], onChange as any) + normalizeImagesize(fileList[0], onChange as any); // return onChange(undefined!); return; } setSizeError(false); - onChange(toDataSrc(fileList[0]) as T[keyof T]) + onChange(toDataSrc(fileList[0]) as T[keyof T]); }} /> {help} @@ -102,11 +103,17 @@ export function InputImage<T>({ )} {sizeError && ( <p class="help" style={{ fontSize: 16 }}> - <i18n.Translate>The image was normalized to be smaller than 1 MB</i18n.Translate> + <i18n.Translate> + The image was normalized to be smaller than 1 MB + </i18n.Translate> </p> )} {!value && ( - <button class="button" type="button" onClick={() => image.current?.click()}> + <button + class="button" + type="button" + onClick={() => image.current?.click()} + > <i18n.Translate>Add</i18n.Translate> </button> )} @@ -119,7 +126,7 @@ export function InputImage<T>({ image.current.value = ""; } onChange(undefined!); - setSizeError(false) + setSizeError(false); }} > <i18n.Translate>Remove</i18n.Translate> @@ -139,19 +146,19 @@ async function toDataSrc(imageFile: File): Promise<string> { "", ), ); - return (`data:${imageFile.type};base64,${b64}`); - }) + return `data:${imageFile.type};base64,${b64}`; + }); } const IMAGE_MAX_WIDTH = 800; const IMAGE_MAX_HEIGHT = 800; function normalizeImagesize(imageFile: File, onComplete: (d: string) => void) { - toDataSrc(imageFile).then(imageSource => { + toDataSrc(imageFile).then((imageSource) => { const img = new Image(); img.src = imageSource; img.onload = function () { - const canvas = document.createElement('canvas') as HTMLCanvasElement; - const ctx = canvas.getContext('2d')!; + const canvas = document.createElement("canvas") as HTMLCanvasElement; + const ctx = canvas.getContext("2d")!; let width = img.width; let height = img.height; @@ -169,7 +176,7 @@ function normalizeImagesize(imageFile: File, onComplete: (d: string) => void) { canvas.width = width; canvas.height = height; ctx.drawImage(img, 0, 0, width, height); - onComplete(canvas.toDataURL()) + onComplete(canvas.toDataURL()); }; - }) + }); } diff --git a/packages/merchant-backoffice-ui/src/components/form/InputPassword.tsx b/packages/merchant-backoffice-ui/src/components/form/InputPassword.tsx @@ -56,9 +56,11 @@ export function InputPassword<T>({ setHide((h) => !h); }} addonAfter={ - <span class="icon"> - {hide ? <i class="mdi mdi-eye-off" /> : <i class="mdi mdi-eye" />} - </span> + hide ? ( + <i class="icon mdi mdi-eye-off" /> + ) : ( + <i class="icon mdi mdi-eye" /> + ) } inputType={hide ? "password" : "text"} expand={expand} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputSearchOnList.tsx b/packages/merchant-backoffice-ui/src/components/form/InputSearchOnList.tsx @@ -107,11 +107,7 @@ export function InputSearchOnList<T extends Entity>({ name="name" label={label} tooltip={i18n.str`Enter description or id`} - addonAfter={ - <span class="icon"> - <i class="mdi mdi-magnify" /> - </span> - } + addonAfter={<i class="icon mdi mdi-magnify" />} > <div> <DropdownList diff --git a/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx b/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx @@ -20,6 +20,7 @@ */ import { h, VNode } from "preact"; import { InputProps, useField } from "./useField.js"; +import { Tooltip } from "../Tooltip.js"; interface Props<T> extends InputProps<T> { readonly?: boolean; @@ -56,9 +57,9 @@ export function InputSelector<T>({ </span> )} {tooltip && ( - <span class="icon has-tooltip-right" onTouchStart={() => {}} data-tooltip={tooltip}> - <i class="mdi mdi-information" /> - </span> + <Tooltip text={tooltip}> + <i class="icon mdi mdi-information" /> + </Tooltip> )} </label> </div> diff --git a/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx b/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx @@ -18,7 +18,10 @@ * * @author Sebastian Javier Marchano (sebasjm) */ -import { TalerMerchantApi, TalerProtocolTimestamp } from "@gnu-taler/taler-util"; +import { + TalerMerchantApi, + TalerProtocolTimestamp, +} from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, h } from "preact"; import { useLayoutEffect, useState } from "preact/hooks"; @@ -28,6 +31,7 @@ import { InputGroup } from "./InputGroup.js"; import { InputLocation } from "./InputLocation.js"; import { InputNumber } from "./InputNumber.js"; import { InputProps, useField } from "./useField.js"; +import { Tooltip } from "../Tooltip.js"; const TALER_SCREEN_ID = 14; @@ -87,38 +91,40 @@ export function InputStock<T>({ <label class="label"> {label} {tooltip && ( - <span class="icon has-tooltip-right" onTouchStart={() => {}} data-tooltip={tooltip}> - <i class="mdi mdi-information" /> - </span> + <Tooltip text={tooltip}> + <i class="icon mdi mdi-information" /> + </Tooltip> )} </label> </div> <div class="field-body is-flex-grow-3"> <div class="field has-addons"> {!alreadyExist ? ( - <button type="button" class="button" - onTouchStart={() => {}} data-tooltip={i18n.str`Click here to configure the product's stock. If left as is, the backend will not control stock.`} - onClick={(): void => { - valueHandler({ - current: 0, - lost: 0, - sold: 0, - } as Stock as any); - }} + <Tooltip + text={i18n.str`Click here to configure the product's stock. If left as is, the backend will not control stock.`} > - <span> + <button + type="button" + class="button" + onClick={(): void => { + valueHandler({ + current: 0, + lost: 0, + sold: 0, + } as Stock as any); + }} + > <i18n.Translate>Manage stock</i18n.Translate> - </span> - </button> + </button> + </Tooltip> ) : ( - <button type="button" class="button" - onTouchStart={() => {}} data-tooltip={i18n.str`This product has been configured without stock control`} - disabled + <Tooltip + text={i18n.str`This product has been configured without stock control`} > - <span> + <button type="button" class="button" disabled> <i18n.Translate>Infinite</i18n.Translate> - </span> - </button> + </button> + </Tooltip> )} </div> </div> @@ -133,7 +139,8 @@ export function InputStock<T>({ const stockAddedErrors: FormErrors<typeof addedStock> = { lost: currentStock + addedStock.incoming < addedStock.lost - ? i18n.str`Shrinkage cannot exceed the current stock and incoming supplies (maximum ${currentStock + addedStock.incoming + ? i18n.str`Shrinkage cannot exceed the current stock and incoming supplies (maximum ${ + currentStock + addedStock.incoming })` : undefined, }; @@ -145,9 +152,9 @@ export function InputStock<T>({ <p class="card-header-title"> {label} {tooltip && ( - <span class="icon" onTouchStart={() => {}} data-tooltip={tooltip}> - <i class="mdi mdi-information" /> - </span> + <Tooltip text={tooltip}> + <i class="icon mdi mdi-information" /> + </Tooltip> )} </p> </header> @@ -184,16 +191,19 @@ export function InputStock<T>({ name="current" label={i18n.str`Current`} side={ - <button type="button" class="button is-danger" - onTouchStart={() => {}} data-tooltip={i18n.str`Remove stock control for this product`} - onClick={(): void => { - valueHandler(undefined as any); - }} + <Tooltip + text={i18n.str`Remove stock control for this product`} > - <span> + <button + type="button" + class="button is-danger" + onClick={(): void => { + valueHandler(undefined as any); + }} + > <i18n.Translate>without stock</i18n.Translate> - </span> - </button> + </button> + </Tooltip> } /> )} @@ -205,7 +215,10 @@ export function InputStock<T>({ withNever /> - <InputGroup<Entity> name="address" label={i18n.str`Warehouse address`}> + <InputGroup<Entity> + name="address" + label={i18n.str`Warehouse address`} + > <InputLocation name="address" /> </InputGroup> </FormProvider> diff --git a/packages/merchant-backoffice-ui/src/components/form/InputTab.tsx b/packages/merchant-backoffice-ui/src/components/form/InputTab.tsx @@ -20,6 +20,7 @@ */ import { h, VNode } from "preact"; import { InputProps, useField } from "./useField.js"; +import { Tooltip } from "../Tooltip.js"; interface Props<T> extends InputProps<T> { readonly?: boolean; @@ -56,9 +57,9 @@ export function InputTab<T>({ </span> )} {tooltip && ( - <span class="icon has-tooltip-right" onTouchStart={() => {}} data-tooltip={tooltip}> - <i class="mdi mdi-information" /> - </span> + <Tooltip text={tooltip}> + <i class="icon mdi mdi-information" /> + </Tooltip> )} </label> </div> diff --git a/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx b/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx @@ -27,6 +27,7 @@ import { FormErrors, FormProvider } from "./FormProvider.js"; import { Input } from "./Input.js"; import { InputGroup } from "./InputGroup.js"; import { InputProps, useField } from "./useField.js"; +import { Tooltip } from "../Tooltip.js"; const TALER_SCREEN_ID = 15; @@ -124,15 +125,16 @@ export function InputTaxes<T>({ name, label }: Props<keyof T>): VNode { /> <div class="buttons is-right mt-5"> - <button - type="submit" - class="button is-info" - onTouchStart={() => {}} data-tooltip={i18n.str`Add tax to the tax list`} - disabled={hasErrors} - onClick={submit} - > - <i18n.Translate>Add</i18n.Translate> - </button> + <Tooltip text={i18n.str`Add tax to the tax list`}> + <button + type="submit" + class="button is-info" + disabled={hasErrors} + onClick={submit} + > + <i18n.Translate>Add</i18n.Translate> + </button> + </Tooltip> </div> </FormProvider> </InputGroup> diff --git a/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx b/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx @@ -20,6 +20,7 @@ */ import { ComponentChildren, h, VNode } from "preact"; import { InputProps, useField } from "./useField.js"; +import { Tooltip } from "../Tooltip.js"; interface Props<T> extends InputProps<T> { name: T; @@ -61,15 +62,11 @@ export function InputToggle<T>({ <label class="label"> {label} {required && ( - <span class="has-text-danger" style={{marginLeft:5}}> + <span class="has-text-danger" style={{ marginLeft: 5 }}> * </span> )} - {tooltip && ( - <span class="icon has-tooltip-right" onTouchStart={() => {}} data-tooltip={tooltip}> - <i class="mdi mdi-information" /> - </span> - )} + {tooltip && <Tooltip text={tooltip} ><i class="icon mdi mdi-information" /></Tooltip>} </label> </div> <div class="field-body is-flex-grow-3"> diff --git a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx @@ -20,6 +20,7 @@ */ import { ComponentChildren, h, VNode } from "preact"; import { InputProps, useField } from "./useField.js"; +import { Tooltip } from "../Tooltip.js"; export interface Props<T> extends InputProps<T> { expand?: boolean; @@ -69,11 +70,7 @@ export function InputWithAddon<T>({ * </span> )} - {tooltip && ( - <span class="icon has-tooltip-right" onTouchStart={() => {}} data-tooltip={tooltip} > - <i class="mdi mdi-information" /> - </span> - )} + {tooltip && <Tooltip text={tooltip} ><i class="icon mdi mdi-information" /></Tooltip>} </label> </div> diff --git a/packages/merchant-backoffice-ui/src/components/form/JumpToElementById.tsx b/packages/merchant-backoffice-ui/src/components/form/JumpToElementById.tsx @@ -24,6 +24,7 @@ import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { useSessionContext } from "../../context/session.js"; import { FormProvider } from "./FormProvider.js"; +import { Tooltip } from "../Tooltip.js"; function NotificationFieldFoot({ notification, @@ -85,17 +86,15 @@ export function JumpToElementById({ /> <NotificationFieldFoot notification={notification} /> </div> - <span class="has-tooltip-bottom" onTouchStart={() => {}} data-tooltip={description}> + <Tooltip text={description}> <ButtonBetterBulma class="button" type="submit" onClick={checkExist} > - <span class="icon"> - <i class="mdi mdi-arrow-right" /> - </span> + <i class="icon mdi mdi-arrow-right" /> </ButtonBetterBulma> - </span> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/components/form/TextField.tsx b/packages/merchant-backoffice-ui/src/components/form/TextField.tsx @@ -19,7 +19,8 @@ * @author Sebastian Javier Marchano (sebasjm) */ import { ComponentChildren, h, VNode } from "preact"; -import { useField, InputProps } from "./useField.js"; +import { InputProps, useField } from "./useField.js"; +import { Tooltip } from "../Tooltip.js"; interface Props<T> extends InputProps<T> { inputType?: "text" | "number" | "multiline" | "password"; @@ -44,9 +45,9 @@ export function TextField<T>({ <label class="label"> {label} {tooltip && ( - <span class="icon has-tooltip-right" onTouchStart={() => {}} data-tooltip={tooltip}> - <i class="mdi mdi-information" /> - </span> + <Tooltip text={tooltip}> + <i class="icon mdi mdi-information" /> + </Tooltip> )} </label> </div> diff --git a/packages/merchant-backoffice-ui/src/components/form/useField.tsx b/packages/merchant-backoffice-ui/src/components/form/useField.tsx @@ -22,6 +22,7 @@ import { ComponentChildren, VNode } from "preact"; import { useState } from "preact/hooks"; import { useFormContext } from "./FormProvider.js"; +import { TranslatedString } from "@gnu-taler/taler-util"; interface Use<V> { error?: string; @@ -86,7 +87,7 @@ export interface InputProps<T> { name: T; label: ComponentChildren; placeholder?: string; - tooltip?: ComponentChildren; + tooltip?: TranslatedString; readonly?: boolean; help?: ComponentChildren; } diff --git a/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx b/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx @@ -34,6 +34,7 @@ import { InputWithAddon } from "../form/InputWithAddon.js"; import { FragmentPersonaFlag } from "../menu/SideBar.js"; import { RoundingInterval } from "@gnu-taler/taler-util"; import { InputSelector } from "../form/InputSelector.js"; +import { Tooltip } from "../Tooltip.js"; const TALER_SCREEN_ID = 16; @@ -41,8 +42,8 @@ export function DefaultInstanceFormFields({ readonlyId, showId, setDefaultPayDelay, - setDefaultRefundDelay, - setDefaultWireDelay + setDefaultRefundDelay, + setDefaultWireDelay, }: { readonlyId?: boolean; showId: boolean; @@ -127,18 +128,21 @@ export function DefaultInstanceFormFields({ tooltip={i18n.str`Time customers have to pay an order before the offer expires by default.`} useProtocolDuration withoutClear - side={<span onTouchStart={() => {}} data-tooltip={i18n.str`Change the value to the default value specified by the server.`}> - <button - type="button" - class="button is-info mr-3" - onClick={() => { - console.log("dale") - setDefaultPayDelay() - }} + side={ + <Tooltip + text={i18n.str`Change the value to the default value specified by the server.`} > - <i18n.Translate>Reset</i18n.Translate> - </button> - </span> + <button + type="button" + class="button is-info mr-3" + onClick={() => { + console.log("dale"); + setDefaultPayDelay(); + }} + > + <i18n.Translate>Reset</i18n.Translate> + </button> + </Tooltip> } /> @@ -148,18 +152,21 @@ export function DefaultInstanceFormFields({ tooltip={i18n.str`Time merchants have to refund an order.`} useProtocolDuration withoutClear - side={<span onTouchStart={() => {}} data-tooltip={i18n.str`Change the value to the default value specified by the server.`}> - <button - type="button" - class="button is-info mr-3" - onClick={() => { - console.log("dale") - setDefaultRefundDelay() - }} + side={ + <Tooltip + text={i18n.str`Change the value to the default value specified by the server.`} > - <i18n.Translate>Reset</i18n.Translate> - </button> - </span> + <button + type="button" + class="button is-info mr-3" + onClick={() => { + console.log("dale"); + setDefaultRefundDelay(); + }} + > + <i18n.Translate>Reset</i18n.Translate> + </button> + </Tooltip> } /> @@ -169,18 +176,21 @@ export function DefaultInstanceFormFields({ tooltip={i18n.str`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.`} useProtocolDuration withoutClear - side={<span onTouchStart={() => {}} data-tooltip={i18n.str`Change the value to the default value specified by the server.`}> - <button - type="button" - class="button is-info mr-3" - onClick={() => { - console.log("dale") - setDefaultWireDelay() - }} + side={ + <Tooltip + text={i18n.str`Change the value to the default value specified by the server.`} > - <i18n.Translate>Reset</i18n.Translate> - </button> - </span> + <button + type="button" + class="button is-info mr-3" + onClick={() => { + console.log("dale"); + setDefaultWireDelay(); + }} + > + <i18n.Translate>Reset</i18n.Translate> + </button> + </Tooltip> } /> diff --git a/packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx b/packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx @@ -25,6 +25,7 @@ import { useState } from "preact/hooks"; import langIcon from "../../assets/icons/languageicon.svg"; import { strings as messages } from "../../i18n/strings.js"; import { usePreference } from "../../hooks/preference.js"; +import { Tooltip } from "../Tooltip.js"; type LangsNames = { [P in keyof typeof messages]: string; @@ -55,14 +56,14 @@ export function LangSelector(): VNode { const [updatingLang, setUpdatingLang] = useState(false); const { lang, changeLanguage } = useTranslationContext(); const [{ persona }] = usePreference(); - const names = - persona === "tester" ? LANG_NAMES_ALL : LANG_NAMES_PROD; + const names = persona === "tester" ? LANG_NAMES_ALL : LANG_NAMES_PROD; return ( <div class="dropdown is-active "> <div class="dropdown-trigger"> - <button type="button" class="button has-tooltip-left" - onTouchStart={() => {}} data-tooltip="change language selection" + <button + type="button" + class="button" aria-haspopup="true" aria-controls="dropdown-menu" onClick={() => setUpdatingLang(!updatingLang)} @@ -72,7 +73,7 @@ export function LangSelector(): VNode { </div> <span>{getLangName(names, lang)}</span> <div class="icon is-right"> - <i class="mdi mdi-chevron-down" /> + <i class="icon mdi mdi-chevron-down" /> </div> </button> </div> diff --git a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx @@ -109,9 +109,7 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_orders} > <a href={"#/orders"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-cash-register" /> - </span> + <i class="icon mdi mdi-cash-register" /> <span class="menu-item-label"> <i18n.Translate>Orders</i18n.Translate> </span> @@ -122,9 +120,8 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_inventory} > <a href={"#/inventory"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-shopping" /> - </span> + <i class="icon mdi mdi-shopping" /> + <span class="menu-item-label"> <i18n.Translate>Inventory</i18n.Translate> </span> @@ -135,9 +132,8 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_categories} > <a href={"#/category"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-label-outline" /> - </span> + <i class="icon mdi mdi-label-outline" /> + <span class="menu-item-label"> <i18n.Translate>Categories</i18n.Translate> </span> @@ -148,9 +144,8 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_wireTransfers} > <a href={"#/transfers"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-arrow-left-right" /> - </span> + <i class="icon mdi mdi-arrow-left-right" /> + <span class="menu-item-label"> <i18n.Translate>Wire transfers</i18n.Translate> </span> @@ -161,9 +156,8 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_templates} > <a href={"#/templates"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-qrcode" /> - </span> + <i class="icon mdi mdi-qrcode" /> + <span class="menu-item-label"> <i18n.Translate>Templates</i18n.Translate> </span> @@ -171,9 +165,8 @@ export function Sidebar({ mobile }: Props): VNode { </HtmlPersonaFlag> <HtmlPersonaFlag htmlElement="li" point={UIElement.sidebar_group}> <a href={"#/groups"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-group" /> - </span> + <i class="icon mdi mdi-group" /> + <span class="menu-item-label"> <i18n.Translate>Product groups</i18n.Translate> </span> @@ -181,9 +174,8 @@ export function Sidebar({ mobile }: Props): VNode { </HtmlPersonaFlag> <HtmlPersonaFlag htmlElement="li" point={UIElement.sidebar_pots}> <a href={"#/pots"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-pot" /> - </span> + <i class="icon mdi mdi-pot" /> + <span class="menu-item-label"> <i18n.Translate>Money pots</i18n.Translate> </span> @@ -194,9 +186,8 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_reports} > <a href={"#/reports"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-paperclip" /> - </span> + <i class="icon mdi mdi-paperclip" /> + <span class="menu-item-label"> <i18n.Translate>Reports</i18n.Translate> </span> @@ -207,9 +198,8 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_statistics} > <a href={"#/statistics"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-counter" /> - </span> + <i class="icon mdi mdi-counter" /> + <span class="menu-item-label"> <i18n.Translate>Statistics</i18n.Translate> </span> @@ -221,9 +211,8 @@ export function Sidebar({ mobile }: Props): VNode { > <a href={"#/tokenfamilies"} class="has-icon"> <span class="menu-item-label"> - <span class="icon"> - <i class="mdi mdi-clock" /> - </span> + <i class="icon mdi mdi-clock" /> + <i18n.Translate>Subscriptions and Discounts</i18n.Translate> </span> </a> @@ -238,9 +227,8 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_bankAccounts} > <a href={"#/bank"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-bank" /> - </span> + <i class="icon mdi mdi-bank" /> + <span class="menu-item-label"> <i18n.Translate>Bank account</i18n.Translate> </span> @@ -288,9 +276,8 @@ export function Sidebar({ mobile }: Props): VNode { : undefined } > - <span class="icon"> - <i class="mdi mdi-account-check" /> - </span> + <i class="icon mdi mdi-account-check" /> + <span class="menu-item-label">KYC Status</span> </a> </HtmlPersonaFlag> @@ -300,9 +287,8 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_otpDevices} > <a href={"#/otp-devices"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-lock" /> - </span> + <i class="icon mdi mdi-lock" /> + <span class="menu-item-label"> <i18n.Translate>OTP Devices</i18n.Translate> </span> @@ -313,9 +299,8 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_webhooks} > <a href={"#/webhooks"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-webhook" /> - </span> + <i class="icon mdi mdi-webhook" /> + <span class="menu-item-label"> <i18n.Translate>Webhooks</i18n.Translate> </span> @@ -326,9 +311,8 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_settings} > <a href={"#/settings"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-square-edit-outline" /> - </span> + <i class="icon mdi mdi-square-edit-outline" /> + <span class="menu-item-label"> <i18n.Translate>Settings</i18n.Translate> </span> @@ -339,9 +323,8 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_password} > <a href={"#/password"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-security" /> - </span> + <i class="icon mdi mdi-security" /> + <span class="menu-item-label"> <i18n.Translate>Password</i18n.Translate> </span> @@ -352,9 +335,8 @@ export function Sidebar({ mobile }: Props): VNode { point={UIElement.sidebar_accessTokens} > <a href={"#/access-token"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-key" /> - </span> + <i class="icon mdi mdi-key" /> + <span class="menu-item-label"> <i18n.Translate>Access tokens</i18n.Translate> </span> @@ -369,27 +351,21 @@ export function Sidebar({ mobile }: Props): VNode { <ul class="menu-list"> <li> <a class="has-icon is-state-info is-hoverable" href="#/interface"> - <span class="icon"> - <i class="mdi mdi-newspaper" /> - </span> + <i class="icon mdi mdi-newspaper" /> <span class="menu-item-label"> <i18n.Translate>Personalization</i18n.Translate> </span> </a> </li> <li> - <div> - <span style={{ width: "3rem" }} class="icon"> - <i class="mdi mdi-web" /> - </span> + <div class="has-icon"> + <i class="icon mdi mdi-web" style={{ width: "3rem" }} /> <span class="menu-item-label">{state.backendUrl.hostname}</span> </div> </li> <li> - <div> - <span style={{ width: "3rem" }} class="icon"> - ID - </span> + <div class="has-icon"> + <span class="icon" style={{ width: "3rem" }}>ID</span> <span class="menu-item-label">{state.instance}</span> </div> </li> @@ -400,9 +376,8 @@ export function Sidebar({ mobile }: Props): VNode { </p> <li> <a href={"#/instance/new"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-plus" /> - </span> + <i class="icon mdi mdi-plus" /> + <span class="menu-item-label"> <i18n.Translate>New</i18n.Translate> </span> @@ -410,9 +385,8 @@ export function Sidebar({ mobile }: Props): VNode { </li> <li> <a href={"#/instances"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-format-list-bulleted" /> - </span> + <i class="icon mdi mdi-format-list-bulleted" /> + <span class="menu-item-label"> <i18n.Translate>List</i18n.Translate> </span> @@ -429,9 +403,8 @@ export function Sidebar({ mobile }: Props): VNode { e.preventDefault(); }} > - <span class="icon"> - <i class="mdi mdi-logout default" /> - </span> + <i class="icon mdi mdi-logout default" /> + <span class="menu-item-label"> <i18n.Translate>Log out</i18n.Translate> </span> diff --git a/packages/merchant-backoffice-ui/src/components/notifications/Notifications.stories.tsx b/packages/merchant-backoffice-ui/src/components/notifications/Notifications.stories.tsx @@ -1,62 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-2024 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 } from "preact"; -import { Notifications } from "./index.js"; - -export default { - title: "Components/Notification", - component: Notifications, - argTypes: { - removeNotification: { action: "removeNotification" }, - }, -}; - -export const Info = (a: any) => <Notifications {...a} />; -Info.args = { - notifications: [ - { - message: "Title", - description: "Some large description", - type: "INFO", - }, - ], -}; -export const Warn = (a: any) => <Notifications {...a} />; -Warn.args = { - notifications: [ - { - message: "Title", - description: "Some large description", - type: "WARN", - }, - ], -}; -export const Error = (a: any) => <Notifications {...a} />; -Error.args = { - notifications: [ - { - message: "Title", - description: "Some large description", - type: "ERROR", - }, - ], -}; diff --git a/packages/merchant-backoffice-ui/src/components/notifications/index.tsx b/packages/merchant-backoffice-ui/src/components/notifications/index.tsx @@ -1,68 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-2024 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 } from "preact"; -import { assertUnreachable } from "@gnu-taler/taler-util"; -import { MessageType, NotificationCard } from "@gnu-taler/web-util/browser"; - -interface Props { - notifications: NotificationCard[]; - removeNotification?: (n: NotificationCard) => void; -} - -function messageStyle(type: MessageType): string { - switch (type) { - case "INFO": - return "message is-info"; - case "WARN": - return "message is-warning"; - case "ERROR": - return "message is-danger"; - case "SUCCESS": - return "message is-success"; - default: { - assertUnreachable(type); - } - } -} - -export function Notifications({ - notifications, - removeNotification, -}: Props): VNode { - return ( - <div class="toast"> - {notifications.map((n, i) => ( - <article key={i} class={messageStyle(n.type)}> - <div class="message-header"> - <p>{n.message}</p> - <button - type="button" - class="delete" - onClick={() => removeNotification && removeNotification(n)} - /> - </div> - {n.description && <div class="message-body">{n.description}</div>} - </article> - ))} - </div> - ); -} diff --git a/packages/merchant-backoffice-ui/src/components/picker/DatePicker.tsx b/packages/merchant-backoffice-ui/src/components/picker/DatePicker.tsx @@ -224,18 +224,15 @@ export class DatePicker extends Component<Props, State> { {!selectYearMode && ( <nav> - <span onClick={this.displayPrevMonth} class="icon"> - <i - style={{ transform: "rotate(180deg)" }} - class="mdi mdi-forward" - /> - </span> + <i + onClick={this.displayPrevMonth} + style={{ transform: "rotate(180deg)" }} + class="icon mdi mdi-forward" + /> <h4> {monthArrShortFull[displayedMonth]} {displayedYear} </h4> - <span onClick={this.displayNextMonth} class="icon"> - <i class="mdi mdi-forward" /> - </span> + <i onClick={this.displayNextMonth} class="icon mdi mdi-forward" /> </nav> )} diff --git a/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx b/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx @@ -163,9 +163,7 @@ function DurationColumn({ style={{ width: "100%", textAlign: "center", margin: 5 }} onClick={onDecrease} > - <span class="icon"> - <i class="mdi mdi-chevron-up" /> - </span> + <i class="icon mdi mdi-chevron-up" /> </button> )} </div> @@ -195,9 +193,7 @@ function DurationColumn({ style={{ width: "100%", textAlign: "center", margin: 5 }} onClick={onIncrease} > - <span class="icon"> - <i class="mdi mdi-chevron-down" /> - </span> + <i class="icon mdi mdi-chevron-down" /> </button> )} </div> diff --git a/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx b/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx @@ -30,6 +30,7 @@ import { InputNumber } from "../form/InputNumber.js"; import { InputTaxes } from "../form/InputTaxes.js"; import { FragmentPersonaFlag } from "../menu/SideBar.js"; import { UIElement } from "../../hooks/preference.js"; +import { Tooltip } from "../Tooltip.js"; const TALER_SCREEN_ID = 22; @@ -74,14 +75,17 @@ export function NonInventoryProductFrom({ return ( <Fragment> <div class="buttons"> - <button - type="button" - class="button is-success" - onTouchStart={() => {}} data-tooltip={i18n.str`Describe and add a product that is not in the inventory list`} - onClick={() => setShowCreateProduct(true)} + <Tooltip + text={i18n.str`Describe and add a product that is not in the inventory list`} > - <i18n.Translate>Add custom product</i18n.Translate> - </button> + <button + type="button" + class="button is-success" + onClick={() => setShowCreateProduct(true)} + > + <i18n.Translate>Add custom product</i18n.Translate> + </button> + </Tooltip> </div> {showCreateProduct && ( <div class="modal is-active"> diff --git a/packages/merchant-backoffice-ui/src/components/product/ProductList.tsx b/packages/merchant-backoffice-ui/src/components/product/ProductList.tsx @@ -13,7 +13,7 @@ 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/> */ -import { Amounts, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { Amounts, TalerMerchantApi, TranslatedString } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import emptyImage from "../../assets/empty.png"; import { @@ -21,6 +21,7 @@ import { useTranslationContext, } from "@gnu-taler/web-util/browser"; import { useSessionContext } from "../../context/session.js"; +import { Tooltip } from "../Tooltip.js"; const TALER_SCREEN_ID = 24; @@ -28,7 +29,7 @@ interface Props { list: TalerMerchantApi.ProductSold[]; actions?: { name: string; - tooltip: string; + tooltip: TranslatedString; handler: (d: TalerMerchantApi.ProductSold, index: number) => void; }[]; } @@ -98,14 +99,15 @@ export function ProductList({ list, actions = [] }: Props): VNode { {actions.map((a, i) => { return ( <div key={i} class="buttons is-right"> - <button - class="button is-small is-danger has-tooltip-left" - onTouchStart={() => {}} data-tooltip={a.tooltip} - type="button" - onClick={() => a.handler(entry, index)} - > - {a.name} - </button> + <Tooltip text={a.tooltip}> + <button + class="button is-small is-danger" + type="button" + onClick={() => a.handler(entry, index)} + > + {a.name} + </button> + </Tooltip> </div> ); })} diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx @@ -54,6 +54,7 @@ import { import { undefinedIfEmpty } from "../../../utils/table.js"; import { FOREVER_REFRESHABLE_TOKEN } from "../../login/index.js"; import { InputPassword } from "../../../components/form/InputPassword.js"; +import { Tooltip } from "../../../components/Tooltip.js"; const TALER_SCREEN_ID = 25; @@ -282,17 +283,17 @@ export function CreatePage({ onConfirm, onBack, forceId }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onClick={create} - type="submit" - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields and choose authorization method` : i18n.str`Confirm operation` } > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma onClick={create} type="submit"> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx b/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx @@ -24,6 +24,7 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { VNode, h } from "preact"; import { StateUpdater, useEffect, useState } from "preact/hooks"; import { useSessionContext } from "../../../context/session.js"; +import { Tooltip } from "../../../components/Tooltip.js"; const TALER_SCREEN_ID = 27; @@ -77,9 +78,7 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-desktop-mac" /> - </span> + <i class="icon mdi mdi-desktop-mac" /> <i18n.Translate>Instances</i18n.Translate> </p> @@ -97,21 +96,16 @@ export function CardTable({ </button> </div> <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Add new instance`} - > + <Tooltip text={i18n.str`Add new instance`}> <button class="button is-info" type="button" accessKey="+" onClick={onCreate} > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> </div> </header> <div class="card-content"> @@ -280,7 +274,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx @@ -38,6 +38,7 @@ import { Fragment } from "preact"; import { SolveMFAChallenges } from "../../../components/SolveMFA.js"; import { useSessionContext } from "../../../context/session.js"; import { ConfirmModal } from "../../../components/modal/index.js"; +import { Tooltip } from "../../../components/Tooltip.js"; const TALER_SCREEN_ID = 28; @@ -150,37 +151,28 @@ export function View({ <div class="tabs" style={{ overflow: "inherit" }}> <ul> <li class={showIsActive}> - <div - class="has-tooltip-right" - onTouchStart={() => {}} data-tooltip={i18n.str`Only show active instances`} - > + <Tooltip text={i18n.str`Only show active instances`}> <a onClick={() => setShow("active")}> <i18n.Translate>Active</i18n.Translate> - <i class="mdi mdi-information ml-2" /> + <i class="icon mdi mdi-information ml-2" /> </a> - </div> + </Tooltip> </li> <li class={showIsDeleted}> - <div - class="has-tooltip-right" - onTouchStart={() => {}} data-tooltip={i18n.str`Only show deleted instances`} - > + <Tooltip text={i18n.str`Only show deleted instances`}> <a onClick={() => setShow("deleted")}> <i18n.Translate>Deleted</i18n.Translate> - <i class="mdi mdi-information ml-2" /> + <i class="icon mdi mdi-information ml-2" /> </a> - </div> + </Tooltip> </li> <li class={showAll}> - <div - class="has-tooltip-right" - onTouchStart={() => {}} data-tooltip={i18n.str`Show all instances`} - > + <Tooltip text={i18n.str`Show all instances`}> <a onClick={() => setShow(null)}> <i18n.Translate>All</i18n.Translate> - <i class="mdi mdi-information ml-2" /> + <i class="icon mdi mdi-information ml-2" /> </a> - </div> + </Tooltip> </li> </ul> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accessTokens/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accessTokens/create/CreatePage.tsx @@ -49,6 +49,7 @@ import { useSessionContext } from "../../../../context/session.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; import { getAvailableForPersona } from "../../../../components/menu/SideBar.js"; import { UIElement, usePreference } from "../../../../hooks/preference.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 29; @@ -288,17 +289,17 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - type="submit" - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - onClick={create} > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma type="submit" onClick={create}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accessTokens/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accessTokens/list/Table.tsx @@ -28,6 +28,7 @@ import { datetimeFormatForPreferences, usePreference, } from "../../../../hooks/preference.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 32; @@ -56,27 +57,20 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-key" /> - </span> + <i class="icon mdi mdi-key" /> <i18n.Translate>Access tokens</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Create access token`} - > + <Tooltip text={i18n.str`Create access token`}> <button class="button is-info" type="button" accessKey="+" onClick={onCreate} > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> </div> </header> <div class="card-content"> @@ -121,14 +115,15 @@ function Table({ <Fragment> <div class="table-container"> {onLoadMoreBefore && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more devices before the first one`} - onClick={onLoadMoreBefore} - > - <i18n.Translate>Load first page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more devices before the first one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreBefore} + > + <i18n.Translate>Load first page</i18n.Translate> + </button> + </Tooltip> )} <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> @@ -189,10 +184,7 @@ function Table({ </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Remove this access token`} - > + <Tooltip text={i18n.str`Remove this access token`}> <button class="button is-small is-danger" type="button" @@ -200,7 +192,7 @@ function Table({ > <i18n.Translate>Delete</i18n.Translate> </button> - </span> + </Tooltip> </div> </td> </tr> @@ -209,14 +201,15 @@ function Table({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more devices after the last one`} - onClick={onLoadMoreAfter} - > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more devices after the last one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </div> </Fragment> @@ -229,7 +222,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx @@ -55,6 +55,7 @@ import { UIElement, usePreference } from "../../../../hooks/preference.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; import { safeConvertURL } from "../update/UpdatePage.js"; import { TestRevenueErrorType, testRevenueAPI } from "./index.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 33; @@ -150,12 +151,15 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { }; const { state: session, lib } = useSessionContext(); - const request: TalerMerchantApi.AccountAddDetails = { - payto_uri: state.payto_uri!, - credit_facade_credentials, - credit_facade_url, - extra_wire_subject_metadata: state.extra_wire_subject_metadata, - }; + const request: TalerMerchantApi.AccountAddDetails | undefined = + !state.payto_uri + ? undefined + : { + payto_uri: state.payto_uri, + credit_facade_credentials, + credit_facade_url, + extra_wire_subject_metadata: state.extra_wire_subject_metadata, + }; const [notification, safeFunctionHandler] = useLocalNotificationBetter(); const mfa = useChallengeHandler(); @@ -163,7 +167,7 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { i18n.str`add bank account`, (token: AccessToken, request: Entity, challengeIds: string[]) => lib.instance.addBankAccount(token, request, { challengeIds }), - !session.token ? undefined : [session.token, request, []], + !session.token || !request ? undefined : [session.token, request, []], ); add.onSuccess = onCreated; add.onFail = (fail) => { @@ -321,15 +325,15 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { readonly threeState side={ - <ButtonBetterBulma - class="button is-info" - type="button" - onTouchStart={() => {}} - data-tooltip={i18n.str`Verify details with server`} - onClick={test} - > - <i18n.Translate>Test</i18n.Translate> - </ButtonBetterBulma> + <Tooltip text={i18n.str`Verify details with server`}> + <ButtonBetterBulma + class="button is-info" + type="button" + onClick={test} + > + <i18n.Translate>Test</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> } /> </FragmentPersonaFlag> @@ -339,18 +343,17 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} - data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - onClick={add} - type="submit" > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma onClick={add} type="submit"> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx @@ -38,6 +38,7 @@ import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; import { ConfirmModal } from "../../../../components/modal/index.js"; import { useSessionContext } from "../../../../context/session.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 35; @@ -122,28 +123,20 @@ export function CardTable({ accounts, onCreate, onSelect }: Props): VNode { <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-bank" /> - </span> + <i class="icon mdi mdi-bank" /> <i18n.Translate>Bank accounts</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} - data-tooltip={i18n.str`Add new account`} - > + <Tooltip text={i18n.str`Add new account`}> <button class="button is-info" type="button" accessKey="+" onClick={onCreate} > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> </div> </header> <div class="card-content"> @@ -260,14 +253,16 @@ function Table({ accounts, onDelete, onSelect }: TableProps): VNode { </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <button - class="button is-danger is-small has-tooltip-left" - onTouchStart={() => {}} - data-tooltip={i18n.str`Delete selected accounts from the database`} - onClick={() => onDelete(acc)} + <Tooltip + text={i18n.str`Delete selected accounts from the database`} > - <i18n.Translate>Delete</i18n.Translate> - </button> + <button + class="button is-danger is-small" + onClick={() => onDelete(acc)} + > + <i18n.Translate>Delete</i18n.Translate> + </button> + </Tooltip> </div> </td> </tr> @@ -300,14 +295,16 @@ function Table({ accounts, onDelete, onSelect }: TableProps): VNode { </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <button - class="button is-danger is-small has-tooltip-left" - onTouchStart={() => {}} - data-tooltip={i18n.str`Delete selected accounts from the database`} - onClick={() => onDelete(acc)} + <Tooltip + text={i18n.str`Delete selected accounts from the database`} > - <i18n.Translate>Delete</i18n.Translate> - </button> + <button + class="button is-danger is-small" + onClick={() => onDelete(acc)} + > + <i18n.Translate>Delete</i18n.Translate> + </button> + </Tooltip> </div> </td> </tr> @@ -340,14 +337,16 @@ function Table({ accounts, onDelete, onSelect }: TableProps): VNode { </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <button - class="button is-danger is-small has-tooltip-left" - onTouchStart={() => {}} - data-tooltip={i18n.str`Delete selected accounts from the database`} - onClick={() => onDelete(acc)} + <Tooltip + text={i18n.str`Delete selected accounts from the database`} > - <i18n.Translate>Delete</i18n.Translate> - </button> + <button + class="button is-danger is-small" + onClick={() => onDelete(acc)} + > + <i18n.Translate>Delete</i18n.Translate> + </button> + </Tooltip> </div> </td> </tr> @@ -380,15 +379,16 @@ function Table({ accounts, onDelete, onSelect }: TableProps): VNode { </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <button - class="button is-danger is-small has-tooltip-left" - onTouchStart={() => {}} - data-tooltip={i18n.str`Delete selected accounts from the database`} - onClick={() => onDelete(acc)} - // onClick={() => onDelete(acc,)} + <Tooltip + text={i18n.str`Delete selected accounts from the database`} > - <i18n.Translate>Delete</i18n.Translate> - </button> + <button + class="button is-danger is-small" + onClick={() => onDelete(acc)} + > + <i18n.Translate>Delete</i18n.Translate> + </button> + </Tooltip> </div> </td> </tr> @@ -407,7 +407,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx @@ -24,12 +24,10 @@ import { TalerError, assertUnreachable } from "@gnu-taler/taler-util"; -import { useTranslationContext } from "@gnu-taler/web-util/browser"; +import { NotificationCardBulma, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { ErrorLoadingMerchant } from "../../../../components/ErrorLoadingMerchant.js"; import { Loading } from "../../../../components/exception/loading.js"; -import { NotificationCardBulma } from "@gnu-taler/web-util/browser"; -import { useSessionContext } from "../../../../context/session.js"; import { useInstanceBankAccounts } from "../../../../hooks/bank.js"; import { LoginPage } from "../../../login/index.js"; import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js"; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx @@ -55,6 +55,7 @@ import { WithId } from "../../../../declaration.js"; import { UIElement, usePreference } from "../../../../hooks/preference.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; import { TestRevenueErrorType, testRevenueAPI } from "../create/index.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 36; @@ -412,15 +413,17 @@ export function UpdatePage({ account, onUpdated, onBack }: Props): VNode { readonly threeState side={ - <ButtonBetterBulma - type="button" - class="button is-info" - onTouchStart={() => {}} - data-tooltip={i18n.str`Compare info from server with account form`} - onClick={test} + <Tooltip + text={i18n.str`Compare info from server with account form`} > - <i18n.Translate>Test</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma + type="button" + class="button is-info" + onClick={test} + > + <i18n.Translate>Test</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> } /> </FragmentPersonaFlag> @@ -432,18 +435,17 @@ export function UpdatePage({ account, onUpdated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} - data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - onClick={update} - type="submit" > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma onClick={update} type="submit"> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </div> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/categories/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/categories/create/CreatePage.tsx @@ -39,6 +39,7 @@ import { import { Input } from "../../../../components/form/Input.js"; import { useSessionContext } from "../../../../context/session.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 37; @@ -109,17 +110,17 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - type="submit" - onClick={create} > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma type="submit" onClick={create}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/categories/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/categories/list/Table.tsx @@ -34,6 +34,7 @@ import { import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; import { useSessionContext } from "../../../../context/session.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 38; @@ -82,27 +83,20 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-label" /> - </span> + <i class="icon mdi mdi-label" /> <i18n.Translate>Categories</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Add new devices`} - > + <Tooltip text={i18n.str`Add new devices`}> <button class="button is-info" accessKey="+" type="button" onClick={onCreate} > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> </div> </header> <div class="card-content"> @@ -149,14 +143,15 @@ function Table({ return ( <div class="table-container"> {onLoadMoreBefore && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more devices before the first one`} - onClick={onLoadMoreBefore} - > - <i18n.Translate>Load first page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more devices before the first one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreBefore} + > + <i18n.Translate>Load first page</i18n.Translate> + </button> + </Tooltip> )} <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> @@ -197,14 +192,17 @@ function Table({ </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <ButtonBetterBulma - type="button" - class="button is-danger is-small has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Delete selected category from the database`} - onClick={onDelete.withArgs(String(i.category_id))} + <Tooltip + text={i18n.str`Delete selected category from the database`} > - <i18n.Translate>Delete</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma + type="button" + class="button is-danger is-small" + onClick={onDelete.withArgs(String(i.category_id))} + > + <i18n.Translate>Delete</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </td> </tr> @@ -213,14 +211,15 @@ function Table({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more devices after the last one`} - onClick={onLoadMoreAfter} - > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more devices after the last one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </div> ); @@ -232,7 +231,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/categories/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/categories/update/UpdatePage.tsx @@ -24,6 +24,7 @@ import { HttpStatusCode, TalerError, TalerMerchantApi, + TranslatedString, } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, @@ -44,6 +45,7 @@ import { ProductWithId, useInstanceProductsFromIds, } from "../../../../hooks/product.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 39; @@ -152,13 +154,11 @@ export function UpdatePage({ category, onUpdated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - type="submit" - onTouchStart={() => {}} data-tooltip={i18n.str`Confirm operation`} - onClick={update} - > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <Tooltip text={i18n.str`Confirm operation`}> + <ButtonBetterBulma type="submit" onClick={update}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> <ProductListSmall onSelect={() => {}} @@ -206,9 +206,7 @@ function ProductListSmall({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-shopping" /> - </span> + <i class="icon mdi mdi-shopping" /> <i18n.Translate>Products</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"></div> @@ -274,17 +272,13 @@ function Table({ }} /> </td> - <td class="has-tooltip-right" style={{ cursor: "pointer" }}> - {i.product_name} - </td> - <td - class="has-tooltip-right" - onTouchStart={() => {}} data-tooltip={i.description} - style={{ cursor: "pointer" }} - > - {i.description.length > 30 - ? i.description.substring(0, 30) + "..." - : i.description} + <td style={{ cursor: "pointer" }}>{i.product_name}</td> + <td style={{ cursor: "pointer" }}> + <Tooltip text={i.description as TranslatedString}> + {i.description.length > 30 + ? i.description.substring(0, 30) + "..." + : i.description} + </Tooltip> </td> </tr> </Fragment> @@ -293,14 +287,15 @@ function Table({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more products after the last one`} - onClick={onLoadMoreAfter} - > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more products after the last one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </Fragment> ); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/groups/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/groups/create/CreatePage.tsx @@ -39,6 +39,7 @@ import { import { Input } from "../../../../components/form/Input.js"; import { useSessionContext } from "../../../../context/session.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 37; @@ -116,17 +117,17 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - type="submit" - onClick={create} > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma type="submit" onClick={create}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/groups/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/groups/list/Table.tsx @@ -34,6 +34,7 @@ import { import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; import { useSessionContext } from "../../../../context/session.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 38; @@ -60,7 +61,8 @@ export function CardTable({ const { state: session, lib } = useSessionContext(); const [notification, safeFunctionHandler] = useLocalNotificationBetter(); - const remove = safeFunctionHandler(i18n.str`delete product group`, + const remove = safeFunctionHandler( + i18n.str`delete product group`, lib.instance.deleteProductGroup.bind(lib.instance), ).lambda((id: string) => (!session.token ? undefined! : [session.token, id])); remove.onSuccess = () => i18n.str`Product group deleted`; @@ -81,27 +83,20 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-label" /> - </span> + <i class="icon mdi mdi-label" /> <i18n.Translate>Product groups</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Add new group`} - > + <Tooltip text={i18n.str`Add new group`}> <button class="button is-info" accessKey="+" type="button" onClick={onCreate} > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> </div> </header> <div class="card-content"> @@ -148,14 +143,15 @@ function Table({ return ( <div class="table-container"> {onLoadMoreBefore && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more groups before the first one`} - onClick={onLoadMoreBefore} - > - <i18n.Translate>Load first page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more groups before the first one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreBefore} + > + <i18n.Translate>Load first page</i18n.Translate> + </button> + </Tooltip> )} <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> @@ -187,14 +183,17 @@ function Table({ </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <ButtonBetterBulma - type="button" - class="button is-danger is-small has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Delete selected group from the database`} - onClick={onDelete.withArgs(String(i.group_serial))} + <Tooltip + text={i18n.str`Delete selected group from the database`} > - <i18n.Translate>Delete</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma + type="button" + class="button is-danger is-small" + onClick={onDelete.withArgs(String(i.group_serial))} + > + <i18n.Translate>Delete</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </td> </tr> @@ -203,14 +202,15 @@ function Table({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more groups after the last one`} - onClick={onLoadMoreAfter} - > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more groups after the last one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </div> ); @@ -222,7 +222,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/groups/list/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/groups/list/UpdatePage.tsx @@ -44,6 +44,7 @@ import { ProductWithId, useInstanceProductsFromIds, } from "../../../../hooks/product.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 39; @@ -155,13 +156,11 @@ export function UpdatePage({ group, onUpdated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - type="submit" - onTouchStart={() => {}} data-tooltip={i18n.str`Confirm operation`} - onClick={update} - > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <Tooltip text={i18n.str`Confirm operation`}> + <ButtonBetterBulma type="submit" onClick={update}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx @@ -47,9 +47,7 @@ export function ListPage({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-clock" /> - </span> + <i class="icon mdi mdi-clock" /> <i18n.Translate>Account's KYC status</i18n.Translate> </p> @@ -134,7 +132,13 @@ function PendingTable({ return <i18n.Translate>Warning</i18n.Translate>; case TalerMerchantApi.MerchantAccountKycStatusSimplified .ERROR: - return <span style={{color:"#e93c3c", fontWeight: "bold"}}><i18n.Translate>Error</i18n.Translate></span>; + return ( + <span + style={{ color: "#e93c3c", fontWeight: "bold" }} + > + <i18n.Translate>Error</i18n.Translate> + </span> + ); default: assertUnreachable(st); } @@ -254,7 +258,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-emoticon-happy mdi-48px" /> + <i class="icon mdi mdi-emoticon-happy mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx @@ -64,6 +64,7 @@ import { UIElement, usePreference } from "../../../../hooks/preference.js"; import { rate } from "../../../../utils/amount.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; import { FragmentPersonaFlag } from "../../../../components/menu/SideBar.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 42; @@ -339,7 +340,7 @@ export function CreatePage({ case HttpStatusCode.Gone: return i18n.str`Product with ID "${fail.body.product_id}" is out of stock.`; case HttpStatusCode.UnavailableForLegalReasons: - return i18n.str`No exchange would accept a payment because of KYC requirements. Verify the `; + return i18n.str`No exchange would accept a payment because of KYC requirements.`; default: assertUnreachable(fail); } @@ -789,12 +790,9 @@ export function CreatePage({ <div class="field-label is-normal"> <label class="label"> <i18n.Translate>Custom field name</i18n.Translate> - <span - class="icon has-tooltip-right" - onTouchStart={() => {}} data-tooltip={"new extra field"} - > - <i class="mdi mdi-information" /> - </span> + <Tooltip text={i18n.str`new extra field`}> + <i class="icon mdi mdi-information" /> + </Tooltip> </label> </div> <div class="field-body is-flex-grow-3"> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx @@ -57,6 +57,7 @@ import { mergeRefunds } from "../../../../utils/amount.js"; import { RefundModal } from "../list/Table.js"; import { Event, Timeline } from "./Timeline.js"; import { ConfirmModal } from "../../../../components/modal/index.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 44; @@ -776,9 +777,8 @@ function PaidPage({ <div class="level-item"> <h1 class="title"> <div class="buttons"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ merchantCanRefund ? i18n.str`Refund order` : i18n.str`Not refundable` @@ -792,10 +792,9 @@ function PaidPage({ > <i18n.Translate>Refund</i18n.Translate> </button> - </span> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={ + </Tooltip> + <Tooltip + text={ !order.wire_details.length ? i18n.str`No wire transfer reported` : i18n.str`Check wire transfers` @@ -812,7 +811,7 @@ function PaidPage({ > <i18n.Translate>Wire transfers</i18n.Translate> </button> - </span> + </Tooltip> </div> </h1> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx @@ -22,6 +22,7 @@ import { datetimeFormatForPreferences, usePreference, } from "../../../../hooks/preference.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; interface Props { events: Event[]; @@ -62,105 +63,93 @@ export function Timeline({ events: e }: Props) { switch (e.type) { case "refund-deadline": return ( - <div - class="timeline-marker is-icon " - onTouchStart={() => {}} data-tooltip={i18n.str`This is when the refund period has expired.`} + <Tooltip + text={i18n.str`This is when the refund period has expired.`} > - <i class="mdi mdi-flag" /> - </div> + <i class="timeline-marker is-icon icon mdi mdi-flag" /> + </Tooltip> ); case "pay-deadline": return ( - <div - class="timeline-marker is-icon " - onTouchStart={() => {}} data-tooltip={i18n.str`This is when the time for making the payment has been expired.`} + <Tooltip + text={i18n.str`This is when the time for making the payment has been expired.`} > - <i class="mdi mdi-flag" /> - </div> + <i class="timeline-marker is-icon icon mdi mdi-flag" /> + </Tooltip> ); case "delivery": return ( - <div class="timeline-marker is-icon "> - <i class="mdi mdi-delivery" /> - </div> + <span> + <i class="timeline-marker is-icon icon mdi mdi-delivery" /> + </span> ); case "start": return ( - <div class="timeline-marker is-icon"> - <i class="mdi mdi-flag " /> - </div> + <span> + <i class="timeline-marker is-icon icon mdi mdi-flag " /> + </span> ); case "wire-deadline": return ( - <div - class="timeline-marker is-icon " - onTouchStart={() => {}} data-tooltip={i18n.str`This is when the wire transfer is going to be executed by the payment service provider.`} + <Tooltip + text={i18n.str`This is when the wire transfer is going to be executed by the payment service provider.`} > - <i class="mdi mdi-flag" /> - </div> + <i class="timeline-marker is-icon icon mdi mdi-flag" /> + </Tooltip> ); case "wired-pending": return ( - <div - class="timeline-marker is-icon is-warning" - onTouchStart={() => {}} data-tooltip={i18n.str`This wire transfer has been notified by the payment service provider.`} + <Tooltip + text={i18n.str`This wire transfer has been notified by the payment service provider.`} > - <i class="mdi mdi-cash" /> - </div> + <i class="timeline-marker is-icon icon mdi mdi-cash" /> + </Tooltip> ); case "wired-confirmed": return ( - <div - class="timeline-marker is-icon is-success" - onTouchStart={() => {}} data-tooltip={i18n.str`This wire transfer has been notified manually or by the banking system.`} + <Tooltip + text={i18n.str`This wire transfer has been notified manually or by the banking system.`} > - <i class="mdi mdi-cash" /> - </div> + <i class="timeline-marker is-icon is-success icon mdi mdi-cash" /> + </Tooltip> ); case "wired-overdue": return ( - <div - class="timeline-marker is-icon is-danger" - onTouchStart={() => {}} data-tooltip={i18n.str`This wire transfer has been notified by the payment service provider.`} + <Tooltip + text={i18n.str`This wire transfer has been notified by the payment service provider.`} > - <i class="mdi mdi-cash" /> - </div> + <i class="timeline-marker is-icon is-danger timeline-marker is-icon icon mdi mdi-cash" /> + </Tooltip> ); case "refund-created": return ( - <div - class="timeline-marker is-icon is-danger" - onTouchStart={() => {}} data-tooltip={i18n.str`This refund is waiting to be claimed by the customer.`} + <Tooltip + text={i18n.str`This refund is waiting to be claimed by the customer.`} > - <i class="mdi mdi-cash" /> - </div> + <i class="timeline-marker is-icon is-danger icon mdi mdi-cash" /> + </Tooltip> ); case "refund-taken": return ( - <div - class="timeline-marker is-icon is-success" - onTouchStart={() => {}} data-tooltip={i18n.str`This refund has been claimed by the customer.`} + <Tooltip + text={i18n.str`This refund has been claimed by the customer.`} > - <i class="mdi mdi-cash" /> - </div> + <i class="timeline-marker is-icon is-success icon mdi mdi-cash" /> + </Tooltip> ); case "now": return ( - <div - class="timeline-marker is-icon is-info" - onTouchStart={() => {}} data-tooltip={i18n.str`The current moment in time.`} - > - <i class="mdi mdi-clock" /> - </div> + <Tooltip text={i18n.str`The current moment in time.`}> + <i class="timeline-marker is-icon is-info icon mdi mdi-clock" /> + </Tooltip> ); case "refund-missed": { return ( - <div - class="timeline-marker is-icon is-danger" - onTouchStart={() => {}} data-tooltip={i18n.str`This refund can't be claimed because the wire transfer has already been made.`} + <Tooltip + text={i18n.str`This refund can't be claimed because the wire transfer has already been made.`} > - <i class="mdi mdi-cash" /> - </div> + <i class="timeline-marker is-icon is-danger icon mdi mdi-cash" /> + </Tooltip> ); } default: { diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx @@ -25,6 +25,7 @@ import { Fragment, VNode, h } from "preact"; import { WithId } from "../../../../declaration.js"; import { OrderListSection } from "./index.js"; import { CardTable } from "./Table.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 47; @@ -57,37 +58,34 @@ export function ListPage({ <Fragment> <div class="columns"> <div class="column"> - <div class="tabs" style={{ paddingTop: 10, paddingBottom: 10 }}> + <div + class="tabs" + style={{ paddingTop: 10, paddingBottom: 10, overflow: "inherit" }} + > <ul> <li class={ section === OrderListSection.NEW ? "is-active" : undefined } > - <div - class="has-tooltip-right" - onTouchStart={() => {}} data-tooltip={i18n.str`Only show unpaid orders`} - > + <Tooltip text={i18n.str`Only show unpaid orders`}> <a onClick={() => onChangeSection(OrderListSection.NEW)}> <i18n.Translate>New</i18n.Translate> - <i class="mdi mdi-information ml-2" /> + <i class="icon mdi mdi-information ml-2" /> </a> - </div> + </Tooltip> </li> <li class={ section === OrderListSection.PAID ? "is-active" : undefined } > - <div - class="has-tooltip-right" - onTouchStart={() => {}} data-tooltip={i18n.str`Only show paid orders`} - > + <Tooltip text={i18n.str`Only show paid orders`}> <a onClick={() => onChangeSection(OrderListSection.PAID)}> <i18n.Translate>Paid</i18n.Translate> - <i class="mdi mdi-information ml-2" /> + <i class="icon mdi mdi-information ml-2" /> </a> - </div> + </Tooltip> </li> <li class={ @@ -96,30 +94,26 @@ export function ListPage({ : undefined } > - <div - class="has-tooltip-right" - onTouchStart={() => {}} data-tooltip={i18n.str`Only show orders with refunds`} - > + <Tooltip text={i18n.str`Only show orders with refunds`}> <a onClick={() => onChangeSection(OrderListSection.REFUNDED)}> <i18n.Translate>Refunded</i18n.Translate> - <i class="mdi mdi-information ml-2" /> + <i class="icon mdi mdi-information ml-2" /> </a> - </div> + </Tooltip> </li> <li class={ section === OrderListSection.PENDING ? "is-active" : undefined } > - <div - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Show only paid orders for which the wire transfer is still pending.`} + <Tooltip + text={i18n.str`Show only paid orders for which the wire transfer is still pending.`} > <a onClick={() => onChangeSection(OrderListSection.PENDING)}> <i18n.Translate>Not wired</i18n.Translate> - <i class="mdi mdi-information ml-2" /> + <i class="icon mdi mdi-information ml-2" /> </a> - </div> + </Tooltip> </li> <li class={ @@ -128,37 +122,29 @@ export function ListPage({ : undefined } > - <div - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Only display orders that have already been transferred by the payment service provider`} + <Tooltip + text={i18n.str`Only display orders that have already been transferred by the payment service provider`} > <a onClick={() => onChangeSection(OrderListSection.INCOMING)}> <i18n.Translate>Completed</i18n.Translate> - <i class="mdi mdi-information ml-2" /> + <i class="icon mdi mdi-information ml-2" /> </a> - </div> + </Tooltip> </li> <li class={section === undefined ? "is-active" : undefined}> - <div - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Remove all filters`} - > + <Tooltip text={i18n.str`Remove all filters`}> <a onClick={() => onChangeSection(OrderListSection.ALL)}> <i18n.Translate>All</i18n.Translate> - <i class="mdi mdi-information ml-2" /> + <i class="icon mdi mdi-information ml-2" /> </a> - </div> + </Tooltip> </li> </ul> </div> </div> - <div class="column "> - - </div> - + <div class="column "></div> </div> - <CardTable orders={orders} onCreate={onCreate} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx @@ -61,6 +61,7 @@ import { import { mergeRefunds } from "../../../../utils/amount.js"; import { getOrderAmountAndMaxDepositFee } from "../details/DetailPage.js"; import { OrderListSection } from "./index.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 48; @@ -92,9 +93,7 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-cash-register" /> - </span> + <i class="icon mdi mdi-cash-register" /> <i18n.Translate>Orders</i18n.Translate> </p> @@ -106,18 +105,16 @@ export function CardTable({ class="card-header-icon" aria-label="more options" > - <span class="has-tooltip-left" onTouchStart={() => {}} data-tooltip={i18n.str`Create order`}> + <Tooltip text={i18n.str`Create order`}> <button class="button is-info" type="button" accessKey="+" onClick={onCreate} > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> </HtmlPersonaFlag> </header> <div class="card-content"> @@ -293,14 +290,15 @@ function Table({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more orders after the last one`} - onClick={onLoadMoreAfter} - > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more orders after the last one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </div> </Fragment> @@ -313,7 +311,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx @@ -54,6 +54,7 @@ import { } from "../../../../hooks/preference.js"; import { format } from "date-fns"; import { DatePicker } from "../../../../components/picker/DatePicker.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 46; @@ -174,7 +175,6 @@ export default function OrderList({ } }; const [pickDate, setPickDate] = useState(false); - const dateTooltip = i18n.str`Select date to show nearby orders`; const [preferences] = usePreference(); return ( @@ -202,14 +202,14 @@ export default function OrderList({ class="button is-fullwidth" onClick={() => setNewDate(undefined)} > - <span class="icon" onTouchStart={() => {}} data-tooltip={i18n.str`Clear date filter`}> - <i class="mdi mdi-close" /> - </span> + <Tooltip text={i18n.str`Clear date filter`}> + <i class="icon mdi mdi-close" /> + </Tooltip> </a> </div> )} <div class="control"> - <span class="has-tooltip-top" onTouchStart={() => {}} data-tooltip={dateTooltip}> + <Tooltip text={i18n.str`Select date to show nearby orders`}> <input class="input" type="text" @@ -229,21 +229,19 @@ export default function OrderList({ setPickDate(true); }} /> - </span> + </Tooltip> </div> <div class="control"> - <span class="has-tooltip-left" onTouchStart={() => {}} data-tooltip={dateTooltip}> + <Tooltip text={i18n.str`Select date to show nearby orders`}> <a class="button is-fullwidth" onClick={() => { setPickDate(true); }} > - <span class="icon"> - <i class="mdi mdi-calendar" /> - </span> + <i class="icon mdi mdi-calendar" /> </a> - </span> + </Tooltip> </div> </div> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx @@ -43,6 +43,7 @@ import { InputSelector } from "../../../../components/form/InputSelector.js"; import { InputWithAddon } from "../../../../components/form/InputWithAddon.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; import { useSessionContext } from "../../../../context/session.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 50; @@ -97,7 +98,8 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { const data = hasErrors ? undefined : (state as TalerMerchantApi.OtpDeviceAddDetails); - const create = safeFunctionHandler(i18n.str`add otp device`, + const create = safeFunctionHandler( + i18n.str`add otp device`, lib.instance.addOtpDevice.bind(lib.instance), !session.token || !data ? undefined : [session.token, data], ); @@ -157,29 +159,28 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { setShowKey(!showKey); }} addonAfter={ - <span class="icon"> - {showKey ? ( - <i class="mdi mdi-eye" /> - ) : ( - <i class="mdi mdi-eye-off" /> - )} - </span> + showKey ? ( + <i class="icon mdi mdi-eye" /> + ) : ( + <i class="icon mdi mdi-eye-off" /> + ) } side={ - <button - type="button" - onTouchStart={() => {}} data-tooltip={i18n.str`Generate random secret key`} - class="button is-info mr-3" - onClick={(e) => { - setState((s) => ({ - ...s, - otp_key: randomRfc3548Base32Key(), - })); - e.preventDefault(); - }} - > - <i18n.Translate>Random</i18n.Translate> - </button> + <Tooltip text={i18n.str`Generate random secret key`}> + <button + type="button" + class="button is-info mr-3" + onClick={(e) => { + setState((s) => ({ + ...s, + otp_key: randomRfc3548Base32Key(), + })); + e.preventDefault(); + }} + > + <i18n.Translate>Random</i18n.Translate> + </button> + </Tooltip> } /> @@ -189,17 +190,17 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - onClick={create} - type="submit" > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma onClick={create} type="submit"> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx @@ -34,6 +34,7 @@ import { import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; import { useSessionContext } from "../../../../context/session.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 51; @@ -60,7 +61,8 @@ export function CardTable({ const { i18n } = useTranslationContext(); - const remove = safeFunctionHandler(i18n.str`delete otp device`, + const remove = safeFunctionHandler( + i18n.str`delete otp device`, lib.instance.deleteOtpDevice.bind(lib.instance), ).lambda((id: string) => (!session.token ? undefined! : [session.token, id])); @@ -81,27 +83,20 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-lock" /> - </span> + <i class="icon mdi mdi-lock" /> <i18n.Translate>OTP devices</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Add new devices`} - > + <Tooltip text={i18n.str`Add new devices`}> <button class="button is-info" type="button" onClick={onCreate} accessKey="+" > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> </div> </header> <div class="card-content"> @@ -148,14 +143,15 @@ function Table({ return ( <div class="table-container"> {onLoadMoreBefore && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more devices before the first one`} - onClick={onLoadMoreBefore} - > - <i18n.Translate>Load first page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more devices before the first one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreBefore} + > + <i18n.Translate>Load first page</i18n.Translate> + </button> + </Tooltip> )} <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> @@ -187,14 +183,17 @@ function Table({ </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <ButtonBetterBulma - type="button" - class="button is-danger is-small has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Delete selected devices from the database`} - onClick={onDelete.withArgs(i.otp_device_id)} + <Tooltip + text={i18n.str`Delete selected devices from the database`} > - <i18n.Translate>Delete</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma + type="button" + class="button is-danger is-small" + onClick={onDelete.withArgs(i.otp_device_id)} + > + <i18n.Translate>Delete</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </td> </tr> @@ -203,14 +202,15 @@ function Table({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more devices after the last one`} - onClick={onLoadMoreAfter} - > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more devices after the last one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </div> ); @@ -222,7 +222,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx @@ -39,6 +39,7 @@ import { InputSelector } from "../../../../components/form/InputSelector.js"; import { InputWithAddon } from "../../../../components/form/InputWithAddon.js"; import { WithId } from "../../../../declaration.js"; import { useSessionContext } from "../../../../context/session.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 52; @@ -135,18 +136,21 @@ export function UpdatePage({ device, onUpdated, onBack }: Props): VNode { setShowKey(!showKey); }} addonAfter={ - <span - class="icon" - onClick={() => { - setShowKey(!showKey); - }} - > - {showKey ? ( - <i class="mdi mdi-eye" /> - ) : ( - <i class="mdi mdi-eye-off" /> - )} - </span> + showKey ? ( + <i + class="icon mdi mdi-eye" + onClick={() => { + setShowKey(false); + }} + /> + ) : ( + <i + class="icon mdi mdi-eye-off" + onClick={() => { + setShowKey(true); + }} + /> + ) } side={ state.otp_key === undefined ? ( @@ -160,19 +164,20 @@ export function UpdatePage({ device, onUpdated, onBack }: Props): VNode { <i18n.Translate>Change key</i18n.Translate> </button> ) : ( - <button - type="button" - onTouchStart={() => {}} data-tooltip={i18n.str`Generate random secret key`} - class="button is-info mr-3" - onClick={() => { - setState((s) => ({ - ...s, - otp_key: randomRfc3548Base32Key(), - })); - }} - > - <i18n.Translate>Random</i18n.Translate> - </button> + <Tooltip text={i18n.str`Generate random secret key`}> + <button + type="button" + class="button is-info mr-3" + onClick={() => { + setState((s) => ({ + ...s, + otp_key: randomRfc3548Base32Key(), + })); + }} + > + <i18n.Translate>Random</i18n.Translate> + </button> + </Tooltip> ) } /> @@ -186,13 +191,11 @@ export function UpdatePage({ device, onUpdated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} data-tooltip={i18n.str`Confirm operation`} - onClick={update} - type="submit" - > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <Tooltip text={i18n.str`Confirm operation`}> + <ButtonBetterBulma onClick={update} type="submit"> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </div> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/password/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/password/DetailPage.tsx @@ -26,10 +26,14 @@ import { } from "@gnu-taler/web-util/browser"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; -import { FormErrors, FormProvider } from "../../../components/form/FormProvider.js"; +import { + FormErrors, + FormProvider, +} from "../../../components/form/FormProvider.js"; import { Input } from "../../../components/form/Input.js"; import { undefinedIfEmpty } from "../../../utils/table.js"; import { InputPassword } from "../../../components/form/InputPassword.js"; +import { Tooltip } from "../../../components/Tooltip.js"; const TALER_SCREEN_ID = 53; @@ -125,21 +129,27 @@ export function DetailPage({ <i18n.Translate>Cancel</i18n.Translate> </a> )} - <ButtonBetterBulma - type="submit" - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - onClick={ - hasErrors - ? changePassword - : changePassword.withArgs(form.current ?? "", form.next!) - } > - <i18n.Translate>Confirm change</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma + type="submit" + onClick={ + hasErrors + ? changePassword + : changePassword.withArgs( + form.current ?? "", + form.next!, + ) + } + > + <i18n.Translate>Confirm change</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/pots/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/pots/create/CreatePage.tsx @@ -19,7 +19,11 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { assertUnreachable, HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { + assertUnreachable, + HttpStatusCode, + TalerMerchantApi, +} from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -35,6 +39,7 @@ import { import { Input } from "../../../../components/form/Input.js"; import { useSessionContext } from "../../../../context/session.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 37; @@ -51,22 +56,17 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { const [state, setState] = useState<Partial<Entity>>({}); const errors = undefinedIfEmpty<FormErrors<Entity>>({ - pot_name: !state.pot_name - ? i18n.str`Required` - : undefined, - description: !state.description - ? i18n.str`Required` - : undefined, + pot_name: !state.pot_name ? i18n.str`Required` : undefined, + description: !state.description ? i18n.str`Required` : undefined, }); const hasErrors = errors !== undefined; const { state: session, lib } = useSessionContext(); const [notification, safeFunctionHandler] = useLocalNotificationBetter(); - const data = !!errors - ? undefined - : (state as TalerMerchantApi.PotAddRequest); - const create = safeFunctionHandler(i18n.str`create money pot`, + const data = !!errors ? undefined : (state as TalerMerchantApi.PotAddRequest); + const create = safeFunctionHandler( + i18n.str`create money pot`, lib.instance.createMoneyPot.bind(lib.instance), !session.token || !data ? undefined : [session.token, data], ); @@ -112,17 +112,17 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - type="submit" - onClick={create} > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma type="submit" onClick={create}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/pots/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/pots/list/Table.tsx @@ -34,6 +34,7 @@ import { import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; import { useSessionContext } from "../../../../context/session.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 38; @@ -60,7 +61,8 @@ export function CardTable({ const { state: session, lib } = useSessionContext(); const [notification, safeFunctionHandler] = useLocalNotificationBetter(); - const remove = safeFunctionHandler(i18n.str`delete money pot`, + const remove = safeFunctionHandler( + i18n.str`delete money pot`, lib.instance.deleteMoneyPot.bind(lib.instance), ).lambda((id: string) => (!session.token ? undefined! : [session.token, id])); remove.onSuccess = () => i18n.str`Money pot deleted`; @@ -81,27 +83,20 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-label" /> - </span> + <i class="icon mdi mdi-label" /> <i18n.Translate>Money pots</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Add new pots`} - > + <Tooltip text={i18n.str`Add new pots`}> <button class="button is-info" accessKey="+" type="button" onClick={onCreate} > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> </div> </header> <div class="card-content"> @@ -148,14 +143,15 @@ function Table({ return ( <div class="table-container"> {onLoadMoreBefore && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more pots before the first one`} - onClick={onLoadMoreBefore} - > - <i18n.Translate>Load first page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more pots before the first one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreBefore} + > + <i18n.Translate>Load first page</i18n.Translate> + </button> + </Tooltip> )} <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> @@ -187,14 +183,17 @@ function Table({ </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <ButtonBetterBulma - type="button" - class="button is-danger is-small has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Delete selected pots from the database`} - onClick={onDelete.withArgs(String(i.pot_serial))} + <Tooltip + text={i18n.str`Delete selected pots from the database`} > - <i18n.Translate>Delete</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma + type="button" + class="button is-danger is-small" + onClick={onDelete.withArgs(String(i.pot_serial))} + > + <i18n.Translate>Delete</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </td> </tr> @@ -203,14 +202,15 @@ function Table({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more pots after the last one`} - onClick={onLoadMoreAfter} - > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more pots after the last one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </div> ); @@ -222,7 +222,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/pots/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/pots/update/UpdatePage.tsx @@ -47,6 +47,7 @@ import { useSessionContext } from "../../../../context/session.js"; import { WithId } from "../../../../declaration.js"; import { InputArray } from "../../../../components/form/InputArray.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 39; @@ -108,7 +109,8 @@ export function UpdatePage({ moneyPot, onUpdated, onBack }: Props): VNode { : undefined, }); - const update = safeFunctionHandler(i18n.str`update money pot`, + const update = safeFunctionHandler( + i18n.str`update money pot`, lib.instance.updateMoneyPot.bind(lib.instance), !token || errors ? undefined : [token, moneyPot.id, data], ); @@ -186,13 +188,11 @@ export function UpdatePage({ moneyPot, onUpdated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - type="button" - onTouchStart={() => {}} data-tooltip={i18n.str`Confirm operation`} - onClick={update} - > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <Tooltip text={i18n.str`Confirm operation`}> + <ButtonBetterBulma type="button" onClick={update}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </div> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx @@ -44,6 +44,7 @@ import { useMoneyPotDetails, } from "../../../../hooks/pots.js"; import { useInstanceProductGroups } from "../../../../hooks/groups.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; export interface Props { onCreate: () => void; @@ -126,17 +127,20 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onClick={create} - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ !create.args ? i18n.str`Please complete the marked fields` - : "confirm operation" + : i18n.str`Confirm operation` } - type="submit" > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma + onClick={create} + type="submit" + > + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </div> <div class="column" /> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx @@ -25,6 +25,7 @@ import { Amounts, HttpStatusCode, TalerMerchantApi, + TranslatedString, assertUnreachable, } from "@gnu-taler/taler-util"; import { @@ -51,6 +52,7 @@ import { dateFormatForPreferences, usePreference, } from "../../../../hooks/preference.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 56; @@ -82,28 +84,21 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-shopping" /> - </span> + <i class="icon mdi mdi-shopping" /> <i18n.Translate>Inventory</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"> {!onCreate ? undefined : ( - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Add product to inventory`} - > + <Tooltip text={i18n.str`Add product to inventory`}> <button class="button is-info" type="button" onClick={onCreate} accessKey="+" > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> )} </div> </header> @@ -252,8 +247,6 @@ function Table({ /> </td> <td - class="has-tooltip-right" - onTouchStart={() => {}} data-tooltip={i.product_name} onClick={() => rowSelection !== i.id && rowSelectionHandler(i.id) } @@ -262,16 +255,16 @@ function Table({ {i.product_name} </td> <td - class="has-tooltip-right" - onTouchStart={() => {}} data-tooltip={i.description} onClick={() => rowSelection !== i.id && rowSelectionHandler(i.id) } style={{ cursor: "pointer" }} > - {i.description.length > 30 - ? i.description.substring(0, 30) + "..." - : i.description} + <Tooltip text={i.description as TranslatedString}> + {i.description.length > 30 + ? i.description.substring(0, 30) + "..." + : i.description} + </Tooltip> </td> <td onClick={() => @@ -341,10 +334,7 @@ function Table({ </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <span - class="has-tooltip-bottom" - onTouchStart={() => {}} data-tooltip={i18n.str`Go to product update page`} - > + <Tooltip text={i18n.str`Go to product update page`}> <button class="button is-small is-success " type="button" @@ -352,11 +342,10 @@ function Table({ > <i18n.Translate>Update</i18n.Translate> </button> - </span> + </Tooltip> {!onDelete ? undefined : ( - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Remove this product from the database`} + <Tooltip + text={i18n.str`Remove this product from the database`} > <button class="button is-small is-danger" @@ -365,7 +354,7 @@ function Table({ > <i18n.Translate>Delete</i18n.Translate> </button> - </span> + </Tooltip> )} </div> </td> @@ -392,14 +381,15 @@ function Table({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more products after the last one`} - onClick={onLoadMoreAfter} - > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more products after the last one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </div> ); @@ -446,10 +436,7 @@ function FastProductWithInfiniteStockUpdateForm({ <button type="button" class="button" onClick={onCancel}> <i18n.Translate>Cancel</i18n.Translate> </button> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Update product with new price`} - > + <Tooltip text={i18n.str`Update product with new price`}> <button type="button" class="button is-info" @@ -462,7 +449,7 @@ function FastProductWithInfiniteStockUpdateForm({ > <i18n.Translate>Confirm update</i18n.Translate> </button> - </span> + </Tooltip> </div> </div> </Fragment> @@ -526,9 +513,8 @@ function FastProductWithManagedStockUpdateForm({ <button type="button" class="button" onClick={onCancel}> <i18n.Translate>Cancel</i18n.Translate> </button> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`There are values with errors.` : i18n.str`Update product with new stock and price` @@ -549,7 +535,7 @@ function FastProductWithManagedStockUpdateForm({ > <i18n.Translate>Confirm</i18n.Translate> </button> - </span> + </Tooltip> </div> </Fragment> ); @@ -569,7 +555,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx @@ -40,6 +40,7 @@ import { Loading } from "../../../../components/exception/loading.js"; import { ErrorLoadingMerchant } from "../../../../components/ErrorLoadingMerchant.js"; import { useInstanceProductGroups } from "../../../../hooks/groups.js"; import { useInstanceMoneyPots } from "../../../../hooks/pots.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 57; @@ -142,17 +143,17 @@ export function UpdatePage({ product, onBack, onConfirm }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onClick={update} - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ !update.args ? i18n.str`Please complete the marked fields` - : "confirm operation" + : i18n.str`Confirm operation` } - type="submit" > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma onClick={update} type="submit"> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </div> <div class="column" /> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reports/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reports/create/CreatePage.tsx @@ -43,6 +43,7 @@ import { useSessionContext } from "../../../../context/session.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; import { assert } from "console"; import { NotificationCardBulma } from "@gnu-taler/web-util/browser"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 37; @@ -198,17 +199,17 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - type="submit" - onClick={create} > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma type="submit" onClick={create}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reports/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reports/list/Table.tsx @@ -36,6 +36,7 @@ import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; import { useSessionContext } from "../../../../context/session.js"; import { durationToString } from "../../../../components/form/InputDuration.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 38; @@ -84,27 +85,20 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-label" /> - </span> + <i class="icon mdi mdi-label" /> <i18n.Translate>Scheduled reports</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Add new reports`} - > + <Tooltip text={i18n.str`Add new reports`}> <button class="button is-info" accessKey="+" type="button" onClick={onCreate} > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> </div> </header> <div class="card-content"> @@ -151,14 +145,15 @@ function Table({ return ( <div class="table-container"> {onLoadMoreBefore && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more devices before the first one`} - onClick={onLoadMoreBefore} - > - <i18n.Translate>Load first page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more devices before the first one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreBefore} + > + <i18n.Translate>Load first page</i18n.Translate> + </button> + </Tooltip> )} <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> @@ -193,14 +188,17 @@ function Table({ </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <ButtonBetterBulma - class="button is-danger is-small has-tooltip-left" - type="button" - onTouchStart={() => {}} data-tooltip={i18n.str`Delete selected scheduled report from the database`} - onClick={onDelete.withArgs(String(i.report_serial))} + <Tooltip + text={i18n.str`Delete selected scheduled report from the database`} > - <i18n.Translate>Delete</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma + class="button is-danger is-small" + type="button" + onClick={onDelete.withArgs(String(i.report_serial))} + > + <i18n.Translate>Delete</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </td> </tr> @@ -209,14 +207,15 @@ function Table({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more devices after the last one`} - onClick={onLoadMoreAfter} - > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more devices after the last one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </div> ); @@ -228,7 +227,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reports/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reports/update/UpdatePage.tsx @@ -43,6 +43,7 @@ import { NotificationCardBulma } from "@gnu-taler/web-util/browser"; import { undefinedIfEmpty } from "../../../../utils/table.js"; import { InputSelector } from "../../../../components/form/InputSelector.js"; import { InputDuration } from "../../../../components/form/InputDuration.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 39; @@ -239,20 +240,18 @@ export function UpdatePage({ report, onUpdated, onBack }: Props): VNode { label={i18n.str`Report frequency shift`} useProtocolDuration /> - + <div class="buttons is-right mt-5"> {onBack && ( <button class="button" onClick={onBack}> <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - type="submit" - onTouchStart={() => {}} data-tooltip={i18n.str`Confirm operation`} - onClick={update} - > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <Tooltip text={i18n.str`Confirm operation`}> + <ButtonBetterBulma type="submit" onClick={update}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/statistics/list/OrdersChart.tsx b/packages/merchant-backoffice-ui/src/paths/instance/statistics/list/OrdersChart.tsx @@ -45,6 +45,7 @@ import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js"; import { LoginPage } from "../../../login/index.js"; import { NotificationCardBulma } from "@gnu-taler/web-util/browser"; import { LineCanvas } from "../../../../components/ChartJS.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 58; @@ -111,13 +112,12 @@ export function OrdersChart({ } const chart = filterOrderCharData(colors, counters.body, filterFromDate); - const dateTooltip = i18n.str`Select date from which to show statistics`; return ( <Fragment> <div class="tabs" style={{ overflow: "inherit" }}> <ul> <li class={!showTable ? "is-active" : ""}> - <div class="has-tooltip-right" onTouchStart={() => {}} data-tooltip={i18n.str`Show chart`}> + <Tooltip text={i18n.str`Show chart`}> <a onClick={() => { setShowTable(false); @@ -125,10 +125,10 @@ export function OrdersChart({ > <i18n.Translate>Orders chart</i18n.Translate> </a> - </div> + </Tooltip> </li> <li class={showTable ? "is-active" : ""}> - <div class="has-tooltip-right" onTouchStart={() => {}} data-tooltip={i18n.str`Show table`}> + <Tooltip text={i18n.str`Show table`}> <a onClick={() => { setShowTable(true); @@ -136,7 +136,7 @@ export function OrdersChart({ > <i18n.Translate>Orders table</i18n.Translate> </a> - </div> + </Tooltip> </li> </ul> </div> @@ -150,17 +150,16 @@ export function OrdersChart({ class="button is-fullwidth" onClick={() => onSelectDate(undefined)} > - <span - class="icon" - onTouchStart={() => {}} data-tooltip={i18n.str`Clear date filter`} - > - <i class="mdi mdi-close" /> - </span> + <Tooltip text={i18n.str`Clear date filter`}> + <i class="icon mdi mdi-close" /> + </Tooltip> </a> </div> )} <div class="control"> - <span class="has-tooltip-top" onTouchStart={() => {}} data-tooltip={dateTooltip}> + <Tooltip + text={i18n.str`Select date from which to show statistics`} + > <input class="input" type="text" @@ -180,21 +179,21 @@ export function OrdersChart({ setPickDate(true); }} /> - </span> + </Tooltip> </div> <div class="control"> - <span class="has-tooltip-left" onTouchStart={() => {}} data-tooltip={dateTooltip}> + <Tooltip + text={i18n.str`Select date from which to show statistics`} + > <a class="button is-fullwidth" onClick={() => { setPickDate(true); }} > - <span class="icon"> - <i class="mdi mdi-calendar" /> - </span> + <i class="icon mdi mdi-calendar" /> </a> - </span> + </Tooltip> </div> </div> </div> @@ -211,9 +210,7 @@ export function OrdersChart({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-shopping" /> - </span> + <i class="icon mdi mdi-shopping" /> {i18n.str`Orders statistics`} </p> </header> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/statistics/list/RevenueChart.tsx b/packages/merchant-backoffice-ui/src/paths/instance/statistics/list/RevenueChart.tsx @@ -48,6 +48,7 @@ import { import { LoginPage } from "../../../login/index.js"; import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js"; import { RelevantTimeUnit, RevenueChartFilter } from "./index.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 59; @@ -134,7 +135,7 @@ export function RevenueChart({ <div class="tabs" style={{ overflow: "inherit" }}> <ul> <li class={!showTable ? "is-active" : ""}> - <div class="has-tooltip-right" onTouchStart={() => {}} data-tooltip={i18n.str`Show chart`}> + <Tooltip text={i18n.str`Show chart`}> <a onClick={() => { setShowTable(false); @@ -142,10 +143,10 @@ export function RevenueChart({ > <i18n.Translate>Revenue chart</i18n.Translate> </a> - </div> + </Tooltip> </li> <li class={showTable ? "is-active" : ""}> - <div class="has-tooltip-right" onTouchStart={() => {}} data-tooltip={i18n.str`Show table`}> + <Tooltip text={i18n.str`Show table`}> <a onClick={() => { setShowTable(true); @@ -153,7 +154,7 @@ export function RevenueChart({ > <i18n.Translate>Revenue table</i18n.Translate> </a> - </div> + </Tooltip> </li> </ul> </div> @@ -212,9 +213,7 @@ export function RevenueChart({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-shopping" /> - </span> + <i class="icon mdi mdi-shopping" /> {i18n.str`Revenue statistics over the past ${ activeFilter.rangeCount } ${translateRange(activeFilter)} for currency '${ diff --git a/packages/merchant-backoffice-ui/src/paths/instance/statistics/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/statistics/list/Table.tsx @@ -24,7 +24,6 @@ import { h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; import { StatSlug } from "./index.js"; - const TALER_SCREEN_ID = 60; type Entity = StatSlug; @@ -52,9 +51,7 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-counter" /> - </span> + <i class="icon mdi mdi-counter" /> <i18n.Translate>Available statistics</i18n.Translate> </p> </header> @@ -73,7 +70,9 @@ export function CardTable({ ) : ( <EmptyTable /> )} - <div style="width: 800px;"><canvas id="orderschartcanvas"></canvas></div> + <div style="width: 800px;"> + <canvas id="orderschartcanvas"></canvas> + </div> </div> </div> </div> @@ -93,7 +92,7 @@ function Table({ amountStats, counterStats, onSelectAmountStat, - onSelectCounterStat + onSelectCounterStat, }: TableProps): VNode { const { i18n } = useTranslationContext(); return ( @@ -104,8 +103,7 @@ function Table({ <th> <i18n.Translate>Description</i18n.Translate> </th> - <th> - </th> + <th></th> <th /> </tr> </thead> @@ -158,13 +156,11 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> - <i18n.Translate> - There are no statistics to list - </i18n.Translate> + <i18n.Translate>There are no statistics to list</i18n.Translate> </p> </div> ); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx @@ -56,6 +56,7 @@ import { import { useSessionContext } from "../../../../context/session.js"; import { useInstanceOtpDevices } from "../../../../hooks/otp.js"; import { UIElement } from "../../../../hooks/preference.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 61; @@ -331,17 +332,20 @@ export function CreatePage({ <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } + > + <ButtonBetterBulma type="submit" onClick={create} > <i18n.Translate>Confirm</i18n.Translate> </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx @@ -23,6 +23,7 @@ import { TalerMerchantApi } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 63; @@ -57,27 +58,20 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-qrcode" /> - </span> + <i class="icon mdi mdi-qrcode" /> <i18n.Translate>Templates</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Add new templates`} - > + <Tooltip text={i18n.str`Add new templates`}> <button class="button is-info" type="button" onClick={onCreate} accessKey="+" > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> </div> </header> <div class="card-content"> @@ -129,14 +123,15 @@ function Table({ return ( <div class="table-container"> {onLoadMoreBefore && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more templates before the first one`} - onClick={onLoadMoreBefore} - > - <i18n.Translate>Load first page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more templates before the first one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreBefore} + > + <i18n.Translate>Load first page</i18n.Translate> + </button> + </Tooltip> )} <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> @@ -168,30 +163,37 @@ function Table({ </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <button - type="button" - class="button is-danger is-small has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Delete selected templates from the database`} - onClick={() => onDelete(i)} + <Tooltip + text={i18n.str`Delete selected templates from the database`} > - <i18n.Translate>Delete</i18n.Translate> - </button> - <button - type="button" - class="button is-info is-small has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Use template to create new order`} - onClick={() => onNewOrder(i)} + <button + type="button" + class="button is-danger is-small" + onClick={() => onDelete(i)} + > + <i18n.Translate>Delete</i18n.Translate> + </button> + </Tooltip> + <Tooltip text={i18n.str`Use template to create new order`}> + <button + type="button" + class="button is-info is-small" + onClick={() => onNewOrder(i)} + > + <i18n.Translate>Test</i18n.Translate> + </button> + </Tooltip> + <Tooltip + text={i18n.str`Generate a QR code for the template.`} > - <i18n.Translate>Test</i18n.Translate> - </button> - <button - type="button" - class="button is-info is-small has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Generate a QR code for the template.`} - onClick={() => onQR(i)} - > - <i18n.Translate>Show QR</i18n.Translate> - </button> + <button + type="button" + class="button is-info is-small" + onClick={() => onQR(i)} + > + <i18n.Translate>Show QR</i18n.Translate> + </button> + </Tooltip> </div> </td> </tr> @@ -200,14 +202,15 @@ function Table({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more templates after the last one`} - onClick={onLoadMoreAfter} - > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more templates after the last one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </div> ); @@ -219,7 +222,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx @@ -59,6 +59,7 @@ import { ComponentPersonaFlag, FragmentPersonaFlag, } from "../../../../components/menu/SideBar.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 65; @@ -392,17 +393,17 @@ export function UpdatePage({ template, onUpdated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ errors !== undefined ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - type="submit" - onClick={update} > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma type="submit" onClick={update}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx @@ -43,6 +43,7 @@ import { import { Input } from "../../../../components/form/Input.js"; import { InputWithAddon } from "../../../../components/form/InputWithAddon.js"; import { useSessionContext } from "../../../../context/session.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 66; @@ -170,17 +171,17 @@ export function UsePage({ <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ errors !== undefined ? i18n.str`Please complete the marked fields` - : "confirm operation" + : i18n.str`Confirm operation` } - type="submit" - onClick={useTemplate} > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma type="submit" onClick={useTemplate}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/create/CreatePage.tsx @@ -52,6 +52,7 @@ import { usePreference, } from "../../../../hooks/preference.js"; import { Input } from "../../../../components/form/Input.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 67; @@ -238,17 +239,17 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onClick={create} - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ !create.args ? i18n.str`Please complete the marked fields` - : "confirm operation" + : i18n.str`Confirm operation` } - type="submit" > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma onClick={create} type="submit"> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/Table.tsx @@ -24,6 +24,7 @@ import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; import { format } from "date-fns"; import { TalerMerchantApi } from "@gnu-taler/taler-util"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 69; @@ -51,27 +52,20 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-clock" /> - </span> + <i class="icon mdi mdi-clock" /> <i18n.Translate>Token Families</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Add token family`} - > + <Tooltip text={i18n.str`Add token family`}> <button class="button is-info" type="button" onClick={onCreate} accessKey="+" > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> </div> </header> <div class="card-content"> @@ -190,11 +184,7 @@ function Table({ </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <span - class="has-tooltip-bottom" - onTouchStart={() => {}} data-tooltip={i18n.str`Go to token family update page`} - - > + <Tooltip text={i18n.str`Go to token family update page`}> <button class="button is-small is-success " type="button" @@ -202,10 +192,9 @@ function Table({ > <i18n.Translate>Update</i18n.Translate> </button> - </span> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Remove this token family from the database`} + </Tooltip> + <Tooltip + text={i18n.str`Remove this token family from the database`} > <button class="button is-small is-danger" @@ -214,7 +203,7 @@ function Table({ > <i18n.Translate>Delete</i18n.Translate> </button> - </span> + </Tooltip> </div> </td> </tr> @@ -233,7 +222,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-emoticon-sad mdi-48px" /> + <i class="icon mdi mdi-emoticon-sad mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/UpdatePage.tsx @@ -48,6 +48,7 @@ import { usePreference, } from "../../../../hooks/preference.js"; import { addDays, addMonths, endOfMonth, format, startOfMonth } from "date-fns"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 70; @@ -211,17 +212,17 @@ export function UpdatePage({ onUpdated, onBack, tokenFamily }: Props) { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - type="submit" - onClick={update} > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma type="submit" onClick={update}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/DetailsPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/DetailsPage.tsx @@ -188,9 +188,7 @@ function DetailsPageInternal({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-star" /> - </span> + <i class="icon mdi mdi-star" /> <i18n.Translate>Transaction details</i18n.Translate> </p> </header> @@ -215,7 +213,7 @@ function DetailsPageInternal({ /> {wt.execution_time && wt.execution_time.t_s !== "never" ? ( <Row - name={i18n.str`Execution time`} + name={i18n.str`Date`} value={format( wt.execution_time.t_s * 1000, datetimeFormatForPreferences(preferences), @@ -245,9 +243,7 @@ function DetailsPageInternal({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-star" /> - </span> + <i class="icon mdi mdi-star" /> <i18n.Translate>Orders in this wire transfer</i18n.Translate> </p> </header> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx @@ -22,7 +22,7 @@ import { Amounts, TalerMerchantApi } from "@gnu-taler/taler-util"; import { RenderAmountBulma, - useTranslationContext + useTranslationContext, } from "@gnu-taler/web-util/browser"; import { format } from "date-fns"; import { h, VNode } from "preact"; @@ -32,6 +32,7 @@ import { datetimeFormatForPreferences, usePreference, } from "../../../../hooks/preference.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 72; @@ -55,9 +56,7 @@ export function CardTableIncoming({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-star" /> - </span> + <i class="icon mdi mdi-star" /> <i18n.Translate>New wire transfers</i18n.Translate> </p> </header> @@ -67,14 +66,17 @@ export function CardTableIncoming({ {transfers.length > 0 ? ( <div class="table-container"> {onLoadMoreBefore && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more wire transfers preceding the first one`} - onClick={onLoadMoreBefore} + <Tooltip + text={i18n.str`Load more wire transfers preceding the first one`} > - <i18n.Translate>Load first page</i18n.Translate> - </button> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreBefore} + > + <i18n.Translate>Load first page</i18n.Translate> + </button> + </Tooltip> )} <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> @@ -125,14 +127,16 @@ export function CardTableIncoming({ <td class="is-actions-cell right-sticky"> {!i.expected_credit_amount ? undefined : ( <div class="buttons is-right"> - <a - class="button is-info is-small has-tooltip-left" - // type="button" - onTouchStart={() => {}} data-tooltip={i18n.str`Show details about the incoming wire transfer.`} - onClick={() => onSelectedToConfirm(i)} + <Tooltip + text={i18n.str`Show details about the incoming wire transfer.`} > - <i18n.Translate>Details</i18n.Translate> - </a> + <a + class="button is-info is-small" + onClick={() => onSelectedToConfirm(i)} + > + <i18n.Translate>Details</i18n.Translate> + </a> + </Tooltip> </div> )} </td> @@ -142,14 +146,17 @@ export function CardTableIncoming({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more transfers after the last one`} - onClick={onLoadMoreAfter} + <Tooltip + text={i18n.str`Load more transfers after the last one`} > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </div> ) : ( @@ -180,9 +187,7 @@ export function CardTableVerified({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-arrow-left-right" /> - </span> + <i class="icon mdi mdi-arrow-left-right" /> <i18n.Translate> Confirmed wire transfers into bank account </i18n.Translate> @@ -194,14 +199,17 @@ export function CardTableVerified({ {transfers.length > 0 ? ( <div class="table-container"> {onLoadMoreBefore && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more wire transfers preceding the first one`} - onClick={onLoadMoreBefore} + <Tooltip + text={i18n.str`Load more wire transfers preceding the first one`} > - <i18n.Translate>Load first page</i18n.Translate> - </button> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreBefore} + > + <i18n.Translate>Load first page</i18n.Translate> + </button> + </Tooltip> )} <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> @@ -248,13 +256,9 @@ export function CardTableVerified({ </td> <td> {i.expected ? ( - <span class="icon"> - <i class="mdi mdi-check" /> - </span> + <i class="icon mdi mdi-check" /> ) : ( - <span class="icon"> - <i class="mdi mdi-close" /> - </span> + <i class="icon mdi mdi-close" /> )} </td> </tr> @@ -263,14 +267,17 @@ export function CardTableVerified({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more transfers after the last one`} - onClick={onLoadMoreAfter} + <Tooltip + text={i18n.str`Load more transfers after the last one`} > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </div> ) : ( @@ -289,7 +296,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/DeletePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/DeletePage.tsx @@ -38,6 +38,7 @@ import { InputToggle } from "../../../components/form/InputToggle.js"; import { SolveMFAChallenges } from "../../../components/SolveMFA.js"; import { useSessionContext } from "../../../context/session.js"; import { undefinedIfEmpty } from "../../../utils/table.js"; +import { Tooltip } from "../../../components/Tooltip.js"; const TALER_SCREEN_ID = 73; @@ -151,18 +152,21 @@ export function DeletePage({ instanceId, onBack, onDeleted }: Props): VNode { </a> )} - <ButtonBetterBulma - class="button is-small is-danger" - type="submit" - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - onClick={remove} > - <i18n.Translate>DELETE</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma + class="button is-small is-danger" + type="submit" + onClick={remove} + > + <i18n.Translate>DELETE</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx @@ -45,6 +45,7 @@ import { SolveMFAChallenges } from "../../../components/SolveMFA.js"; import { useSessionContext } from "../../../context/session.js"; import { undefinedIfEmpty } from "../../../utils/table.js"; import { CopyButton } from "../../../components/modal/index.js"; +import { Tooltip } from "../../../components/Tooltip.js"; const TALER_SCREEN_ID = 75; @@ -238,26 +239,23 @@ export function UpdatePage({ /> <div class="buttons is-right mt-4"> - <button - type="button" - class="button" - onClick={onBack} - onTouchStart={() => {}} data-tooltip="cancel operation" - > - <i18n.Translate>Cancel</i18n.Translate> - </button> + <Tooltip text={i18n.str`Cancel operation`}> + <button type="button" class="button" onClick={onBack}> + <i18n.Translate>Cancel</i18n.Translate> + </button> + </Tooltip> - <ButtonBetterBulma - type="submit" - onClick={update} - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma type="submit" onClick={update}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx @@ -40,6 +40,7 @@ import { Input } from "../../../../components/form/Input.js"; import { InputSelector } from "../../../../components/form/InputSelector.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; import { useSessionContext } from "../../../../context/session.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 76; @@ -128,6 +129,21 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { label={i18n.str`ID`} tooltip={i18n.str`Webhook ID to use`} /> + {/* <Input<Entity> + name="webhook_id" + label={i18n.str`ID`} + tooltip={i18n.str`Super long string that will make the tooltip very big. Super long string that will make the tooltip very big. Super long string that will make the tooltip very big. Super long string that will make the tooltip very big.`} + /> + <Input<Entity> + name="webhook_id" + label={i18n.str`Medium size field`} + tooltip={i18n.str`Super long string that will make the tooltip very big. Super long string that will make the tooltip very big. Super long string that will make the tooltip very big. Super long string that will make the tooltip very big.`} + /> + <Input<Entity> + name="webhook_id" + label={i18n.str`Large field name that is close to edge`} + tooltip={i18n.str`Super long string that will make the tooltip very big. Super long string that will make the tooltip very big. Super long string that will make the tooltip very big. Super long string that will make the tooltip very big.`} + /> */} <InputSelector name="event_type" label={i18n.str`Event`} @@ -279,17 +295,17 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} - <ButtonBetterBulma - onTouchStart={() => {}} data-tooltip={ + <Tooltip + text={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - type="submit" - onClick={create} > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma type="submit" onClick={create}> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx @@ -35,6 +35,7 @@ import { import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; import { useSessionContext } from "../../../../context/session.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 77; @@ -87,27 +88,21 @@ export function CardTable({ <div class="card has-table"> <header class="card-header"> <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-webhook" /> - </span> + <i class="icon mdi mdi-webhook" /> + <i18n.Translate>Webhooks</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Add new webhooks`} - > + <Tooltip text={i18n.str`Add new webhooks`}> <button class="button is-info" type="button" onClick={onCreate} accessKey="+" > - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> + <i class="icon mdi mdi-plus mdi-36px" /> </button> - </span> + </Tooltip> </div> </header> <div class="card-content"> @@ -154,14 +149,15 @@ function Table({ return ( <div class="table-container"> {onLoadMoreBefore && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more webhooks before the first one`} - onClick={onLoadMoreBefore} - > - <i18n.Translate>Load first page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more webhooks before the first one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreBefore} + > + <i18n.Translate>Load first page</i18n.Translate> + </button> + </Tooltip> )} <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> @@ -193,14 +189,17 @@ function Table({ </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <ButtonBetterBulma - type="button" - class="button is-danger is-small has-tooltip-left" - onTouchStart={() => {}} data-tooltip={i18n.str`Delete selected webhook from the database`} - onClick={deleteWebhook.withArgs(i.webhook_id)} + <Tooltip + text={i18n.str`Delete selected webhook from the database`} > - <i18n.Translate>Delete</i18n.Translate> - </ButtonBetterBulma> + <ButtonBetterBulma + type="button" + class="button is-danger is-small" + onClick={deleteWebhook.withArgs(i.webhook_id)} + > + <i18n.Translate>Delete</i18n.Translate> + </ButtonBetterBulma> + </Tooltip> </div> </td> </tr> @@ -209,14 +208,15 @@ function Table({ </tbody> </table> {onLoadMoreAfter && ( - <button - type="button" - class="button is-fullwidth" - onTouchStart={() => {}} data-tooltip={i18n.str`Load more webhooks after the last one`} - onClick={onLoadMoreAfter} - > - <i18n.Translate>Load next page</i18n.Translate> - </button> + <Tooltip text={i18n.str`Load more webhooks after the last one`}> + <button + type="button" + class="button is-fullwidth" + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + </Tooltip> )} </div> ); @@ -228,7 +228,7 @@ function EmptyTable(): VNode { <div class="content has-text-grey has-text-centered"> <p> <span class="icon is-large"> - <i class="mdi mdi-magnify mdi-48px" /> + <i class="icon mdi mdi-magnify mdi-48px" /> </span> </p> <p> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx @@ -41,6 +41,7 @@ import { InputSelector } from "../../../../components/form/InputSelector.js"; import { WithId } from "../../../../declaration.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; import { useSessionContext } from "../../../../context/session.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 78; @@ -217,17 +218,20 @@ export function UpdatePage({ webhook, onConfirm, onBack }: Props): VNode { <i18n.Translate>Cancel</i18n.Translate> </button> )} + <Tooltip + text={ + hasErrors + ? i18n.str`Please complete the marked fields` + : i18n.str`Confirm operation` + } + > <ButtonBetterBulma - onTouchStart={() => {}} data-tooltip={ - hasErrors - ? i18n.str`Please complete the marked fields` - : i18n.str`Confirm operation` - } onClick={update} type="submit" > <i18n.Translate>Confirm</i18n.Translate> </ButtonBetterBulma> + </Tooltip> </div> </FormProvider> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/login/index.tsx b/packages/merchant-backoffice-ui/src/paths/login/index.tsx @@ -40,6 +40,7 @@ import { SolveMFAChallenges } from "../../components/SolveMFA.js"; import { useSessionContext } from "../../context/session.js"; import { FormProvider } from "../../components/form/FormProvider.js"; import { doAutoFocus } from "../../components/form/Input.js"; +import { Tooltip } from "../../components/Tooltip.js"; const TALER_SCREEN_ID = 79; @@ -145,12 +146,9 @@ export function LoginPage({ showCreateAccount, focus }: Props): VNode { <div class="field-label is-normal"> <label class="label"> <i18n.Translate>Username</i18n.Translate> - <span - class="icon has-tooltip-right" - onTouchStart={() => {}} data-tooltip={i18n.str`Instance name.`} - > - <i class="mdi mdi-information" /> - </span> + <Tooltip text={i18n.str`Instance name.`}> + <i class="icon mdi mdi-information" /> + </Tooltip> </label> </div> <div class="field-body"> @@ -175,12 +173,9 @@ export function LoginPage({ showCreateAccount, focus }: Props): VNode { <div class="field-label is-normal"> <label class="label"> <i18n.Translate>Password</i18n.Translate> - <span - class="icon has-tooltip-right" - onTouchStart={() => {}} data-tooltip={i18n.str`Instance password.`} - > - <i class="mdi mdi-information" /> - </span> + <Tooltip text={i18n.str`Instance password.`}> + <i class="icon mdi mdi-information" /> + </Tooltip> </label> </div> <div class="field-body"> @@ -205,13 +200,11 @@ export function LoginPage({ showCreateAccount, focus }: Props): VNode { }} > <a class="button is-static point"> - <span class="icon"> - {hidePassword ? ( - <i class="mdi mdi-eye-off" /> - ) : ( - <i class="mdi mdi-eye" /> - )} - </span> + {hidePassword ? ( + <i class="icon mdi mdi-eye-off" /> + ) : ( + <i class="icon mdi mdi-eye" /> + )} </a> </div> </div> @@ -250,9 +243,7 @@ export function LoginPage({ showCreateAccount, focus }: Props): VNode { {!showCreateAccount ? undefined : ( <div style={{ marginTop: 8 }}> <a href={"#/account/new"} class="has-icon"> - <span class="icon"> - <i class="mdi mdi-account-plus" /> - </span> + <i class="icon mdi mdi-account-plus" /> <span class="menu-item-label"> <i18n.Translate>Create new account</i18n.Translate> </span> diff --git a/packages/merchant-backoffice-ui/src/paths/settings/index.tsx b/packages/merchant-backoffice-ui/src/paths/settings/index.tsx @@ -36,10 +36,11 @@ import { usePreference, } from "../../hooks/preference.js"; import { InputBoolean } from "../../components/form/InputBoolean.js"; +import { Tooltip } from "../../components/Tooltip.js"; const TALER_SCREEN_ID = 83; -type FormType = Preferences & {developer: boolean}; +type FormType = Preferences & { developer: boolean }; export function Settings({ onClose }: { onClose?: () => void }): VNode { const { i18n } = useTranslationContext(); const { config } = useSessionContext(); @@ -50,7 +51,7 @@ export function Settings({ onClose }: { onClose?: () => void }): VNode { const formValue: FormType = { ...value, persona: value.persona ?? config.default_persona, - developer: showDebugInfo + developer: showDebugInfo, }; function valueHandler(s: (d: Partial<FormType>) => Partial<FormType>): void { @@ -89,14 +90,11 @@ export function Settings({ onClose }: { onClose?: () => void }): VNode { <div class="field-label is-normal"> <label class="label"> <i18n.Translate>Language</i18n.Translate> - <span - class="icon has-tooltip-right" - onTouchStart={() => {}} data-tooltip={ - "Force language setting instance of taking the browser" - } + <Tooltip + text={i18n.str`Force language setting instance of taking the browser`} > - <i class="mdi mdi-information" /> - </span> + <i class="icon mdi mdi-information" /> + </Tooltip> </label> </div> <div class="field field-body has-addons is-flex-grow-3"> diff --git a/packages/merchant-backoffice-ui/src/scss/_aside.scss b/packages/merchant-backoffice-ui/src/scss/_aside.scss @@ -58,7 +58,7 @@ aside.aside { position: fixed; top: 0; left: 0; - z-index: 40; + z-index: 35; height: 100%; padding: 0; box-shadow: $aside-box-shadow; diff --git a/packages/merchant-backoffice-ui/src/scss/main.scss b/packages/merchant-backoffice-ui/src/scss/main.scss @@ -70,24 +70,6 @@ $tooltip-color: red; top: 0; } -.toast { - position: absolute; - width: 60%; - margin-left: 10%; - margin-right: 10%; - z-index: 999; - - display: flex; - flex-direction: column; - padding: 15px; - text-align: center; - pointer-events: none; -} - -.toast>.message { - white-space: pre-wrap; - opacity: 80%; -} div { &.is-loading { @@ -162,26 +144,31 @@ input:read-only { } [data-tooltip]:before { - max-width: 15rem; + max-width: 200px; width: max-content; - text-align: left; - transition: opacity 0.1s linear 1s; - // transform: inherit !important; + text-align: center; + transition: opacity 0.1s linear 0.5s; white-space: pre-wrap !important; font-weight: normal; // position: relative; } -.icon[data-tooltip]:before { - transition: none; - z-index: 5; +.mdi-information::before { + font-size: larger; } +// .mdi-information::before:active { +// color: blue; +// } -[data-tooltip]:not(.is-disabled).show-tooltip:before, -[data-tooltip]:not(.is-loading).show-tooltip:before, -[data-tooltip]:not([disabled]).show-tooltip:before { - opacity: 1; - visibility: visible; +[data-tooltip]:not(.is-disabled).has-tooltip-custom:before, +[data-tooltip]:not(.is-loading).has-tooltip-custom:before, +[data-tooltip]:not([disabled]).has-tooltip-custom:before { + bottom: auto; + right: auto; + top: 100%; + left: 0px; + transform: translateX(var(--pos-x)); + z-index: 45; } span[data-tooltip] {