use translation context from web-utils, don't use match react-router since is broken

This commit is contained in:
Sebastian 2022-12-20 17:45:24 -03:00
parent 382e66b179
commit c59f9a2556
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
81 changed files with 1165 additions and 1420 deletions

View File

@ -64,8 +64,7 @@ function getFilesInDirectory(startPath, regex) {
return result
}
const allTestFiles = getFilesInDirectory(path.join(BASE, 'tests'), /.test.tsx?$/)
const allTestFiles = getFilesInDirectory(path.join(BASE, 'src'), /.test.tsx?$/)
const entryPoints = ["src/index.tsx", "src/stories.tsx", ...allTestFiles];
let GIT_ROOT = BASE;

View File

@ -3,12 +3,13 @@
"name": "@gnu-taler/merchant-backoffice",
"version": "0.0.4",
"license": "MIT",
"type": "module",
"scripts": {
"build": "./build.mjs",
"check": "tsc",
"compile": "tsc && ./build.mjs",
"dev": "preact watch --port ${PORT:=8080} --no-sw --no-esm",
"test": "pnpm compile && mocha --require source-map-support/register '*.test.js' 'dist/**/test.js'",
"test": "pnpm compile && mocha --require source-map-support/register 'dist/**/*.test.js' 'dist/**/test.js'",
"lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
"i18n:extract": "pogen extract",
"i18n:merge": "pogen merge",
@ -38,7 +39,7 @@
"date-fns": "2.29.3",
"history": "4.10.1",
"jed": "1.1.1",
"preact": "10.6.5",
"preact": "10.11.3",
"preact-router": "3.2.1",
"qrcode-generator": "1.4.4",
"swr": "1.3.0",
@ -79,22 +80,5 @@
"sass": "1.56.1",
"typedoc": "^0.20.36",
"typescript": "4.8.4"
},
"jest": {
"preset": "jest-preset-preact",
"transformIgnorePatterns": [
"node_modules/.pnpm/(?!(@gnu-taler\\+taler-util))",
"\\.pnp\\.[^\\/]+$"
],
"setupFiles": [
"<rootDir>/tests/__mocks__/browserMocks.ts",
"<rootDir>/tests/__mocks__/setupTests.ts"
],
"moduleNameMapper": {
"\\.(css|less)$": "identity-obj-proxy"
},
"transform": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|po)$": "<rootDir>/tests/__mocks__/fileTransformer.js"
}
}
}
}

View File

@ -19,6 +19,10 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import {
TranslationProvider,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { route } from "preact-router";
import { useMemo } from "preact/hooks";
@ -33,16 +37,15 @@ import {
useBackendContext,
} from "./context/backend.js";
import { ConfigContextProvider } from "./context/config.js";
import { TranslationProvider } from "./context/translation.js";
import { useBackendConfig } from "./hooks/backend.js";
import { useTranslator } from "./i18n/index.js";
import { strings } from "./i18n/strings.js";
import LoginPage from "./paths/login/index.js";
export function Application(): VNode {
return (
// <FetchContextProvider>
<BackendContextProvider>
<TranslationProvider>
<TranslationProvider source={strings}>
<ApplicationStatusRoutes />
</TranslationProvider>
</BackendContextProvider>
@ -53,7 +56,7 @@ export function Application(): VNode {
function ApplicationStatusRoutes(): VNode {
const { updateLoginStatus, triedToLog } = useBackendContext();
const result = useBackendConfig();
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const updateLoginInfoAndGoToRoot = (url: string, token?: string) => {
updateLoginStatus(url, token);
@ -88,7 +91,7 @@ function ApplicationStatusRoutes(): VNode {
<NotYetReadyAppMenu title="Error" />
<NotificationCard
notification={{
message: i18n`Server not found`,
message: i18n.str`Server not found`,
type: "ERROR",
description: `Check your url`,
}}
@ -103,9 +106,9 @@ function ApplicationStatusRoutes(): VNode {
<NotYetReadyAppMenu title="Error" />
<NotificationCard
notification={{
message: i18n`Couldn't access the server`,
message: i18n.str`Couldn't access the server`,
type: "ERROR",
description: i18n`Got message ${result.message} from ${result.info?.url}`,
description: i18n.str`Got message ${result.message} from ${result.info?.url}`,
}}
/>
<LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
@ -120,9 +123,9 @@ function ApplicationStatusRoutes(): VNode {
<NotYetReadyAppMenu title="Error" />
<NotificationCard
notification={{
message: i18n`Unexpected Error`,
message: i18n.str`Unexpected Error`,
type: "ERROR",
description: i18n`Got message ${result.message} from ${result.info?.url}`,
description: i18n.str`Got message ${result.message} from ${result.info?.url}`,
}}
/>
<LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
@ -130,7 +133,7 @@ function ApplicationStatusRoutes(): VNode {
);
return (
<div id="app" class="has-navbar-fixed-top">
<div class="has-navbar-fixed-top">
<ConfigContextProvider value={ctx}>
<ApplicationReadyRoutes />
</ConfigContextProvider>

View File

@ -18,23 +18,23 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { createHashHistory } from "history";
import { Fragment, h, VNode } from "preact";
import Router, { Route, route } from "preact-router";
import { useState } from "preact/hooks";
import {
NotificationCard,
NotYetReadyAppMenu,
} from "./components/menu/index.js";
import { useBackendContext } from "./context/backend.js";
import { useBackendInstancesTestForAdmin } from "./hooks/backend.js";
import { InstanceRoutes } from "./InstanceRoutes.js";
import LoginPage from "./paths/login/index.js";
import { INSTANCE_ID_LOOKUP } from "./utils/constants.js";
import {
NotYetReadyAppMenu,
NotificationCard,
} from "./components/menu/index.js";
import { useTranslator } from "./i18n/index.js";
import { createHashHistory } from "history";
import { useState } from "preact/hooks";
export function ApplicationReadyRoutes(): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const {
url: backendURL,
updateLoginStatus,
@ -54,8 +54,8 @@ export function ApplicationReadyRoutes(): VNode {
<NotYetReadyAppMenu title="Login" onLogout={clearTokenAndGoToRoot} />
<NotificationCard
notification={{
message: i18n`Access denied`,
description: i18n`Check your token is valid`,
message: i18n.str`Access denied`,
description: i18n.str`Check your token is valid`,
type: "ERROR",
}}
/>
@ -81,8 +81,8 @@ export function ApplicationReadyRoutes(): VNode {
<NotYetReadyAppMenu title="Error" onLogout={clearTokenAndGoToRoot} />
<NotificationCard
notification={{
message: i18n`Couldn't access the server.`,
description: i18n`Could not infer instance id from url ${backendURL}`,
message: i18n.str`Couldn't access the server.`,
description: i18n.str`Could not infer instance id from url ${backendURL}`,
type: "ERROR",
}}
/>
@ -108,7 +108,12 @@ export function ApplicationReadyRoutes(): VNode {
);
}
function DefaultMainRoute({ instance, admin, instanceNameByBackendURL }: any) {
function DefaultMainRoute({
instance,
admin,
instanceNameByBackendURL,
url, //from preact-router
}: any): VNode {
const [instanceName, setInstanceName] = useState(
instanceNameByBackendURL || instance || "default",
);
@ -116,6 +121,7 @@ function DefaultMainRoute({ instance, admin, instanceNameByBackendURL }: any) {
return (
<InstanceRoutes
admin={admin}
path={url}
id={instanceName}
setInstanceName={setInstanceName}
/>

View File

@ -19,6 +19,8 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { format } from "date-fns";
import { Fragment, FunctionComponent, h, VNode } from "preact";
import { Route, route, Router } from "preact-router";
import { useCallback, useEffect, useMemo, useState } from "preact/hooks";
@ -26,39 +28,37 @@ import { Loading } from "./components/exception/loading.js";
import { Menu, NotificationCard } from "./components/menu/index.js";
import { useBackendContext } from "./context/backend.js";
import { InstanceContextProvider } from "./context/instance.js";
import { HttpError } from "./hooks/backend.js";
import {
useBackendDefaultToken,
useBackendInstanceToken,
useLocalStorage,
} from "./hooks/index.js";
import { HttpError } from "./hooks/backend.js";
import { Translate, useTranslator } from "./i18n/index.js";
import { useInstanceKYCDetails } from "./hooks/instance.js";
import InstanceCreatePage from "./paths/admin/create/index.js";
import InstanceListPage from "./paths/admin/list/index.js";
import ListKYCPage from "./paths/instance/kyc/list/index.js";
import OrderCreatePage from "./paths/instance/orders/create/index.js";
import OrderDetailsPage from "./paths/instance/orders/details/index.js";
import OrderListPage from "./paths/instance/orders/list/index.js";
import ProductCreatePage from "./paths/instance/products/create/index.js";
import ProductListPage from "./paths/instance/products/list/index.js";
import ProductUpdatePage from "./paths/instance/products/update/index.js";
import TransferListPage from "./paths/instance/transfers/list/index.js";
import TransferCreatePage from "./paths/instance/transfers/create/index.js";
import TemplateListPage from "./paths/instance/templates/list/index.js";
import TemplateUpdatePage from "./paths/instance/templates/update/index.js";
import TemplateCreatePage from "./paths/instance/templates/create/index.js";
import ReservesCreatePage from "./paths/instance/reserves/create/index.js";
import ReservesDetailsPage from "./paths/instance/reserves/details/index.js";
import ReservesListPage from "./paths/instance/reserves/list/index.js";
import ListKYCPage from "./paths/instance/kyc/list/index.js";
import TemplateCreatePage from "./paths/instance/templates/create/index.js";
import TemplateListPage from "./paths/instance/templates/list/index.js";
import TemplateUpdatePage from "./paths/instance/templates/update/index.js";
import TransferCreatePage from "./paths/instance/transfers/create/index.js";
import TransferListPage from "./paths/instance/transfers/list/index.js";
import InstanceUpdatePage, {
Props as InstanceUpdatePageProps,
AdminUpdate as InstanceAdminUpdatePage,
Props as InstanceUpdatePageProps,
} from "./paths/instance/update/index.js";
import LoginPage from "./paths/login/index.js";
import NotFoundPage from "./paths/notfound/index.js";
import { Notification } from "./utils/types.js";
import { useInstanceKYCDetails } from "./hooks/instance.js";
import { format } from "date-fns";
export enum InstancePaths {
// details = '/',
@ -99,10 +99,16 @@ export enum AdminPaths {
export interface Props {
id: string;
admin?: boolean;
path: string;
setInstanceName: (s: string) => void;
}
export function InstanceRoutes({ id, admin, setInstanceName }: Props): VNode {
export function InstanceRoutes({
id,
admin,
path,
setInstanceName,
}: Props): VNode {
const [_, updateDefaultToken] = useBackendDefaultToken();
const [token, updateToken] = useBackendInstanceToken(id);
const {
@ -113,7 +119,7 @@ export function InstanceRoutes({ id, admin, setInstanceName }: Props): VNode {
const cleaner = useCallback(() => {
updateToken(undefined);
}, [id]);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
type GlobalNotifState = (Notification & { to: string }) | undefined;
const [globalNotification, setGlobalNotification] =
@ -144,8 +150,8 @@ export function InstanceRoutes({ id, admin, setInstanceName }: Props): VNode {
function ServerErrorRedirectTo(to: InstancePaths | AdminPaths) {
return function ServerErrorRedirectToImpl(error: HttpError) {
setGlobalNotification({
message: i18n`The backend reported a problem: HTTP status #${error.status}`,
description: i18n`Diagnostic from ${error.info?.url} is "${error.message}"`,
message: i18n.str`The backend reported a problem: HTTP status #${error.status}`,
description: i18n.str`Diagnostic from ${error.info?.url} is "${error.message}"`,
details:
error.clientError || error.serverError
? error.error?.detail
@ -161,8 +167,8 @@ export function InstanceRoutes({ id, admin, setInstanceName }: Props): VNode {
<Fragment>
<NotificationCard
notification={{
message: i18n`Access denied`,
description: i18n`The access token provided is invalid.`,
message: i18n.str`Access denied`,
description: i18n.str`The access token provided is invalid.`,
type: "ERROR",
}}
/>
@ -177,8 +183,8 @@ export function InstanceRoutes({ id, admin, setInstanceName }: Props): VNode {
<Fragment>
<NotificationCard
notification={{
message: i18n`No 'default' instance configured yet.`,
description: i18n`Create a 'default' instance to begin using the merchant backoffice.`,
message: i18n.str`No 'default' instance configured yet.`,
description: i18n.str`Create a 'default' instance to begin using the merchant backoffice.`,
type: "INFO",
}}
/>
@ -208,6 +214,7 @@ export function InstanceRoutes({ id, admin, setInstanceName }: Props): VNode {
<Menu
instance={id}
admin={admin}
path={path}
onLogout={clearTokenAndGoToRoot}
setInstanceName={setInstanceName}
/>
@ -488,7 +495,7 @@ function AdminInstanceUpdatePage({
() => ({ id, token, admin: true, changeToken }),
[id, token],
);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<InstanceContextProvider value={value}>
@ -500,8 +507,8 @@ function AdminInstanceUpdatePage({
<Fragment>
<NotificationCard
notification={{
message: i18n`The backend reported a problem: HTTP status #${error.status}`,
description: i18n`Diagnostic from ${error.info?.url} is "${error.message}"`,
message: i18n.str`The backend reported a problem: HTTP status #${error.status}`,
description: i18n.str`Diagnostic from ${error.info?.url} is "${error.message}"`,
details:
error.clientError || error.serverError
? error.error?.detail
@ -518,8 +525,8 @@ function AdminInstanceUpdatePage({
<Fragment>
<NotificationCard
notification={{
message: i18n`Access denied`,
description: i18n`The access token provided is invalid`,
message: i18n.str`Access denied`,
description: i18n.str`The access token provided is invalid`,
type: "ERROR",
}}
/>
@ -534,6 +541,7 @@ function AdminInstanceUpdatePage({
function KycBanner(): VNode {
const kycStatus = useInstanceKYCDetails();
const { i18n } = useTranslationContext();
const today = format(new Date(), "yyyy-MM-dd");
const [lastHide, setLastHide] = useLocalStorage("kyc-last-hide");
const hasBeenHidden = today === lastHide;
@ -552,7 +560,7 @@ function KycBanner(): VNode {
</p>
<div class="buttons is-right">
<button class="button" onClick={() => setLastHide(today)}>
<Translate>Hide for today</Translate>
<i18n.Translate>Hide for today</i18n.Translate>
</button>
</div>
</div>

View File

@ -22,7 +22,7 @@
import { ComponentChildren, h } from "preact";
import { LoadingModal } from "../modal/index.js";
import { useAsync } from "../../hooks/async.js";
import { Translate } from "../../i18n/index.js";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
type Props = {
children: ComponentChildren;
@ -33,14 +33,14 @@ type Props = {
export function AsyncButton({ onClick, disabled, children, ...rest }: Props) {
const { isSlow, isLoading, request, cancel } = useAsync(onClick);
const { i18n } = useTranslationContext();
if (isSlow) {
return <LoadingModal onCancel={cancel} />;
}
if (isLoading) {
return (
<button class="button">
<Translate>Loading...</Translate>
<i18n.Translate>Loading...</i18n.Translate>
</button>
);
}

View File

@ -19,11 +19,11 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { useBackendContext } from "../../context/backend.js";
import { useInstanceContext } from "../../context/instance.js";
import { Translate, useTranslator } from "../../i18n/index.js";
import { Notification } from "../../utils/types.js";
interface Props {
@ -51,7 +51,7 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
const [token, setToken] = useState(currentToken);
const [url, setURL] = useState(backendUrl);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="columns is-centered">
@ -61,13 +61,13 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
class="modal-card-head"
style={{ border: "1px solid", borderBottom: 0 }}
>
<p class="modal-card-title">{i18n`Login required`}</p>
<p class="modal-card-title">{i18n.str`Login required`}</p>
</header>
<section
class="modal-card-body"
style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
>
<Translate>Please enter your access token.</Translate>
<i18n.Translate>Please enter your access token.</i18n.Translate>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">URL</label>
@ -95,7 +95,7 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">
<Translate>Access Token</Translate>
<i18n.Translate>Access Token</i18n.Translate>
</label>
</div>
<div class="field-body">
@ -133,7 +133,7 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
onConfirm(url, normalizeToken(token));
}}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</button>
</footer>
</div>

View File

@ -18,9 +18,9 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Translate, useTranslator } from "../../i18n/index.js";
import { InputProps, useField } from "./useField.js";
export interface Props<T> extends InputProps<T> {
@ -52,7 +52,7 @@ export function InputArray<T>({
const array: any[] = (value ? value! : []) as any;
const [currentValue, setCurrentValue] = useState("");
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="field is-horizontal">
@ -99,7 +99,7 @@ export function InputArray<T>({
const v = fromStr(currentValue);
if (!isValid(v)) {
setLocalError(
i18n`The value ${v} is invalid for a payment url`,
i18n.str`The value ${v} is invalid for a payment url`,
);
return;
}
@ -107,9 +107,9 @@ export function InputArray<T>({
onChange([v, ...array] as any);
setCurrentValue("");
}}
data-tooltip={i18n`add element to the list`}
data-tooltip={i18n.str`add element to the list`}
>
<Translate>add</Translate>
<i18n.Translate>add</i18n.Translate>
</button>
</p>
</div>

View File

@ -18,7 +18,7 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { ComponentChildren, h } from "preact";
import { ComponentChildren, h, VNode } from "preact";
import { useConfigContext } from "../../context/config.js";
import { Amount } from "../../declaration.js";
import { InputWithAddon } from "./InputWithAddon.js";
@ -42,7 +42,7 @@ export function InputCurrency<T>({
addonAfter,
children,
side,
}: Props<keyof T>) {
}: Props<keyof T>): VNode {
const config = useConfigContext();
return (
<InputWithAddon<T>
@ -60,7 +60,8 @@ export function InputCurrency<T>({
toStr={(v?: Amount) => v?.split(":")[1] || ""}
fromStr={(v: string) => (!v ? "" : `${config.currency}:${v}`)}
inputExtra={{ min: 0 }}
children={children}
/>
>
{children}
</InputWithAddon>
);
}

View File

@ -18,10 +18,10 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { format } from "date-fns";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Translate, useTranslator } from "../../i18n/index.js";
import { DatePicker } from "../picker/DatePicker.js";
import { InputProps, useField } from "./useField.js";
@ -43,7 +43,7 @@ export function InputDate<T>({
withTimestampSupport,
}: Props<keyof T>): VNode {
const [opened, setOpened] = useState(false);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const { error, required, value, onChange } = useField<T>(name);
@ -120,25 +120,25 @@ export function InputDate<T>({
<span
data-tooltip={
withTimestampSupport
? i18n`change value to unknown date`
: i18n`change value to empty`
? i18n.str`change value to unknown date`
: i18n.str`change value to empty`
}
>
<button
class="button is-info mr-3"
onClick={() => onChange(undefined as any)}
>
<Translate>clear</Translate>
<i18n.Translate>clear</i18n.Translate>
</button>
</span>
)}
{withTimestampSupport && (
<span data-tooltip={i18n`change value to never`}>
<span data-tooltip={i18n.str`change value to never`}>
<button
class="button is-info"
onClick={() => onChange({ t_s: "never" } as any)}
>
<Translate>never</Translate>
<i18n.Translate>never</i18n.Translate>
</button>
</span>
)}

View File

@ -18,10 +18,10 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { intervalToDuration, formatDuration } from "date-fns";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { formatDuration, intervalToDuration } from "date-fns";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Translate, useTranslator } from "../../i18n/index.js";
import { SimpleModal } from "../modal/index.js";
import { DurationPicker } from "../picker/DurationPicker.js";
import { InputProps, useField } from "./useField.js";
@ -43,14 +43,14 @@ export function InputDuration<T>({
withForever,
}: Props<keyof T>): VNode {
const [opened, setOpened] = useState(false);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const { error, required, value, onChange } = useField<T>(name);
let strValue = "";
if (!value) {
strValue = "";
} else if (value.d_us === "forever") {
strValue = i18n`forever`;
strValue = i18n.str`forever`;
} else {
strValue = formatDuration(
intervalToDuration({ start: 0, end: value.d_us / 1000 }),
@ -59,17 +59,17 @@ export function InputDuration<T>({
formatDistance: (name, value) => {
switch (name) {
case "xMonths":
return i18n`${value}M`;
return i18n.str`${value}M`;
case "xYears":
return i18n`${value}Y`;
return i18n.str`${value}Y`;
case "xDays":
return i18n`${value}d`;
return i18n.str`${value}d`;
case "xHours":
return i18n`${value}h`;
return i18n.str`${value}h`;
case "xMinutes":
return i18n`${value}min`;
return i18n.str`${value}min`;
case "xSeconds":
return i18n`${value}sec`;
return i18n.str`${value}sec`;
}
},
localize: {
@ -134,22 +134,22 @@ export function InputDuration<T>({
{error && <p class="help is-danger">{error}</p>}
</div>
{withForever && (
<span data-tooltip={i18n`change value to never`}>
<span data-tooltip={i18n.str`change value to never`}>
<button
class="button is-info mr-3"
onClick={() => onChange({ d_us: "forever" } as any)}
>
<Translate>forever</Translate>
<i18n.Translate>forever</i18n.Translate>
</button>
</span>
)}
{!readonly && (
<span data-tooltip={i18n`change value to empty`}>
<span data-tooltip={i18n.str`change value to empty`}>
<button
class="button is-info "
onClick={() => onChange(undefined as any)}
>
<Translate>clear</Translate>
<i18n.Translate>clear</i18n.Translate>
</button>
</span>
)}

View File

@ -18,9 +18,9 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { ComponentChildren, h, VNode } from "preact";
import { useRef, useState } from "preact/hooks";
import { Translate } from "../../i18n/index.js";
import { MAX_IMAGE_SIZE as MAX_IMAGE_UPLOAD_SIZE } from "../../utils/constants.js";
import { InputProps, useField } from "./useField.js";
@ -43,7 +43,7 @@ export function InputImage<T>({
const { error, value, onChange } = useField<T>(name);
const image = useRef<HTMLInputElement>(null);
const { i18n } = useTranslationContext();
const [sizeError, setSizeError] = useState(false);
return (
@ -102,17 +102,17 @@ export function InputImage<T>({
{error && <p class="help is-danger">{error}</p>}
{sizeError && (
<p class="help is-danger">
<Translate>Image should be smaller than 1 MB</Translate>
<i18n.Translate>Image should be smaller than 1 MB</i18n.Translate>
</p>
)}
{!value && (
<button class="button" onClick={() => image.current?.click()}>
<Translate>Add</Translate>
<i18n.Translate>Add</i18n.Translate>
</button>
)}
{value && (
<button class="button" onClick={() => onChange(undefined!)}>
<Translate>Remove</Translate>
<i18n.Translate>Remove</i18n.Translate>
</button>
)}
</div>

View File

@ -19,31 +19,34 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { Fragment, h } from "preact";
import { useTranslator } from "../../i18n/index.js";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Input } from "./Input.js";
export function InputLocation({ name }: { name: string }) {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<>
<Input name={`${name}.country`} label={i18n`Country`} />
<Input name={`${name}.country`} label={i18n.str`Country`} />
<Input
name={`${name}.address_lines`}
inputType="multiline"
label={i18n`Address`}
label={i18n.str`Address`}
toStr={(v: string[] | undefined) => (!v ? "" : v.join("\n"))}
fromStr={(v: string) => v.split("\n")}
/>
<Input name={`${name}.building_number`} label={i18n`Building number`} />
<Input name={`${name}.building_name`} label={i18n`Building name`} />
<Input name={`${name}.street`} label={i18n`Street`} />
<Input name={`${name}.post_code`} label={i18n`Post code`} />
<Input name={`${name}.town_location`} label={i18n`Town location`} />
<Input name={`${name}.town`} label={i18n`Town`} />
<Input name={`${name}.district`} label={i18n`District`} />
<Input
name={`${name}.building_number`}
label={i18n.str`Building number`}
/>
<Input name={`${name}.building_name`} label={i18n.str`Building name`} />
<Input name={`${name}.street`} label={i18n.str`Street`} />
<Input name={`${name}.post_code`} label={i18n.str`Post code`} />
<Input name={`${name}.town_location`} label={i18n.str`Town location`} />
<Input name={`${name}.town`} label={i18n.str`Town`} />
<Input name={`${name}.district`} label={i18n.str`District`} />
<Input
name={`${name}.country_subdivision`}
label={i18n`Country subdivision`}
label={i18n.str`Country subdivision`}
/>
</>
);

View File

@ -18,9 +18,9 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { h, VNode, Fragment } from "preact";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useCallback, useState } from "preact/hooks";
import { Translate, Translator, useTranslator } from "../../i18n/index.js";
import { COUNTRY_TABLE } from "../../utils/constants.js";
import { undefinedIfEmpty } from "../../utils/table.js";
import { FormErrors, FormProvider } from "./FormProvider.js";
@ -69,24 +69,30 @@ function checkAddressChecksum(address: string) {
return true;
}
function validateBitcoin(addr: string, i18n: Translator): string | undefined {
function validateBitcoin(
addr: string,
i18n: ReturnType<typeof useTranslationContext>["i18n"],
): string | undefined {
try {
const valid = /^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,39}$/.test(addr);
if (valid) return undefined;
} catch (e) {
console.log(e);
}
return i18n`This is not a valid bitcoin address.`;
return i18n.str`This is not a valid bitcoin address.`;
}
function validateEthereum(addr: string, i18n: Translator): string | undefined {
function validateEthereum(
addr: string,
i18n: ReturnType<typeof useTranslationContext>["i18n"],
): string | undefined {
try {
const valid = isEthereumAddress(addr);
if (valid) return undefined;
} catch (e) {
console.log(e);
}
return i18n`This is not a valid Ethereum address.`;
return i18n.str`This is not a valid Ethereum address.`;
}
/**
@ -103,12 +109,15 @@ function validateEthereum(addr: string, i18n: Translator): string | undefined {
* If the remainder is 1, the check digit test is passed and the IBAN might be valid.
*
*/
function validateIBAN(iban: string, i18n: Translator): string | undefined {
function validateIBAN(
iban: string,
i18n: ReturnType<typeof useTranslationContext>["i18n"],
): string | undefined {
// Check total length
if (iban.length < 4)
return i18n`IBAN numbers usually have more that 4 digits`;
return i18n.str`IBAN numbers usually have more that 4 digits`;
if (iban.length > 34)
return i18n`IBAN numbers usually have less that 34 digits`;
return i18n.str`IBAN numbers usually have less that 34 digits`;
const A_code = "A".charCodeAt(0);
const Z_code = "Z".charCodeAt(0);
@ -116,7 +125,7 @@ function validateIBAN(iban: string, i18n: Translator): string | undefined {
// check supported country
const code = IBAN.substr(0, 2);
const found = code in COUNTRY_TABLE;
if (!found) return i18n`IBAN country code not found`;
if (!found) return i18n.str`IBAN country code not found`;
// 2.- Move the four initial characters to the end of the string
const step2 = IBAN.substr(4) + iban.substr(0, 4);
@ -140,7 +149,8 @@ function validateIBAN(iban: string, i18n: Translator): string | undefined {
}
const checksum = calculate_iban_checksum(step3);
if (checksum !== 1) return i18n`IBAN number is not valid, checksum is wrong`;
if (checksum !== 1)
return i18n.str`IBAN number is not valid, checksum is wrong`;
return undefined;
}
@ -175,7 +185,7 @@ export function InputPaytoForm<T>({
payToPath = `/${value.path1}`;
}
}
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const ops = value.options!;
const url = tryUrl(`payto://${value.target}${payToPath}`);
@ -188,9 +198,9 @@ export function InputPaytoForm<T>({
const paytoURL = !url ? "" : url.toString();
const errors: FormErrors<Entity> = {
target: value.target === noTargetValue ? i18n`required` : undefined,
target: value.target === noTargetValue ? i18n.str`required` : undefined,
path1: !value.path1
? i18n`required`
? i18n.str`required`
: value.target === "iban"
? validateIBAN(value.path1, i18n)
: value.target === "bitcoin"
@ -201,12 +211,12 @@ export function InputPaytoForm<T>({
path2:
value.target === "x-taler-bank"
? !value.path2
? i18n`required`
? i18n.str`required`
: undefined
: undefined,
options: undefinedIfEmpty({
"receiver-name": !value.options?.["receiver-name"]
? i18n`required`
? i18n.str`required`
: undefined,
}),
};
@ -235,23 +245,23 @@ export function InputPaytoForm<T>({
>
<InputSelector<Entity>
name="target"
label={i18n`Target type`}
tooltip={i18n`Method to use for wire transfer`}
label={i18n.str`Target type`}
tooltip={i18n.str`Method to use for wire transfer`}
values={targets}
toStr={(v) => (v === noTargetValue ? i18n`Choose one...` : v)}
toStr={(v) => (v === noTargetValue ? i18n.str`Choose one...` : v)}
/>
{value.target === "ach" && (
<Fragment>
<Input<Entity>
name="path1"
label={i18n`Routing`}
tooltip={i18n`Routing number.`}
label={i18n.str`Routing`}
tooltip={i18n.str`Routing number.`}
/>
<Input<Entity>
name="path2"
label={i18n`Account`}
tooltip={i18n`Account number.`}
label={i18n.str`Account`}
tooltip={i18n.str`Account number.`}
/>
</Fragment>
)}
@ -259,8 +269,8 @@ export function InputPaytoForm<T>({
<Fragment>
<Input<Entity>
name="path1"
label={i18n`Code`}
tooltip={i18n`Business Identifier Code.`}
label={i18n.str`Code`}
tooltip={i18n.str`Business Identifier Code.`}
/>
</Fragment>
)}
@ -268,8 +278,8 @@ export function InputPaytoForm<T>({
<Fragment>
<Input<Entity>
name="path1"
label={i18n`Account`}
tooltip={i18n`Bank Account Number.`}
label={i18n.str`Account`}
tooltip={i18n.str`Bank Account Number.`}
inputExtra={{ style: { textTransform: "uppercase" } }}
/>
</Fragment>
@ -278,8 +288,8 @@ export function InputPaytoForm<T>({
<Fragment>
<Input<Entity>
name="path1"
label={i18n`Account`}
tooltip={i18n`Unified Payment Interface.`}
label={i18n.str`Account`}
tooltip={i18n.str`Unified Payment Interface.`}
/>
</Fragment>
)}
@ -287,8 +297,8 @@ export function InputPaytoForm<T>({
<Fragment>
<Input<Entity>
name="path1"
label={i18n`Address`}
tooltip={i18n`Bitcoin protocol.`}
label={i18n.str`Address`}
tooltip={i18n.str`Bitcoin protocol.`}
/>
</Fragment>
)}
@ -296,8 +306,8 @@ export function InputPaytoForm<T>({
<Fragment>
<Input<Entity>
name="path1"
label={i18n`Address`}
tooltip={i18n`Ethereum protocol.`}
label={i18n.str`Address`}
tooltip={i18n.str`Ethereum protocol.`}
/>
</Fragment>
)}
@ -305,8 +315,8 @@ export function InputPaytoForm<T>({
<Fragment>
<Input<Entity>
name="path1"
label={i18n`Address`}
tooltip={i18n`Interledger protocol.`}
label={i18n.str`Address`}
tooltip={i18n.str`Interledger protocol.`}
/>
</Fragment>
)}
@ -315,13 +325,13 @@ export function InputPaytoForm<T>({
<Fragment>
<Input<Entity>
name="path1"
label={i18n`Host`}
tooltip={i18n`Bank host.`}
label={i18n.str`Host`}
tooltip={i18n.str`Bank host.`}
/>
<Input<Entity>
name="path2"
label={i18n`Account`}
tooltip={i18n`Bank account.`}
label={i18n.str`Account`}
tooltip={i18n.str`Bank account.`}
/>
</Fragment>
)}
@ -329,8 +339,8 @@ export function InputPaytoForm<T>({
{value.target !== noTargetValue && (
<Input
name="options.receiver-name"
label={i18n`Name`}
tooltip={i18n`Bank account owner's name.`}
label={i18n.str`Name`}
tooltip={i18n.str`Bank account owner's name.`}
/>
)}
@ -357,7 +367,7 @@ export function InputPaytoForm<T>({
/>
</div>
))}
{!paytos.length && i18n`No accounts yet.`}
{!paytos.length && i18n.str`No accounts yet.`}
</div>
</div>
@ -365,11 +375,11 @@ export function InputPaytoForm<T>({
<div class="buttons is-right mt-5">
<button
class="button is-info"
data-tooltip={i18n`add tax to the tax list`}
data-tooltip={i18n.str`add tax to the tax list`}
disabled={hasErrors}
onClick={submit}
>
<Translate>Add</Translate>
<i18n.Translate>Add</i18n.Translate>
</button>
</div>
)}

View File

@ -18,11 +18,11 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import emptyImage from "../../assets/empty.png";
import { MerchantBackend, WithId } from "../../declaration.js";
import { Translate, useTranslator } from "../../i18n/index.js";
import { FormErrors, FormProvider } from "./FormProvider.js";
import { InputWithAddon } from "./InputWithAddon.js";
@ -50,7 +50,7 @@ export function InputSearchProduct({
const errors: FormErrors<ProductSearch> = {
name: undefined,
};
const i18n = useTranslator();
const { i18n } = useTranslationContext();
if (selected) {
return (
@ -63,10 +63,11 @@ export function InputSearchProduct({
<div class="media-content">
<div class="content">
<p class="media-meta">
<Translate>Product id</Translate>: <b>{selected.id}</b>
<i18n.Translate>Product id</i18n.Translate>: <b>{selected.id}</b>
</p>
<p>
<Translate>Description</Translate>: {selected.description}
<i18n.Translate>Description</i18n.Translate>:{" "}
{selected.description}
</p>
<div class="buttons is-right mt-5">
<button
@ -90,8 +91,8 @@ export function InputSearchProduct({
>
<InputWithAddon<ProductSearch>
name="name"
label={i18n`Product`}
tooltip={i18n`search products by it's description or id`}
label={i18n.str`Product`}
tooltip={i18n.str`search products by it's description or id`}
addonAfter={
<span class="icon">
<i class="mdi mdi-magnify" />
@ -120,6 +121,7 @@ interface ProductListProps {
}
function ProductList({ name, onSelect, list }: ProductListProps) {
const { i18n } = useTranslationContext();
if (!name) {
/* FIXME
this BR is added to occupy the space that will be added when the
@ -146,7 +148,9 @@ function ProductList({ name, onSelect, list }: ProductListProps) {
<div class="dropdown-content">
{!filtered.length ? (
<div class="dropdown-item">
<Translate>no products found with that description</Translate>
<i18n.Translate>
no products found with that description
</i18n.Translate>
</div>
) : (
filtered.map((p) => (

View File

@ -18,14 +18,15 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Translate, useTranslator } from "../../i18n/index.js";
import { InputProps, useField } from "./useField.js";
export type Props<T> = InputProps<T>;
const TokenStatus = ({ prev, post }: any) => {
const { i18n } = useTranslationContext();
if (
(prev === undefined || prev === null) &&
(post === undefined || post === null)
@ -33,11 +34,11 @@ const TokenStatus = ({ prev, post }: any) => {
return null;
return prev === post ? null : post === null ? (
<span class="tag is-danger is-align-self-center ml-2">
<Translate>Deleting</Translate>
<i18n.Translate>Deleting</i18n.Translate>
</span>
) : (
<span class="tag is-warning is-align-self-center ml-2">
<Translate>Changing</Translate>
<i18n.Translate>Changing</i18n.Translate>
</span>
);
};
@ -55,7 +56,7 @@ export function InputSecured<T>({
const [active, setActive] = useState(false);
const [newValue, setNuewValue] = useState("");
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<Fragment>
@ -84,7 +85,7 @@ export function InputSecured<T>({
<i class="mdi mdi-lock-reset" />
</div>
<span>
<Translate>Manage access token</Translate>
<i18n.Translate>Manage access token</i18n.Translate>
</span>
</button>
<TokenStatus prev={initial} post={value} />
@ -125,7 +126,7 @@ export function InputSecured<T>({
<i class="mdi mdi-lock-outline" />
</div>
<span>
<Translate>Update</Translate>
<i18n.Translate>Update</i18n.Translate>
</span>
</button>
</div>
@ -154,7 +155,7 @@ export function InputSecured<T>({
<i class="mdi mdi-lock-open-variant" />
</div>
<span>
<Translate>Remove</Translate>
<i18n.Translate>Remove</i18n.Translate>
</span>
</button>
</div>
@ -171,7 +172,7 @@ export function InputSecured<T>({
<i class="mdi mdi-lock-open-variant" />
</div>
<span>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</span>
</button>
</div>

View File

@ -18,17 +18,16 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h } from "preact";
import { MerchantBackend, Timestamp } from "../../declaration.js";
import { InputProps, useField } from "./useField.js";
import { FormProvider, FormErrors } from "./FormProvider.js";
import { useLayoutEffect, useState } from "preact/hooks";
import { Input } from "./Input.js";
import { InputGroup } from "./InputGroup.js";
import { InputNumber } from "./InputNumber.js";
import { MerchantBackend, Timestamp } from "../../declaration.js";
import { FormErrors, FormProvider } from "./FormProvider.js";
import { InputDate } from "./InputDate.js";
import { Translate, useTranslator } from "../../i18n/index.js";
import { InputGroup } from "./InputGroup.js";
import { InputLocation } from "./InputLocation.js";
import { InputNumber } from "./InputNumber.js";
import { InputProps, useField } from "./useField.js";
export interface Props<T> extends InputProps<T> {
alreadyExist?: boolean;
@ -64,7 +63,7 @@ export function InputStock<T>({
incoming: 0,
lost: 0,
});
const i18n = useTranslator();
const { i18n } = useTranslationContext();
useLayoutEffect(() => {
if (!formValue) {
@ -97,7 +96,7 @@ export function InputStock<T>({
{!alreadyExist ? (
<button
class="button"
data-tooltip={i18n`click here to configure the stock of the product, leave it as is and the backend will not control stock`}
data-tooltip={i18n.str`click here to configure the stock of the product, leave it as is and the backend will not control stock`}
onClick={(): void => {
valueHandler({
current: 0,
@ -107,17 +106,17 @@ export function InputStock<T>({
}}
>
<span>
<Translate>Manage stock</Translate>
<i18n.Translate>Manage stock</i18n.Translate>
</span>
</button>
) : (
<button
class="button"
data-tooltip={i18n`this product has been configured without stock control`}
data-tooltip={i18n.str`this product has been configured without stock control`}
disabled
>
<span>
<Translate>Infinite</Translate>
<i18n.Translate>Infinite</i18n.Translate>
</span>
</button>
)}
@ -134,7 +133,7 @@ export function InputStock<T>({
const stockAddedErrors: FormErrors<typeof addedStock> = {
lost:
currentStock + addedStock.incoming < addedStock.lost
? i18n`lost cannot be greater than current and incoming (max ${
? i18n.str`lost cannot be greater than current and incoming (max ${
currentStock + addedStock.incoming
})`
: undefined,
@ -142,8 +141,8 @@ export function InputStock<T>({
// const stockUpdateDescription = stockAddedErrors.lost ? '' : (
// !!addedStock.incoming || !!addedStock.lost ?
// i18n`current stock will change from ${currentStock} to ${currentStock + addedStock.incoming - addedStock.lost}` :
// i18n`current stock will stay at ${currentStock}`
// i18n.str`current stock will change from ${currentStock} to ${currentStock + addedStock.incoming - addedStock.lost}` :
// i18n.str`current stock will stay at ${currentStock}`
// )
return (
@ -174,8 +173,8 @@ export function InputStock<T>({
object={addedStock}
valueHandler={setAddedStock as any}
>
<InputNumber name="incoming" label={i18n`Incoming`} />
<InputNumber name="lost" label={i18n`Lost`} />
<InputNumber name="incoming" label={i18n.str`Incoming`} />
<InputNumber name="lost" label={i18n.str`Lost`} />
</FormProvider>
{/* <div class="field is-horizontal">
@ -190,17 +189,17 @@ export function InputStock<T>({
) : (
<InputNumber<Entity>
name="current"
label={i18n`Current`}
label={i18n.str`Current`}
side={
<button
class="button is-danger"
data-tooltip={i18n`remove stock control for this product`}
data-tooltip={i18n.str`remove stock control for this product`}
onClick={(): void => {
valueHandler(undefined as any);
}}
>
<span>
<Translate>without stock</Translate>
<i18n.Translate>without stock</i18n.Translate>
</span>
</button>
}
@ -209,11 +208,14 @@ export function InputStock<T>({
<InputDate<Entity>
name="nextRestock"
label={i18n`Next restock`}
label={i18n.str`Next restock`}
withTimestampSupport
/>
<InputGroup<Entity> name="address" label={i18n`Delivery address`}>
<InputGroup<Entity>
name="address"
label={i18n.str`Delivery address`}
>
<InputLocation name="address" />
</InputGroup>
</FormProvider>

View File

@ -18,11 +18,11 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useCallback, useState } from "preact/hooks";
import * as yup from "yup";
import { MerchantBackend } from "../../declaration.js";
import { Translate, useTranslator } from "../../i18n/index.js";
import { TaxSchema as schema } from "../../schemas/index.js";
import { FormErrors, FormProvider } from "./FormProvider.js";
import { Input } from "./Input.js";
@ -67,7 +67,7 @@ export function InputTaxes<T>({
valueHandler({});
}, [value]);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
//FIXME: translating plural singular
return (
@ -110,34 +110,35 @@ export function InputTaxes<T>({
/>
</div>
))}
{!taxes.length && i18n`No taxes configured for this product.`}
{!taxes.length && i18n.str`No taxes configured for this product.`}
</div>
</div>
<Input<Entity>
name="tax"
label={i18n`Amount`}
tooltip={i18n`Taxes can be in currencies that differ from the main currency used by the merchant.`}
label={i18n.str`Amount`}
tooltip={i18n.str`Taxes can be in currencies that differ from the main currency used by the merchant.`}
>
<Translate>
Enter currency and value separated with a colon, e.g. "USD:2.3".
</Translate>
<i18n.Translate>
Enter currency and value separated with a colon, e.g.
&quot;USD:2.3&quot;.
</i18n.Translate>
</Input>
<Input<Entity>
name="name"
label={i18n`Description`}
tooltip={i18n`Legal name of the tax, e.g. VAT or import duties.`}
label={i18n.str`Description`}
tooltip={i18n.str`Legal name of the tax, e.g. VAT or import duties.`}
/>
<div class="buttons is-right mt-5">
<button
class="button is-info"
data-tooltip={i18n`add tax to the tax list`}
data-tooltip={i18n.str`add tax to the tax list`}
disabled={hasErrors}
onClick={submit}
>
<Translate>Add</Translate>
<i18n.Translate>Add</i18n.Translate>
</button>
</div>
</FormProvider>

View File

@ -19,9 +19,9 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useBackendContext } from "../../context/backend.js";
import { useTranslator } from "../../i18n/index.js";
import { Entity } from "../../paths/admin/create/CreatePage.js";
import { Input } from "../form/Input.js";
import { InputCurrency } from "../form/InputCurrency.js";
@ -39,7 +39,7 @@ export function DefaultInstanceFormFields({
readonlyId?: boolean;
showId: boolean;
}): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const backend = useBackendContext();
return (
<Fragment>
@ -48,86 +48,86 @@ export function DefaultInstanceFormFields({
name="id"
addonBefore={`${backend.url}/instances/`}
readonly={readonlyId}
label={i18n`Identifier`}
tooltip={i18n`Name of the instance in URLs. The 'default' instance is special in that it is used to administer other instances.`}
label={i18n.str`Identifier`}
tooltip={i18n.str`Name of the instance in URLs. The 'default' instance is special in that it is used to administer other instances.`}
/>
)}
<Input<Entity>
name="name"
label={i18n`Business name`}
tooltip={i18n`Legal name of the business represented by this instance.`}
label={i18n.str`Business name`}
tooltip={i18n.str`Legal name of the business represented by this instance.`}
/>
<Input<Entity>
name="email"
label={i18n`Email`}
tooltip={i18n`Contact email`}
label={i18n.str`Email`}
tooltip={i18n.str`Contact email`}
/>
<Input<Entity>
name="website"
label={i18n`Website URL`}
tooltip={i18n`URL.`}
label={i18n.str`Website URL`}
tooltip={i18n.str`URL.`}
/>
<InputImage<Entity>
name="logo"
label={i18n`Logo`}
tooltip={i18n`Logo image.`}
label={i18n.str`Logo`}
tooltip={i18n.str`Logo image.`}
/>
<InputPaytoForm<Entity>
name="payto_uris"
label={i18n`Bank account`}
tooltip={i18n`URI specifying bank account for crediting revenue.`}
label={i18n.str`Bank account`}
tooltip={i18n.str`URI specifying bank account for crediting revenue.`}
/>
<InputCurrency<Entity>
name="default_max_deposit_fee"
label={i18n`Default max deposit fee`}
tooltip={i18n`Maximum deposit fees this merchant is willing to pay per order by default.`}
label={i18n.str`Default max deposit fee`}
tooltip={i18n.str`Maximum deposit fees this merchant is willing to pay per order by default.`}
/>
<InputCurrency<Entity>
name="default_max_wire_fee"
label={i18n`Default max wire fee`}
tooltip={i18n`Maximum wire fees this merchant is willing to pay per wire transfer by default.`}
label={i18n.str`Default max wire fee`}
tooltip={i18n.str`Maximum wire fees this merchant is willing to pay per wire transfer by default.`}
/>
<Input<Entity>
name="default_wire_fee_amortization"
label={i18n`Default wire fee amortization`}
tooltip={i18n`Number of orders excess wire transfer fees will be divided by to compute per order surcharge.`}
label={i18n.str`Default wire fee amortization`}
tooltip={i18n.str`Number of orders excess wire transfer fees will be divided by to compute per order surcharge.`}
/>
<InputGroup
name="address"
label={i18n`Address`}
tooltip={i18n`Physical location of the merchant.`}
label={i18n.str`Address`}
tooltip={i18n.str`Physical location of the merchant.`}
>
<InputLocation name="address" />
</InputGroup>
<InputGroup
name="jurisdiction"
label={i18n`Jurisdiction`}
tooltip={i18n`Jurisdiction for legal disputes with the merchant.`}
label={i18n.str`Jurisdiction`}
tooltip={i18n.str`Jurisdiction for legal disputes with the merchant.`}
>
<InputLocation name="jurisdiction" />
</InputGroup>
<InputDuration<Entity>
name="default_pay_delay"
label={i18n`Default payment delay`}
label={i18n.str`Default payment delay`}
withForever
tooltip={i18n`Time customers have to pay an order before the offer expires by default.`}
tooltip={i18n.str`Time customers have to pay an order before the offer expires by default.`}
/>
<InputDuration<Entity>
name="default_wire_transfer_delay"
label={i18n`Default wire transfer delay`}
tooltip={i18n`Maximum time an exchange is allowed to delay wiring funds to the merchant, enabling it to aggregate smaller payments into larger wire transfers and reducing wire fees.`}
label={i18n.str`Default wire transfer delay`}
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.`}
withForever
/>
</Fragment>

View File

@ -19,11 +19,11 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import langIcon from "../../assets/icons/languageicon.svg";
import { useTranslationContext } from "../../context/translation.js";
import { strings as messages } from "../../i18n/strings";
import { strings as messages } from "../../i18n/strings.js";
type LangsNames = {
[P in keyof typeof messages]: string;

View File

@ -19,13 +19,11 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useCallback } from "preact/hooks";
import { useBackendContext } from "../../context/backend.js";
import { useConfigContext } from "../../context/config.js";
import { useInstanceContext } from "../../context/instance.js";
import { useInstanceKYCDetails } from "../../hooks/instance.js";
import { Translate } from "../../i18n/index.js";
import { LangSelector } from "./LangSelector.js";
const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
@ -48,7 +46,7 @@ export function Sidebar({
}: Props): VNode {
const config = useConfigContext();
const backend = useBackendContext();
const { i18n } = useTranslationContext();
const kycStatus = useInstanceKYCDetails();
const needKYC = kycStatus.ok && kycStatus.data.type === "redirect";
@ -81,7 +79,7 @@ export function Sidebar({
{instance ? (
<Fragment>
<p class="menu-label">
<Translate>Instance</Translate>
<i18n.Translate>Instance</i18n.Translate>
</p>
<ul class="menu-list">
<li>
@ -90,7 +88,7 @@ export function Sidebar({
<i class="mdi mdi-square-edit-outline" />
</span>
<span class="menu-item-label">
<Translate>Settings</Translate>
<i18n.Translate>Settings</i18n.Translate>
</span>
</a>
</li>
@ -100,7 +98,7 @@ export function Sidebar({
<i class="mdi mdi-cash-register" />
</span>
<span class="menu-item-label">
<Translate>Orders</Translate>
<i18n.Translate>Orders</i18n.Translate>
</span>
</a>
</li>
@ -110,7 +108,7 @@ export function Sidebar({
<i class="mdi mdi-shopping" />
</span>
<span class="menu-item-label">
<Translate>Products</Translate>
<i18n.Translate>Products</i18n.Translate>
</span>
</a>
</li>
@ -120,7 +118,7 @@ export function Sidebar({
<i class="mdi mdi-bank" />
</span>
<span class="menu-item-label">
<Translate>Transfers</Translate>
<i18n.Translate>Transfers</i18n.Translate>
</span>
</a>
</li>
@ -130,7 +128,7 @@ export function Sidebar({
<i class="mdi mdi-newspaper" />
</span>
<span class="menu-item-label">
<Translate>Templates</Translate>
<i18n.Translate>Templates</i18n.Translate>
</span>
</a>
</li>
@ -156,7 +154,7 @@ export function Sidebar({
</Fragment>
) : undefined}
<p class="menu-label">
<Translate>Connection</Translate>
<i18n.Translate>Connection</i18n.Translate>
</p>
<ul class="menu-list">
<li>
@ -190,7 +188,7 @@ export function Sidebar({
{admin && !mimic && (
<Fragment>
<p class="menu-label">
<Translate>Instances</Translate>
<i18n.Translate>Instances</i18n.Translate>
</p>
<li>
<a href={"/instance/new"} class="has-icon">
@ -198,7 +196,7 @@ export function Sidebar({
<i class="mdi mdi-plus" />
</span>
<span class="menu-item-label">
<Translate>New</Translate>
<i18n.Translate>New</i18n.Translate>
</span>
</a>
</li>
@ -208,7 +206,7 @@ export function Sidebar({
<i class="mdi mdi-format-list-bulleted" />
</span>
<span class="menu-item-label">
<Translate>List</Translate>
<i18n.Translate>List</i18n.Translate>
</span>
</a>
</li>
@ -223,7 +221,7 @@ export function Sidebar({
<i class="mdi mdi-logout default" />
</span>
<span class="menu-item-label">
<Translate>Log out</Translate>
<i18n.Translate>Log out</i18n.Translate>
</span>
</a>
</li>

View File

@ -15,7 +15,6 @@
*/
import { ComponentChildren, Fragment, h, VNode } from "preact";
import Match from "preact-router/match";
import { useEffect, useState } from "preact/hooks";
import { AdminPaths } from "../../AdminRoutes.js";
import { InstancePaths } from "../../InstanceRoutes.js";
@ -58,6 +57,7 @@ function getAdminTitle(path: string, instance: string) {
interface MenuProps {
title?: string;
path: string;
instance: string;
admin?: boolean;
onLogout?: () => void;
@ -81,64 +81,59 @@ export function Menu({
onLogout,
title,
instance,
path,
admin,
setInstanceName,
}: MenuProps): VNode {
const [mobileOpen, setMobileOpen] = useState(false);
const titleWithSubtitle = title
? title
: !admin
? getInstanceTitle(path, instance)
: getAdminTitle(path, instance);
const adminInstance = instance === "default";
const mimic = admin && !adminInstance;
return (
<Match>
{({ path }: any) => {
const titleWithSubtitle = title
? title
: !admin
? getInstanceTitle(path, instance)
: getAdminTitle(path, instance);
const adminInstance = instance === "default";
const mimic = admin && !adminInstance;
return (
<WithTitle title={titleWithSubtitle}>
<div
class={mobileOpen ? "has-aside-mobile-expanded" : ""}
onClick={() => setMobileOpen(false)}
>
<NavigationBar
onMobileMenu={() => setMobileOpen(!mobileOpen)}
title={titleWithSubtitle}
/>
<WithTitle title={titleWithSubtitle}>
<div
class={mobileOpen ? "has-aside-mobile-expanded" : ""}
onClick={() => setMobileOpen(false)}
>
<NavigationBar
onMobileMenu={() => setMobileOpen(!mobileOpen)}
title={titleWithSubtitle}
/>
{onLogout && (
<Sidebar
onLogout={onLogout}
admin={admin}
mimic={mimic}
instance={instance}
mobile={mobileOpen}
/>
)}
{onLogout && (
<Sidebar
onLogout={onLogout}
admin={admin}
mimic={mimic}
instance={instance}
mobile={mobileOpen}
/>
)}
{mimic && (
<nav class="level">
<div class="level-item has-text-centered has-background-warning">
<p class="is-size-5">
You are viewing the instance <b>"{instance}"</b>.{" "}
<a
href="#/instances"
onClick={(e) => {
setInstanceName("default");
}}
>
go back
</a>
</p>
</div>
</nav>
)}
{mimic && (
<nav class="level">
<div class="level-item has-text-centered has-background-warning">
<p class="is-size-5">
You are viewing the instance <b>&quot;{instance}&quot;</b>.{" "}
<a
href="#/instances"
onClick={(e) => {
setInstanceName("default");
}}
>
go back
</a>
</p>
</div>
</WithTitle>
);
}}
</Match>
</nav>
)}
</div>
</WithTitle>
);
}

View File

@ -19,12 +19,12 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { ComponentChildren, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { useInstanceContext } from "../../context/instance.js";
import { Translate, useTranslator } from "../../i18n/index.js";
import { DEFAULT_REQUEST_TIMEOUT } from "../../utils/constants.js";
import { Loading, Spinner } from "../exception/loading.js";
import { Spinner } from "../exception/loading.js";
import { FormProvider } from "../form/FormProvider.js";
import { Input } from "../form/Input.js";
@ -49,6 +49,7 @@ export function ConfirmModal({
disabled,
label = "Confirm",
}: Props): VNode {
const { i18n } = useTranslationContext();
return (
<div class={active ? "modal is-active" : "modal"}>
<div class="modal-background " onClick={onCancel} />
@ -65,14 +66,14 @@ export function ConfirmModal({
<footer class="modal-card-foot">
<div class="buttons is-right" style={{ width: "100%" }}>
<button class="button " onClick={onCancel}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
<button
class={danger ? "button is-danger " : "button is-info "}
disabled={disabled}
onClick={onConfirm}
>
<Translate>{label}</Translate>
<i18n.Translate>{label}</i18n.Translate>
</button>
</div>
</footer>
@ -94,6 +95,7 @@ export function ContinueModal({
children,
disabled,
}: Props): VNode {
const { i18n } = useTranslationContext();
return (
<div class={active ? "modal is-active" : "modal"}>
<div class="modal-background " onClick={onCancel} />
@ -110,7 +112,7 @@ export function ContinueModal({
disabled={disabled}
onClick={onConfirm}
>
<Translate>Continue</Translate>
<i18n.Translate>Continue</i18n.Translate>
</button>
</div>
</footer>
@ -147,6 +149,7 @@ export function ClearConfirmModal({
onConfirm,
children,
}: Props & { onClear?: () => void }): VNode {
const { i18n } = useTranslationContext();
return (
<div class="modal is-active">
<div class="modal-background " onClick={onCancel} />
@ -163,19 +166,19 @@ export function ClearConfirmModal({
onClick={onClear}
disabled={onClear === undefined}
>
<Translate>Clear</Translate>
<i18n.Translate>Clear</i18n.Translate>
</button>
)}
<div class="buttons is-right" style={{ width: "100%" }}>
<button class="button " onClick={onCancel}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
<button
class="button is-info"
onClick={onConfirm}
disabled={onConfirm === undefined}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</button>
</div>
</footer>
@ -210,7 +213,7 @@ export function DeleteModal({
onConfirm={() => onConfirm(element.id)}
>
<p>
If you delete the instance named <b>"{element.name}"</b> (ID:{" "}
If you delete the instance named <b>&quot;{element.name}&quot;</b> (ID:{" "}
<b>{element.id}</b>), the merchant will no longer be able to process
orders or refunds
</p>
@ -241,12 +244,13 @@ export function PurgeModal({
onConfirm={() => onConfirm(element.id)}
>
<p>
If you purge the instance named <b>"{element.name}"</b> (ID:{" "}
<b>{element.id}</b>), you will also delete all it's transaction data.
If you purge the instance named <b>&quot;{element.name}&quot;</b> (ID:{" "}
<b>{element.id}</b>), you will also delete all it&apos;s transaction
data.
</p>
<p>
The instance will disappear from your list, and you will no longer be
able to access it's data.
able to access it&apos;s data.
</p>
<p class="warning">
Purging an instance <b>cannot be undone</b>.
@ -275,20 +279,22 @@ export function UpdateTokenModal({
new_token: "",
repeat_token: "",
});
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const hasInputTheCorrectOldToken = oldToken && oldToken !== form.old_token;
const errors = {
old_token: hasInputTheCorrectOldToken
? i18n`is not the same as the current access token`
? i18n.str`is not the same as the current access token`
: undefined,
new_token: !form.new_token
? i18n`cannot be empty`
? i18n.str`cannot be empty`
: form.new_token === form.old_token
? i18n`cannot be the same as the old token`
? i18n.str`cannot be the same as the old token`
: undefined,
repeat_token:
form.new_token !== form.repeat_token ? i18n`is not the same` : undefined,
form.new_token !== form.repeat_token
? i18n.str`is not the same`
: undefined,
};
const hasErrors = Object.keys(errors).some(
@ -297,7 +303,7 @@ export function UpdateTokenModal({
const instance = useInstanceContext();
const text = i18n`You are updating the access token from instance with id ${instance.id}`;
const text = i18n.str`You are updating the access token from instance with id ${instance.id}`;
return (
<ClearConfirmModal
@ -313,28 +319,28 @@ export function UpdateTokenModal({
{oldToken && (
<Input<State>
name="old_token"
label={i18n`Old access token`}
tooltip={i18n`access token currently in use`}
label={i18n.str`Old access token`}
tooltip={i18n.str`access token currently in use`}
inputType="password"
/>
)}
<Input<State>
name="new_token"
label={i18n`New access token`}
tooltip={i18n`next access token to be used`}
label={i18n.str`New access token`}
tooltip={i18n.str`next access token to be used`}
inputType="password"
/>
<Input<State>
name="repeat_token"
label={i18n`Repeat access token`}
tooltip={i18n`confirm the same access token`}
label={i18n.str`Repeat access token`}
tooltip={i18n.str`confirm the same access token`}
inputType="password"
/>
</FormProvider>
<p>
<Translate>
<i18n.Translate>
Clearing the access token will mean public access to the instance
</Translate>
</i18n.Translate>
</p>
</div>
<div class="column" />
@ -353,16 +359,18 @@ export function SetTokenNewInstanceModal({
new_token: "",
repeat_token: "",
});
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const errors = {
new_token: !form.new_token
? i18n`cannot be empty`
? i18n.str`cannot be empty`
: form.new_token === form.old_token
? i18n`cannot be the same as the old access token`
? i18n.str`cannot be the same as the old access token`
: undefined,
repeat_token:
form.new_token !== form.repeat_token ? i18n`is not the same` : undefined,
form.new_token !== form.repeat_token
? i18n.str`is not the same`
: undefined,
};
const hasErrors = Object.keys(errors).some(
@ -374,7 +382,7 @@ export function SetTokenNewInstanceModal({
<div class="modal-background " onClick={onCancel} />
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">{i18n`You are setting the access token for the new instance`}</p>
<p class="modal-card-title">{i18n.str`You are setting the access token for the new instance`}</p>
<button class="delete " aria-label="close" onClick={onCancel} />
</header>
<section class="modal-card-body is-main-section">
@ -388,22 +396,22 @@ export function SetTokenNewInstanceModal({
>
<Input<State>
name="new_token"
label={i18n`New access token`}
tooltip={i18n`next access token to be used`}
label={i18n.str`New access token`}
tooltip={i18n.str`next access token to be used`}
inputType="password"
/>
<Input<State>
name="repeat_token"
label={i18n`Repeat access token`}
tooltip={i18n`confirm the same access token`}
label={i18n.str`Repeat access token`}
tooltip={i18n.str`confirm the same access token`}
inputType="password"
/>
</FormProvider>
<p>
<Translate>
<i18n.Translate>
With external authorization method no check will be done by
the merchant backend
</Translate>
</i18n.Translate>
</p>
</div>
<div class="column" />
@ -416,19 +424,19 @@ export function SetTokenNewInstanceModal({
onClick={onClear}
disabled={onClear === undefined}
>
<Translate>Set external authorization</Translate>
<i18n.Translate>Set external authorization</i18n.Translate>
</button>
)}
<div class="buttons is-right" style={{ width: "100%" }}>
<button class="button " onClick={onCancel}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
<button
class="button is-info"
onClick={() => onConfirm(form.new_token!)}
disabled={hasErrors}
>
<Translate>Set access token</Translate>
<i18n.Translate>Set access token</i18n.Translate>
</button>
</div>
</footer>
@ -443,14 +451,14 @@ export function SetTokenNewInstanceModal({
}
export function LoadingModal({ onCancel }: { onCancel: () => void }): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="modal is-active">
<div class="modal-background " onClick={onCancel} />
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">
<Translate>Operation in progress...</Translate>
<i18n.Translate>Operation in progress...</i18n.Translate>
</p>
</header>
<section class="modal-card-body">
@ -459,12 +467,12 @@ export function LoadingModal({ onCancel }: { onCancel: () => void }): VNode {
<Spinner />
<div class="column" />
</div>
<p>{i18n`The operation will be automatically canceled after ${DEFAULT_REQUEST_TIMEOUT} seconds`}</p>
<p>{i18n.str`The operation will be automatically canceled after ${DEFAULT_REQUEST_TIMEOUT} seconds`}</p>
</section>
<footer class="modal-card-foot">
<div class="buttons is-right" style={{ width: "100%" }}>
<button class="button " onClick={onCancel}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
</div>
</footer>

View File

@ -19,9 +19,9 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { useTranslator } from "../../i18n/index.js";
import "../../scss/DurationPicker.scss";
export interface Props {
@ -46,13 +46,13 @@ export function DurationPicker({
const ms = ss * 60;
const hs = ms * 60;
const ds = hs * 24;
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="rdp-picker">
{days && (
<DurationColumn
unit={i18n`days`}
unit={i18n.str`days`}
max={99}
value={Math.floor(value / ds)}
onDecrease={value >= ds ? () => onChange(value - ds) : undefined}
@ -62,7 +62,7 @@ export function DurationPicker({
)}
{hours && (
<DurationColumn
unit={i18n`hours`}
unit={i18n.str`hours`}
max={23}
min={1}
value={Math.floor(value / hs) % 24}
@ -73,7 +73,7 @@ export function DurationPicker({
)}
{minutes && (
<DurationColumn
unit={i18n`minutes`}
unit={i18n.str`minutes`}
max={59}
min={1}
value={Math.floor(value / ms) % 60}
@ -84,7 +84,7 @@ export function DurationPicker({
)}
{seconds && (
<DurationColumn
unit={i18n`seconds`}
unit={i18n.str`seconds`}
max={59}
value={Math.floor(value / ss) % 60}
onDecrease={value >= ss ? () => onChange(value - ss) : undefined}

View File

@ -13,14 +13,14 @@
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 { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { FormProvider, FormErrors } from "../form/FormProvider.js";
import { MerchantBackend, WithId } from "../../declaration.js";
import { ProductMap } from "../../paths/instance/orders/create/CreatePage.js";
import { FormErrors, FormProvider } from "../form/FormProvider.js";
import { InputNumber } from "../form/InputNumber.js";
import { InputSearchProduct } from "../form/InputSearchProduct.js";
import { MerchantBackend, WithId } from "../../declaration.js";
import { Translate, useTranslator } from "../../i18n/index.js";
import { ProductMap } from "../../paths/instance/orders/create/CreatePage.js";
type Form = {
product: MerchantBackend.Products.ProductDetail & WithId;
@ -45,21 +45,23 @@ export function InventoryProductForm({
const [state, setState] = useState<Partial<Form>>(initialState);
const [errors, setErrors] = useState<FormErrors<Form>>({});
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const productWithInfiniteStock =
state.product && state.product.total_stock === -1;
const submit = (): void => {
if (!state.product) {
setErrors({ product: i18n`You must enter a valid product identifier.` });
setErrors({
product: i18n.str`You must enter a valid product identifier.`,
});
return;
}
if (productWithInfiniteStock) {
onAddProduct(state.product, 1);
} else {
if (!state.quantity || state.quantity <= 0) {
setErrors({ quantity: i18n`Quantity must be greater than 0!` });
setErrors({ quantity: i18n.str`Quantity must be greater than 0!` });
return;
}
const currentStock =
@ -71,7 +73,7 @@ export function InventoryProductForm({
if (state.quantity + p.quantity > currentStock) {
const left = currentStock - p.quantity;
setErrors({
quantity: i18n`This quantity exceeds remaining stock. Currently, only ${left} units remain unreserved in stock.`,
quantity: i18n.str`This quantity exceeds remaining stock. Currently, only ${left} units remain unreserved in stock.`,
});
return;
}
@ -80,7 +82,7 @@ export function InventoryProductForm({
if (state.quantity > currentStock) {
const left = currentStock;
setErrors({
quantity: i18n`This quantity exceeds remaining stock. Currently, only ${left} units remain unreserved in stock.`,
quantity: i18n.str`This quantity exceeds remaining stock. Currently, only ${left} units remain unreserved in stock.`,
});
return;
}
@ -104,15 +106,15 @@ export function InventoryProductForm({
{!productWithInfiniteStock && (
<InputNumber<Form>
name="quantity"
label={i18n`Quantity`}
tooltip={i18n`how many products will be added`}
label={i18n.str`Quantity`}
tooltip={i18n.str`how many products will be added`}
/>
)}
</div>
<div class="column">
<div class="buttons is-right">
<button class="button is-success" onClick={submit}>
<Translate>Add from inventory</Translate>
<i18n.Translate>Add from inventory</i18n.Translate>
</button>
</div>
</div>

View File

@ -13,19 +13,19 @@
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 { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useCallback, useEffect, useState } from "preact/hooks";
import * as yup from "yup";
import { MerchantBackend } from "../../declaration.js";
import { useListener } from "../../hooks/listener.js";
import { NonInventoryProductSchema as schema } from "../../schemas/index.js";
import { FormErrors, FormProvider } from "../form/FormProvider.js";
import { Input } from "../form/Input.js";
import { InputCurrency } from "../form/InputCurrency.js";
import { InputImage } from "../form/InputImage.js";
import { InputNumber } from "../form/InputNumber.js";
import { InputTaxes } from "../form/InputTaxes.js";
import { MerchantBackend } from "../../declaration.js";
import { useListener } from "../../hooks/listener.js";
import { Translate, useTranslator } from "../../i18n/index.js";
import { NonInventoryProductSchema as schema } from "../../schemas/index.js";
type Entity = MerchantBackend.Product;
@ -62,17 +62,17 @@ export function NonInventoryProductFrom({
return Promise.resolve();
});
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<Fragment>
<div class="buttons">
<button
class="button is-success"
data-tooltip={i18n`describe and add a product that is not in the inventory list`}
data-tooltip={i18n.str`describe and add a product that is not in the inventory list`}
onClick={() => setShowCreateProduct(true)}
>
<Translate>Add custom product</Translate>
<i18n.Translate>Add custom product</i18n.Translate>
</button>
</div>
{showCreateProduct && (
@ -83,7 +83,7 @@ export function NonInventoryProductFrom({
/>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">{i18n`Complete information of the product`}</p>
<p class="modal-card-title">{i18n.str`Complete information of the product`}</p>
<button
class="delete "
aria-label="close"
@ -102,14 +102,14 @@ export function NonInventoryProductFrom({
class="button "
onClick={() => setShowCreateProduct(false)}
>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
<button
class="button is-info "
disabled={!submitForm}
onClick={submitForm}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</button>
</div>
</footer>
@ -170,7 +170,7 @@ export function ProductForm({ onSubscribe, initial }: ProductProps): VNode {
onSubscribe(hasErrors ? undefined : submit);
}, [submit, hasErrors]);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div>
@ -182,33 +182,33 @@ export function ProductForm({ onSubscribe, initial }: ProductProps): VNode {
>
<InputImage<NonInventoryProduct>
name="image"
label={i18n`Image`}
tooltip={i18n`photo of the product`}
label={i18n.str`Image`}
tooltip={i18n.str`photo of the product`}
/>
<Input<NonInventoryProduct>
name="description"
inputType="multiline"
label={i18n`Description`}
tooltip={i18n`full product description`}
label={i18n.str`Description`}
tooltip={i18n.str`full product description`}
/>
<Input<NonInventoryProduct>
name="unit"
label={i18n`Unit`}
tooltip={i18n`name of the product unit`}
label={i18n.str`Unit`}
tooltip={i18n.str`name of the product unit`}
/>
<InputCurrency<NonInventoryProduct>
name="price"
label={i18n`Price`}
tooltip={i18n`amount in the current currency`}
label={i18n.str`Price`}
tooltip={i18n.str`amount in the current currency`}
/>
<InputNumber<NonInventoryProduct>
name="quantity"
label={i18n`Quantity`}
tooltip={i18n`how many products will be added`}
label={i18n.str`Quantity`}
tooltip={i18n.str`how many products will be added`}
/>
<InputTaxes<NonInventoryProduct> name="taxes" label={i18n`Taxes`} />
<InputTaxes<NonInventoryProduct> name="taxes" label={i18n.str`Taxes`} />
</FormProvider>
</div>
);

View File

@ -19,17 +19,17 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h } from "preact";
import { useCallback, useEffect, useState } from "preact/hooks";
import * as yup from "yup";
import { useBackendContext } from "../../context/backend.js";
import { MerchantBackend } from "../../declaration.js";
import { useTranslator } from "../../i18n/index.js";
import {
ProductCreateSchema as createSchema,
ProductUpdateSchema as updateSchema,
} from "../../schemas/index.js";
import { FormProvider, FormErrors } from "../form/FormProvider.js";
import { FormErrors, FormProvider } from "../form/FormProvider.js";
import { Input } from "../form/Input.js";
import { InputCurrency } from "../form/InputCurrency.js";
import { InputImage } from "../form/InputImage.js";
@ -115,7 +115,7 @@ export function ProductForm({ onSubscribe, initial, alreadyExist }: Props) {
}, [submit, hasErrors]);
const backend = useBackendContext();
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div>
@ -129,46 +129,46 @@ export function ProductForm({ onSubscribe, initial, alreadyExist }: Props) {
<InputWithAddon<Entity>
name="product_id"
addonBefore={`${backend.url}/product/`}
label={i18n`ID`}
tooltip={i18n`product identification to use in URLs (for internal use only)`}
label={i18n.str`ID`}
tooltip={i18n.str`product identification to use in URLs (for internal use only)`}
/>
)}
<InputImage<Entity>
name="image"
label={i18n`Image`}
tooltip={i18n`illustration of the product for customers`}
label={i18n.str`Image`}
tooltip={i18n.str`illustration of the product for customers`}
/>
<Input<Entity>
name="description"
inputType="multiline"
label={i18n`Description`}
tooltip={i18n`product description for customers`}
label={i18n.str`Description`}
tooltip={i18n.str`product description for customers`}
/>
<InputNumber<Entity>
name="minimum_age"
label={i18n`Age restricted`}
tooltip={i18n`is this product restricted for customer below certain age?`}
label={i18n.str`Age restricted`}
tooltip={i18n.str`is this product restricted for customer below certain age?`}
/>
<Input<Entity>
name="unit"
label={i18n`Unit`}
tooltip={i18n`unit describing quantity of product sold (e.g. 2 kilograms, 5 liters, 3 items, 5 meters) for customers`}
label={i18n.str`Unit`}
tooltip={i18n.str`unit describing quantity of product sold (e.g. 2 kilograms, 5 liters, 3 items, 5 meters) for customers`}
/>
<InputCurrency<Entity>
name="price"
label={i18n`Price`}
tooltip={i18n`sale price for customers, including taxes, for above units of the product`}
label={i18n.str`Price`}
tooltip={i18n.str`sale price for customers, including taxes, for above units of the product`}
/>
<InputStock
name="stock"
label={i18n`Stock`}
label={i18n.str`Stock`}
alreadyExist={alreadyExist}
tooltip={i18n`product inventory for products with finite supply (for internal use only)`}
tooltip={i18n.str`product inventory for products with finite supply (for internal use only)`}
/>
<InputTaxes<Entity>
name="taxes"
label={i18n`Taxes`}
tooltip={i18n`taxes included in the product price, exposed to customers`}
label={i18n.str`Taxes`}
tooltip={i18n.str`taxes included in the product price, exposed to customers`}
/>
</FormProvider>
</div>

View File

@ -16,8 +16,8 @@
import { Amounts } from "@gnu-taler/taler-util";
import { h, VNode } from "preact";
import emptyImage from "../../assets/empty.png";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { MerchantBackend } from "../../declaration.js";
import { Translate } from "../../i18n/index.js";
interface Props {
list: MerchantBackend.Product[];
@ -28,25 +28,26 @@ interface Props {
}[];
}
export function ProductList({ list, actions = [] }: Props): VNode {
const { i18n } = useTranslationContext();
return (
<div class="table-container">
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>
<Translate>image</Translate>
<i18n.Translate>image</i18n.Translate>
</th>
<th>
<Translate>description</Translate>
<i18n.Translate>description</i18n.Translate>
</th>
<th>
<Translate>quantity</Translate>
<i18n.Translate>quantity</i18n.Translate>
</th>
<th>
<Translate>unit price</Translate>
<i18n.Translate>unit price</i18n.Translate>
</th>
<th>
<Translate>total price</Translate>
<i18n.Translate>total price</i18n.Translate>
</th>
<th />
</tr>

View File

@ -29,7 +29,7 @@ interface Type {
useSWRInfinite: typeof useSWRInfinite;
}
const Context = createContext<Type>({} as any);
const Context = createContext<Type>({} as Type);
export const useFetchContext = (): Type => useContext(Context);
export const FetchContextProvider = ({

View File

@ -1,35 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021-2023 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 { createContext } from "preact";
import { useContext } from "preact/hooks";
interface Type {
id: string;
token?: string;
admin?: boolean;
changeToken: (t?: string) => void;
}
const Context = createContext<Type>({} as any);
export const ListenerContextProvider = Context.Provider;
export const useListenerContext = (): Type => useContext(Context);

View File

@ -1,66 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021-2023 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 { createContext, h, VNode } from "preact";
import { useContext, useEffect } from "preact/hooks";
import { useLang } from "../hooks";
import * as jedLib from "jed";
import { strings } from "../i18n/strings";
interface Type {
lang: string;
handler: any;
changeLanguage: (l: string) => void;
}
const initial = {
lang: "en",
handler: null,
changeLanguage: () => {
// do not change anything
},
};
const Context = createContext<Type>(initial);
interface Props {
initial?: string;
children: any;
forceLang?: string;
}
export const TranslationProvider = ({
initial,
children,
forceLang,
}: Props): VNode => {
const [lang, changeLanguage] = useLang(initial);
useEffect(() => {
if (forceLang) {
changeLanguage(forceLang);
}
});
const handler = new jedLib.Jed(strings[lang]);
return h(Context.Provider, {
value: { lang, handler, changeLanguage },
children,
});
};
export const useTranslationContext = (): Type => useContext(Context);

View File

@ -1,215 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021-2023 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/>
*/
/**
* Translation helpers for React components and template literals.
*/
/**
* Imports
*/
import { ComponentChild, ComponentChildren, h, Fragment, VNode } from "preact";
import { useTranslationContext } from "../context/translation.js";
export type Translator = (
stringSeq: TemplateStringsArray,
...values: any[]
) => string;
export function useTranslator(): Translator {
const ctx = useTranslationContext();
const jed = ctx.handler;
return function str(
stringSeq: TemplateStringsArray,
...values: any[]
): string {
const s = toI18nString(stringSeq);
if (!s) return s;
const tr = jed
.translate(s)
.ifPlural(1, s)
.fetch(...values);
return tr;
};
}
/**
* Convert template strings to a msgid
*/
function toI18nString(stringSeq: ReadonlyArray<string>): string {
let s = "";
for (let i = 0; i < stringSeq.length; i++) {
s += stringSeq[i];
if (i < stringSeq.length - 1) {
s += `%${i + 1}$s`;
}
}
return s;
}
interface TranslateSwitchProps {
target: number;
children: ComponentChildren;
}
function stringifyChildren(children: ComponentChildren): string {
let n = 1;
const ss = (children instanceof Array ? children : [children]).map((c) => {
if (typeof c === "string") {
return c;
}
return `%${n++}$s`;
});
const s = ss.join("").replace(/ +/g, " ").trim();
return s;
}
interface TranslateProps {
children: ComponentChildren;
/**
* Component that the translated element should be wrapped in.
* Defaults to "div".
*/
wrap?: any;
/**
* Props to give to the wrapped component.
*/
wrapProps?: any;
}
function getTranslatedChildren(
translation: string,
children: ComponentChildren,
): ComponentChild[] {
const tr = translation.split(/%(\d+)\$s/);
const childArray = children instanceof Array ? children : [children];
// Merge consecutive string children.
const placeholderChildren = Array<ComponentChild>();
for (let i = 0; i < childArray.length; i++) {
const x = childArray[i];
if (x === undefined) {
continue;
} else if (typeof x === "string") {
continue;
} else {
placeholderChildren.push(x);
}
}
const result = Array<ComponentChild>();
for (let i = 0; i < tr.length; i++) {
if (i % 2 == 0) {
// Text
result.push(tr[i]);
} else {
const childIdx = Number.parseInt(tr[i], 10) - 1;
result.push(placeholderChildren[childIdx]);
}
}
return result;
}
/**
* Translate text node children of this component.
* If a child component might produce a text node, it must be wrapped
* in a another non-text element.
*
* Example:
* ```
* <Translate>
* Hello. Your score is <span><PlayerScore player={player} /></span>
* </Translate>
* ```
*/
export function Translate({ children }: TranslateProps): VNode {
const s = stringifyChildren(children);
const ctx = useTranslationContext();
const translation: string = ctx.handler.ngettext(s, s, 1);
const result = getTranslatedChildren(translation, children);
return <Fragment>{result}</Fragment>;
}
/**
* Switch translation based on singular or plural based on the target prop.
* Should only contain TranslateSingular and TransplatePlural as children.
*
* Example:
* ```
* <TranslateSwitch target={n}>
* <TranslateSingular>I have {n} apple.</TranslateSingular>
* <TranslatePlural>I have {n} apples.</TranslatePlural>
* </TranslateSwitch>
* ```
*/
export function TranslateSwitch({ children, target }: TranslateSwitchProps) {
let singular: VNode<TranslationPluralProps> | undefined;
let plural: VNode<TranslationPluralProps> | undefined;
// const children = this.props.children;
if (children) {
(children instanceof Array ? children : [children]).forEach(
(child: any) => {
if (child.type === TranslatePlural) {
plural = child;
}
if (child.type === TranslateSingular) {
singular = child;
}
},
);
}
if (!singular || !plural) {
console.error("translation not found");
return h("span", {}, ["translation not found"]);
}
singular.props.target = target;
plural.props.target = target;
// We're looking up the translation based on the
// singular, even if we must use the plural form.
return singular;
}
interface TranslationPluralProps {
children: ComponentChildren;
target: number;
}
/**
* See [[TranslateSwitch]].
*/
export function TranslatePlural({
children,
target,
}: TranslationPluralProps): VNode {
const s = stringifyChildren(children);
const ctx = useTranslationContext();
const translation = ctx.handler.ngettext(s, s, 1);
const result = getTranslatedChildren(translation, children);
return <Fragment>{result}</Fragment>;
}
/**
* See [[TranslateSwitch]].
*/
export function TranslateSingular({
children,
target,
}: TranslationPluralProps): VNode {
const s = stringifyChildren(children);
const ctx = useTranslationContext();
const translation = ctx.handler.ngettext(s, s, target);
const result = getTranslatedChildren(translation, children);
return <Fragment>{result}</Fragment>;
}

View File

@ -20,6 +20,7 @@
*/
import { h, VNode, FunctionalComponent } from "preact";
import { ConfigContextProvider } from "../../../context/config.js";
import { CreatePage as TestedComponent } from "./CreatePage.js";
export default {
@ -35,7 +36,16 @@ function createExample<Props>(
Component: FunctionalComponent<Props>,
props: Partial<Props>,
) {
const r = (args: any) => <Component {...args} />;
const r = (args: any) => (
<ConfigContextProvider
value={{
currency: "ARS",
version: "1",
}}
>
<Component {...args} />
</ConfigContextProvider>
);
r.args = props;
return r;
}

View File

@ -19,20 +19,19 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { Amounts } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import * as yup from "yup";
import { AsyncButton } from "../../../components/exception/AsyncButton.js";
import {
FormErrors,
FormProvider,
} from "../../../components/form/FormProvider.js";
import { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields.js";
import { SetTokenNewInstanceModal } from "../../../components/modal/index.js";
import { MerchantBackend } from "../../../declaration.js";
import { Translate, useTranslator } from "../../../i18n/index.js";
import { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields.js";
import { INSTANCE_ID_REGEX, PAYTO_REGEX } from "../../../utils/constants.js";
import { Amounts } from "@gnu-taler/taler-util";
import { undefinedIfEmpty } from "../../../utils/table.js";
export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage & {
@ -61,55 +60,57 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
const [isTokenDialogActive, updateIsTokenDialogActive] =
useState<boolean>(false);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const errors: FormErrors<Entity> = {
id: !value.id
? i18n`required`
? i18n.str`required`
: !INSTANCE_ID_REGEX.test(value.id)
? i18n`is not valid`
? i18n.str`is not valid`
: undefined,
name: !value.name ? i18n`required` : undefined,
name: !value.name ? i18n.str`required` : undefined,
payto_uris:
!value.payto_uris || !value.payto_uris.length
? i18n`required`
? i18n.str`required`
: undefinedIfEmpty(
value.payto_uris.map((p) => {
return !PAYTO_REGEX.test(p) ? i18n`is not valid` : undefined;
return !PAYTO_REGEX.test(p) ? i18n.str`is not valid` : undefined;
}),
),
default_max_deposit_fee: !value.default_max_deposit_fee
? i18n`required`
? i18n.str`required`
: !Amounts.parse(value.default_max_deposit_fee)
? i18n`invalid format`
? i18n.str`invalid format`
: undefined,
default_max_wire_fee: !value.default_max_wire_fee
? i18n`required`
? i18n.str`required`
: !Amounts.parse(value.default_max_wire_fee)
? i18n`invalid format`
? i18n.str`invalid format`
: undefined,
default_wire_fee_amortization:
value.default_wire_fee_amortization === undefined
? i18n`required`
? i18n.str`required`
: isNaN(value.default_wire_fee_amortization)
? i18n`is not a number`
? i18n.str`is not a number`
: value.default_wire_fee_amortization < 1
? i18n`must be 1 or greater`
? i18n.str`must be 1 or greater`
: undefined,
default_pay_delay: !value.default_pay_delay ? i18n`required` : undefined,
default_pay_delay: !value.default_pay_delay
? i18n.str`required`
: undefined,
default_wire_transfer_delay: !value.default_wire_transfer_delay
? i18n`required`
? i18n.str`required`
: undefined,
address: undefinedIfEmpty({
address_lines:
value.address?.address_lines && value.address?.address_lines.length > 7
? i18n`max 7 lines`
? i18n.str`max 7 lines`
: undefined,
}),
jurisdiction: undefinedIfEmpty({
address_lines:
value.address?.address_lines && value.address?.address_lines.length > 7
? i18n`max 7 lines`
? i18n.str`max 7 lines`
: undefined,
}),
};
@ -174,14 +175,14 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
<h1 class="title">
<button
class="button is-danger has-tooltip-bottom"
data-tooltip={i18n`change authorization configuration`}
data-tooltip={i18n.str`change authorization configuration`}
onClick={() => updateIsTokenDialogActive(true)}
>
<div class="icon is-centered">
<i class="mdi mdi-lock-reset" />
</div>
<span>
<Translate>Set access token</Translate>
<i18n.Translate>Set access token</i18n.Translate>
</span>
</button>
</h1>
@ -205,7 +206,7 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
<div class="buttons is-right mt-5">
{onBack && (
<button class="button" onClick={onBack}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
)}
<AsyncButton
@ -213,11 +214,11 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
disabled={!isTokenSet || hasErrors}
data-tooltip={
hasErrors
? i18n`Need to complete marked fields and choose authorization method`
? i18n.str`Need to complete marked fields and choose authorization method`
: "confirm operation"
}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</AsyncButton>
</div>
</div>

View File

@ -17,12 +17,12 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { NotificationCard } from "../../../components/menu/index.js";
import { MerchantBackend } from "../../../declaration.js";
import { useAdminAPI } from "../../../hooks/instance.js";
import { useTranslator } from "../../../i18n/index.js";
import { Notification } from "../../../utils/types.js";
import { CreatePage } from "./CreatePage.js";
import { InstanceCreatedSuccessfully } from "./InstanceCreatedSuccessfully.js";
@ -38,7 +38,7 @@ export default function Create({ onBack, onConfirm, forceId }: Props): VNode {
const { createInstance } = useAdminAPI();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const [createdOk, setCreatedOk] = useState<Entity | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
if (createdOk) {
return (
@ -62,7 +62,7 @@ export default function Create({ onBack, onConfirm, forceId }: Props): VNode {
})
.catch((error) => {
setNotif({
message: i18n`Failed to create instance`,
message: i18n.str`Failed to create instance`,
type: "ERROR",
description: error.message,
});

View File

@ -20,6 +20,7 @@
*/
import { h, VNode, FunctionalComponent } from "preact";
import { ConfigContextProvider } from "../../../context/config.js";
import { CreatePage as TestedComponent } from "./CreatePage.js";
export default {
@ -32,17 +33,20 @@ export default {
};
function createExample<Props>(
Component: FunctionalComponent<Props>,
Internal: FunctionalComponent<Props>,
props: Partial<Props>,
) {
const r = (args: any) => h(Component, args);
// const r = (args: any) => <Component {...args} />;
r.args = props;
return r;
const component = (args: any) => (
<ConfigContextProvider
value={{
currency: "TESTKUDOS",
version: "1",
}}
>
<Internal {...(props as any)} />
</ConfigContextProvider>
);
return { component, props };
}
export const Example = createExample(TestedComponent, {});
// export const Example = (a: any): VNode => <CreatePage {...a} />;
// Example.args = {
// isLoading: false
// }

View File

@ -19,10 +19,10 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { StateUpdater, useEffect, useState } from "preact/hooks";
import { MerchantBackend } from "../../../declaration.js";
import { Translate, useTranslator } from "../../../i18n/index.js";
interface Props {
instances: MerchantBackend.Instances.Instance[];
@ -68,7 +68,7 @@ export function CardTable({
}
}, [actionQueue, selected, onUpdate]);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="card has-table">
@ -77,7 +77,7 @@ export function CardTable({
<span class="icon">
<i class="mdi mdi-desktop-mac" />
</span>
<Translate>Instances</Translate>
<i18n.Translate>Instances</i18n.Translate>
</p>
<div class="card-header-icon" aria-label="more options">
@ -90,11 +90,14 @@ export function CardTable({
)
}
>
<Translate>Delete</Translate>
<i18n.Translate>Delete</i18n.Translate>
</button>
</div>
<div class="card-header-icon" aria-label="more options">
<span class="has-tooltip-left" data-tooltip={i18n`add new instance`}>
<span
class="has-tooltip-left"
data-tooltip={i18n.str`add new instance`}
>
<button class="button is-info" type="button" onClick={onCreate}>
<span class="icon is-small">
<i class="mdi mdi-plus mdi-36px" />
@ -149,6 +152,7 @@ function Table({
onDelete,
onPurge,
}: TableProps): VNode {
const { i18n } = useTranslationContext();
return (
<div class="table-container">
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@ -171,10 +175,10 @@ function Table({
</label>
</th>
<th>
<Translate>ID</Translate>
<i18n.Translate>ID</i18n.Translate>
</th>
<th>
<Translate>Name</Translate>
<i18n.Translate>Name</i18n.Translate>
</th>
<th />
</tr>
@ -213,7 +217,7 @@ function Table({
type="button"
onClick={(): void => onUpdate(i.id)}
>
<Translate>Edit</Translate>
<i18n.Translate>Edit</i18n.Translate>
</button>
{!i.deleted && (
<button
@ -221,7 +225,7 @@ function Table({
type="button"
onClick={(): void => onDelete(i)}
>
<Translate>Delete</Translate>
<i18n.Translate>Delete</i18n.Translate>
</button>
)}
{i.deleted && (
@ -230,7 +234,7 @@ function Table({
type="button"
onClick={(): void => onPurge(i)}
>
<Translate>Purge</Translate>
<i18n.Translate>Purge</i18n.Translate>
</button>
)}
</div>
@ -245,6 +249,7 @@ function Table({
}
function EmptyTable(): VNode {
const { i18n } = useTranslationContext();
return (
<div class="content has-text-grey has-text-centered">
<p>
@ -253,9 +258,9 @@ function EmptyTable(): VNode {
</span>
</p>
<p>
<Translate>
<i18n.Translate>
There is no instances yet, add more pressing the + sign
</Translate>
</i18n.Translate>
</p>
</div>
);

View File

@ -19,11 +19,11 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { MerchantBackend } from "../../../declaration.js";
import { CardTable as CardTableActive } from "./TableActive.js";
import { useState } from "preact/hooks";
import { Translate, useTranslator } from "../../../i18n/index.js";
interface Props {
instances: MerchantBackend.Instances.Instance[];
@ -48,7 +48,7 @@ export function View({
const showIsActive = show === "active" ? "is-active" : "";
const showIsDeleted = show === "deleted" ? "is-active" : "";
const showAll = show === null ? "is-active" : "";
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const showingInstances = showIsDeleted
? instances.filter((i) => i.deleted)
@ -66,30 +66,30 @@ export function View({
<li class={showIsActive}>
<div
class="has-tooltip-right"
data-tooltip={i18n`Only show active instances`}
data-tooltip={i18n.str`Only show active instances`}
>
<a onClick={() => setShow("active")}>
<Translate>Active</Translate>
<i18n.Translate>Active</i18n.Translate>
</a>
</div>
</li>
<li class={showIsDeleted}>
<div
class="has-tooltip-right"
data-tooltip={i18n`Only show deleted instances`}
data-tooltip={i18n.str`Only show deleted instances`}
>
<a onClick={() => setShow("deleted")}>
<Translate>Deleted</Translate>
<i18n.Translate>Deleted</i18n.Translate>
</a>
</div>
</li>
<li class={showAll}>
<div
class="has-tooltip-right"
data-tooltip={i18n`Show all instances`}
data-tooltip={i18n.str`Show all instances`}
>
<a onClick={() => setShow(null)}>
<Translate>All</Translate>
<i18n.Translate>All</i18n.Translate>
</a>
</div>
</li>

View File

@ -19,6 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Loading } from "../../../components/exception/loading.js";
@ -27,7 +28,6 @@ import { DeleteModal, PurgeModal } from "../../../components/modal/index.js";
import { MerchantBackend } from "../../../declaration.js";
import { HttpError } from "../../../hooks/backend.js";
import { useAdminAPI, useBackendInstances } from "../../../hooks/instance.js";
import { useTranslator } from "../../../i18n/index.js";
import { Notification } from "../../../utils/types.js";
import { View } from "./View.js";
@ -56,7 +56,7 @@ export default function Instances({
useState<MerchantBackend.Instances.Instance | null>(null);
const { deleteInstance, purgeInstance } = useAdminAPI();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
if (result.clientError && result.isUnauthorized) return onUnauthorized();
if (result.clientError && result.isNotfound) return onNotFound();
@ -84,12 +84,12 @@ export default function Instances({
await deleteInstance(deleting.id);
// pushNotification({ message: 'delete_success', type: 'SUCCESS' })
setNotif({
message: i18n`Instance "${deleting.name}" (ID: ${deleting.id}) has been deleted`,
message: i18n.str`Instance "${deleting.name}" (ID: ${deleting.id}) has been deleted`,
type: "SUCCESS",
});
} catch (error) {
setNotif({
message: i18n`Failed to delete instance`,
message: i18n.str`Failed to delete instance`,
type: "ERROR",
description: error instanceof Error ? error.message : undefined,
});
@ -107,12 +107,12 @@ export default function Instances({
try {
await purgeInstance(purging.id);
setNotif({
message: i18n`Instance "${purging.name}" (ID: ${purging.id}) has been disabled`,
message: i18n.str`Instance "${purging.name}" (ID: ${purging.id}) has been disabled`,
type: "SUCCESS",
});
} catch (error) {
setNotif({
message: i18n`Failed to purge instance`,
message: i18n.str`Failed to purge instance`,
type: "ERROR",
description: error instanceof Error ? error.message : undefined,
});

View File

@ -19,12 +19,12 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { FormProvider } from "../../../components/form/FormProvider.js";
import { Input } from "../../../components/form/Input.js";
import { MerchantBackend } from "../../../declaration.js";
import { useTranslator } from "../../../i18n/index.js";
type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage;
interface Props {
@ -49,7 +49,7 @@ function convert(
export function DetailPage({ selected }: Props): VNode {
const [value, valueHandler] = useState<Partial<Entity>>(convert(selected));
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div>
@ -73,11 +73,11 @@ export function DetailPage({ selected }: Props): VNode {
<div class="column" />
<div class="column is-6">
<FormProvider<Entity> object={value} valueHandler={valueHandler}>
<Input<Entity> name="name" readonly label={i18n`Name`} />
<Input<Entity> name="name" readonly label={i18n.str`Name`} />
<Input<Entity>
name="payto_uris"
readonly
label={i18n`Account address`}
label={i18n.str`Account address`}
/>
</FormProvider>
</div>

View File

@ -20,6 +20,7 @@
*/
import { h, VNode, FunctionalComponent } from "preact";
import { ConfigContextProvider } from "../../../context/config.js";
import { DetailPage as TestedComponent } from "./DetailPage.js";
export default {
@ -32,12 +33,20 @@ export default {
};
function createExample<Props>(
Component: FunctionalComponent<Props>,
Internal: FunctionalComponent<Props>,
props: Partial<Props>,
) {
const r = (args: any) => <Component {...args} />;
r.args = props;
return r;
const component = (args: any) => (
<ConfigContextProvider
value={{
currency: "TESTKUDOS",
version: "1",
}}
>
<Internal {...(props as any)} />
</ConfigContextProvider>
);
return { component, props };
}
export const Example = createExample(TestedComponent, {

View File

@ -15,4 +15,3 @@
*/
export * as details from "./details/stories.js";

View File

@ -19,16 +19,16 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { MerchantBackend } from "../../../../declaration.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
export interface Props {
status: MerchantBackend.Instances.AccountKycRedirects;
}
export function ListPage({ status }: Props): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<section class="section is-main-section">
@ -38,7 +38,7 @@ export function ListPage({ status }: Props): VNode {
<span class="icon">
<i class="mdi mdi-clock" />
</span>
<Translate>Pending KYC verification</Translate>
<i18n.Translate>Pending KYC verification</i18n.Translate>
</p>
<div class="card-header-icon" aria-label="more options" />
@ -63,7 +63,7 @@ export function ListPage({ status }: Props): VNode {
<span class="icon">
<i class="mdi mdi-clock" />
</span>
<Translate>Timed out</Translate>
<i18n.Translate>Timed out</i18n.Translate>
</p>
<div class="card-header-icon" aria-label="more options" />
@ -93,19 +93,20 @@ interface TimedOutTableProps {
}
function PendingTable({ entries }: PendingTableProps): VNode {
const { i18n } = useTranslationContext();
return (
<div class="table-container">
<table class="table is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>
<Translate>Exchange</Translate>
<i18n.Translate>Exchange</i18n.Translate>
</th>
<th>
<Translate>Target account</Translate>
<i18n.Translate>Target account</i18n.Translate>
</th>
<th>
<Translate>KYC URL</Translate>
<i18n.Translate>KYC URL</i18n.Translate>
</th>
</tr>
</thead>
@ -130,19 +131,20 @@ function PendingTable({ entries }: PendingTableProps): VNode {
}
function TimedOutTable({ entries }: TimedOutTableProps): VNode {
const { i18n } = useTranslationContext();
return (
<div class="table-container">
<table class="table is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>
<Translate>Exchange</Translate>
<i18n.Translate>Exchange</i18n.Translate>
</th>
<th>
<Translate>Code</Translate>
<i18n.Translate>Code</i18n.Translate>
</th>
<th>
<Translate>Http Status</Translate>
<i18n.Translate>Http Status</i18n.Translate>
</th>
</tr>
</thead>
@ -163,6 +165,7 @@ function TimedOutTable({ entries }: TimedOutTableProps): VNode {
}
function EmptyTable(): VNode {
const { i18n } = useTranslationContext();
return (
<div class="content has-text-grey has-text-centered">
<p>
@ -171,7 +174,7 @@ function EmptyTable(): VNode {
</span>
</p>
<p>
<Translate>No pending kyc verification!</Translate>
<i18n.Translate>No pending kyc verification!</i18n.Translate>
</p>
</div>
);

View File

@ -19,29 +19,29 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { add, isAfter, isBefore, isFuture } from "date-fns";
import { Amounts } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { add, isAfter, isBefore, isFuture } from "date-fns";
import { Fragment, h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
import {
FormProvider,
FormErrors,
FormProvider,
} from "../../../../components/form/FormProvider.js";
import { Input } from "../../../../components/form/Input.js";
import { InputBoolean } from "../../../../components/form/InputBoolean.js";
import { InputCurrency } from "../../../../components/form/InputCurrency.js";
import { InputDate } from "../../../../components/form/InputDate.js";
import { InputGroup } from "../../../../components/form/InputGroup.js";
import { InputLocation } from "../../../../components/form/InputLocation.js";
import { InputNumber } from "../../../../components/form/InputNumber.js";
import { InventoryProductForm } from "../../../../components/product/InventoryProductForm.js";
import { NonInventoryProductFrom } from "../../../../components/product/NonInventoryProductForm.js";
import { ProductList } from "../../../../components/product/ProductList.js";
import { useConfigContext } from "../../../../context/config.js";
import { Duration, MerchantBackend, WithId } from "../../../../declaration.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
import { OrderCreateSchema as schema } from "../../../../schemas/index.js";
import { rate } from "../../../../utils/amount.js";
import { InventoryProductForm } from "../../../../components/product/InventoryProductForm.js";
import { NonInventoryProductFrom } from "../../../../components/product/NonInventoryProductForm.js";
import { InputNumber } from "../../../../components/form/InputNumber.js";
import { InputBoolean } from "../../../../components/form/InputBoolean.js";
import { undefinedIfEmpty } from "../../../../utils/table.js";
interface Props {
@ -140,65 +140,65 @@ export function CreatePage({
const inventoryList = Object.values(value.inventoryProducts || {});
const productList = Object.values(value.products || {});
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const errors: FormErrors<Entity> = {
pricing: undefinedIfEmpty({
summary: !value.pricing?.summary ? i18n`required` : undefined,
summary: !value.pricing?.summary ? i18n.str`required` : undefined,
order_price: !value.pricing?.order_price
? i18n`required`
? i18n.str`required`
: Amounts.isZero(value.pricing.order_price)
? i18n`must be greater than 0`
? i18n.str`must be greater than 0`
: undefined,
}),
extra:
value.extra && !stringIsValidJSON(value.extra)
? i18n`not a valid json`
? i18n.str`not a valid json`
: undefined,
payments: undefinedIfEmpty({
refund_deadline: !value.payments?.refund_deadline
? undefined
: !isFuture(value.payments.refund_deadline)
? i18n`should be in the future`
? i18n.str`should be in the future`
: value.payments.pay_deadline &&
isBefore(value.payments.refund_deadline, value.payments.pay_deadline)
? i18n`refund deadline cannot be before pay deadline`
? i18n.str`refund deadline cannot be before pay deadline`
: value.payments.wire_transfer_deadline &&
isBefore(
value.payments.wire_transfer_deadline,
value.payments.refund_deadline,
)
? i18n`wire transfer deadline cannot be before refund deadline`
? i18n.str`wire transfer deadline cannot be before refund deadline`
: undefined,
pay_deadline: !value.payments?.pay_deadline
? undefined
: !isFuture(value.payments.pay_deadline)
? i18n`should be in the future`
? i18n.str`should be in the future`
: value.payments.wire_transfer_deadline &&
isBefore(
value.payments.wire_transfer_deadline,
value.payments.pay_deadline,
)
? i18n`wire transfer deadline cannot be before pay deadline`
? i18n.str`wire transfer deadline cannot be before pay deadline`
: undefined,
auto_refund_deadline: !value.payments?.auto_refund_deadline
? undefined
: !isFuture(value.payments.auto_refund_deadline)
? i18n`should be in the future`
? i18n.str`should be in the future`
: !value.payments?.refund_deadline
? i18n`should have a refund deadline`
? i18n.str`should have a refund deadline`
: !isAfter(
value.payments.refund_deadline,
value.payments.auto_refund_deadline,
)
? i18n`auto refund cannot be after refund deadline`
? i18n.str`auto refund cannot be after refund deadline`
: undefined,
}),
shipping: undefinedIfEmpty({
delivery_date: !value.shipping?.delivery_date
? undefined
: !isFuture(value.shipping.delivery_date)
? i18n`should be in the future`
? i18n.str`should be in the future`
: undefined,
}),
};
@ -349,7 +349,7 @@ export function CreatePage({
{/* // FIXME: translating plural singular */}
<InputGroup
name="inventory_products"
label={i18n`Manage products in order`}
label={i18n.str`Manage products in order`}
alternative={
allProducts.length > 0 && (
<p>
@ -358,7 +358,7 @@ export function CreatePage({
</p>
)
}
tooltip={i18n`Manage list of products in the order.`}
tooltip={i18n.str`Manage list of products in the order.`}
>
<InventoryProductForm
currentProducts={value.inventoryProducts || {}}
@ -379,8 +379,8 @@ export function CreatePage({
list={allProducts}
actions={[
{
name: i18n`Remove`,
tooltip: i18n`Remove this product from the order.`,
name: i18n.str`Remove`,
tooltip: i18n.str`Remove this product from the order.`,
handler: (e, index) => {
if (e.product_id) {
removeProductFromTheInventoryList(e.product_id);
@ -404,13 +404,13 @@ export function CreatePage({
<Fragment>
<InputCurrency
name="pricing.products_price"
label={i18n`Total price`}
label={i18n.str`Total price`}
readonly
tooltip={i18n`total product price added up`}
tooltip={i18n.str`total product price added up`}
/>
<InputCurrency
name="pricing.order_price"
label={i18n`Total price`}
label={i18n.str`Total price`}
addonAfter={
discountOrRise > 0 &&
(discountOrRise < 1
@ -419,103 +419,103 @@ export function CreatePage({
)}`
: `rise of %${Math.round((discountOrRise - 1) * 100)}`)
}
tooltip={i18n`Amount to be paid by the customer`}
tooltip={i18n.str`Amount to be paid by the customer`}
/>
</Fragment>
) : (
<InputCurrency
name="pricing.order_price"
label={i18n`Order price`}
tooltip={i18n`final order price`}
label={i18n.str`Order price`}
tooltip={i18n.str`final order price`}
/>
)}
<Input
name="pricing.summary"
inputType="multiline"
label={i18n`Summary`}
tooltip={i18n`Title of the order to be shown to the customer`}
label={i18n.str`Summary`}
tooltip={i18n.str`Title of the order to be shown to the customer`}
/>
<InputGroup
name="shipping"
label={i18n`Shipping and Fulfillment`}
label={i18n.str`Shipping and Fulfillment`}
initialActive
>
<InputDate
name="shipping.delivery_date"
label={i18n`Delivery date`}
tooltip={i18n`Deadline for physical delivery assured by the merchant.`}
label={i18n.str`Delivery date`}
tooltip={i18n.str`Deadline for physical delivery assured by the merchant.`}
/>
{value.shipping?.delivery_date && (
<InputGroup
name="shipping.delivery_location"
label={i18n`Location`}
tooltip={i18n`address where the products will be delivered`}
label={i18n.str`Location`}
tooltip={i18n.str`address where the products will be delivered`}
>
<InputLocation name="shipping.delivery_location" />
</InputGroup>
)}
<Input
name="shipping.fullfilment_url"
label={i18n`Fulfillment URL`}
tooltip={i18n`URL to which the user will be redirected after successful payment.`}
label={i18n.str`Fulfillment URL`}
tooltip={i18n.str`URL to which the user will be redirected after successful payment.`}
/>
</InputGroup>
<InputGroup
name="payments"
label={i18n`Taler payment options`}
tooltip={i18n`Override default Taler payment settings for this order`}
label={i18n.str`Taler payment options`}
tooltip={i18n.str`Override default Taler payment settings for this order`}
>
<InputDate
name="payments.pay_deadline"
label={i18n`Payment deadline`}
tooltip={i18n`Deadline for the customer to pay for the offer before it expires. Inventory products will be reserved until this deadline.`}
label={i18n.str`Payment deadline`}
tooltip={i18n.str`Deadline for the customer to pay for the offer before it expires. Inventory products will be reserved until this deadline.`}
/>
<InputDate
name="payments.refund_deadline"
label={i18n`Refund deadline`}
tooltip={i18n`Time until which the order can be refunded by the merchant.`}
label={i18n.str`Refund deadline`}
tooltip={i18n.str`Time until which the order can be refunded by the merchant.`}
/>
<InputDate
name="payments.wire_transfer_deadline"
label={i18n`Wire transfer deadline`}
tooltip={i18n`Deadline for the exchange to make the wire transfer.`}
label={i18n.str`Wire transfer deadline`}
tooltip={i18n.str`Deadline for the exchange to make the wire transfer.`}
/>
<InputDate
name="payments.auto_refund_deadline"
label={i18n`Auto-refund deadline`}
tooltip={i18n`Time until which the wallet will automatically check for refunds without user interaction.`}
label={i18n.str`Auto-refund deadline`}
tooltip={i18n.str`Time until which the wallet will automatically check for refunds without user interaction.`}
/>
<InputCurrency
name="payments.max_fee"
label={i18n`Maximum deposit fee`}
tooltip={i18n`Maximum deposit fees the merchant is willing to cover for this order. Higher deposit fees must be covered in full by the consumer.`}
label={i18n.str`Maximum deposit fee`}
tooltip={i18n.str`Maximum deposit fees the merchant is willing to cover for this order. Higher deposit fees must be covered in full by the consumer.`}
/>
<InputCurrency
name="payments.max_wire_fee"
label={i18n`Maximum wire fee`}
tooltip={i18n`Maximum aggregate wire fees the merchant is willing to cover for this order. Wire fees exceeding this amount are to be covered by the customers.`}
label={i18n.str`Maximum wire fee`}
tooltip={i18n.str`Maximum aggregate wire fees the merchant is willing to cover for this order. Wire fees exceeding this amount are to be covered by the customers.`}
/>
<InputNumber
name="payments.wire_fee_amortization"
label={i18n`Wire fee amortization`}
tooltip={i18n`Factor by which wire fees exceeding the above threshold are divided to determine the share of excess wire fees to be paid explicitly by the consumer.`}
label={i18n.str`Wire fee amortization`}
tooltip={i18n.str`Factor by which wire fees exceeding the above threshold are divided to determine the share of excess wire fees to be paid explicitly by the consumer.`}
/>
<InputBoolean
name="payments.createToken"
label={i18n`Create token`}
tooltip={i18n`Uncheck this option if the merchant backend generated an order ID with enough entropy to prevent adversarial claims.`}
label={i18n.str`Create token`}
tooltip={i18n.str`Uncheck this option if the merchant backend generated an order ID with enough entropy to prevent adversarial claims.`}
/>
<InputNumber
name="payments.minimum_age"
label={i18n`Minimum age required`}
tooltip={i18n`Any value greater than 0 will limit the coins able be used to pay this contract. If empty the age restriction will be defined by the products`}
label={i18n.str`Minimum age required`}
tooltip={i18n.str`Any value greater than 0 will limit the coins able be used to pay this contract. If empty the age restriction will be defined by the products`}
help={
minAgeByProducts > 0
? i18n`Min age defined by the producs is ${minAgeByProducts}`
? i18n.str`Min age defined by the producs is ${minAgeByProducts}`
: undefined
}
/>
@ -523,14 +523,14 @@ export function CreatePage({
<InputGroup
name="extra"
label={i18n`Additional information`}
tooltip={i18n`Custom information to be included in the contract for this order.`}
label={i18n.str`Additional information`}
tooltip={i18n.str`Custom information to be included in the contract for this order.`}
>
<Input
name="extra"
inputType="multiline"
label={`Value`}
tooltip={i18n`You must enter a value in JavaScript Object Notation (JSON).`}
tooltip={i18n.str`You must enter a value in JavaScript Object Notation (JSON).`}
/>
</InputGroup>
</FormProvider>
@ -538,7 +538,7 @@ export function CreatePage({
<div class="buttons is-right mt-5">
{onBack && (
<button class="button" onClick={onBack}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
)}
<button
@ -546,7 +546,7 @@ export function CreatePage({
onClick={submit}
disabled={hasErrors}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</button>
</div>
</div>

View File

@ -13,11 +13,11 @@
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 { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
import { CreatedSuccessfully } from "../../../../components/notifications/CreatedSuccessfully.js";
import { useOrderAPI } from "../../../../hooks/order.js";
import { Translate } from "../../../../i18n/index.js";
import { Entity } from "./index.js";
interface Props {
@ -33,7 +33,7 @@ export function OrderCreatedSuccessfully({
}: Props): VNode {
const { getPaymentURL } = useOrderAPI();
const [url, setURL] = useState<string | undefined>(undefined);
const { i18n } = useTranslationContext();
useEffect(() => {
getPaymentURL(entity.response.order_id).then((response) => {
setURL(response.data);
@ -48,7 +48,7 @@ export function OrderCreatedSuccessfully({
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">
<Translate>Amount</Translate>
<i18n.Translate>Amount</i18n.Translate>
</label>
</div>
<div class="field-body is-flex-grow-3">
@ -66,7 +66,7 @@ export function OrderCreatedSuccessfully({
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">
<Translate>Summary</Translate>
<i18n.Translate>Summary</i18n.Translate>
</label>
</div>
<div class="field-body is-flex-grow-3">
@ -84,7 +84,7 @@ export function OrderCreatedSuccessfully({
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">
<Translate>Order ID</Translate>
<i18n.Translate>Order ID</i18n.Translate>
</label>
</div>
<div class="field-body is-flex-grow-3">
@ -98,7 +98,7 @@ export function OrderCreatedSuccessfully({
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">
<Translate>Payment URL</Translate>
<i18n.Translate>Payment URL</i18n.Translate>
</label>
</div>
<div class="field-body is-flex-grow-3">

View File

@ -20,6 +20,7 @@
*/
import { AmountJson, Amounts } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { format } from "date-fns";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
@ -34,7 +35,6 @@ import { TextField } from "../../../../components/form/TextField.js";
import { ProductList } from "../../../../components/product/ProductList.js";
import { useBackendContext } from "../../../../context/backend.js";
import { MerchantBackend } from "../../../../declaration.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
import { mergeRefunds } from "../../../../utils/amount.js";
import { RefundModal } from "../list/Table.js";
import { Event, Timeline } from "./Timeline.js";
@ -56,84 +56,84 @@ type Unpaid = MerchantBackend.Orders.CheckPaymentUnpaidResponse;
type Claimed = MerchantBackend.Orders.CheckPaymentClaimedResponse;
function ContractTerms({ value }: { value: CT }) {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<InputGroup name="contract_terms" label={i18n`Contract Terms`}>
<InputGroup name="contract_terms" label={i18n.str`Contract Terms`}>
<FormProvider<CT> object={value} valueHandler={null}>
<Input<CT>
readonly
name="summary"
label={i18n`Summary`}
tooltip={i18n`human-readable description of the whole purchase`}
label={i18n.str`Summary`}
tooltip={i18n.str`human-readable description of the whole purchase`}
/>
<InputCurrency<CT>
readonly
name="amount"
label={i18n`Amount`}
tooltip={i18n`total price for the transaction`}
label={i18n.str`Amount`}
tooltip={i18n.str`total price for the transaction`}
/>
{value.fulfillment_url && (
<Input<CT>
readonly
name="fulfillment_url"
label={i18n`Fulfillment URL`}
tooltip={i18n`URL for this purchase`}
label={i18n.str`Fulfillment URL`}
tooltip={i18n.str`URL for this purchase`}
/>
)}
<Input<CT>
readonly
name="max_fee"
label={i18n`Max fee`}
tooltip={i18n`maximum total deposit fee accepted by the merchant for this contract`}
label={i18n.str`Max fee`}
tooltip={i18n.str`maximum total deposit fee accepted by the merchant for this contract`}
/>
<Input<CT>
readonly
name="max_wire_fee"
label={i18n`Max wire fee`}
tooltip={i18n`maximum wire fee accepted by the merchant`}
label={i18n.str`Max wire fee`}
tooltip={i18n.str`maximum wire fee accepted by the merchant`}
/>
<Input<CT>
readonly
name="wire_fee_amortization"
label={i18n`Wire fee amortization`}
tooltip={i18n`over how many customer transactions does the merchant expect to amortize wire fees on average`}
label={i18n.str`Wire fee amortization`}
tooltip={i18n.str`over how many customer transactions does the merchant expect to amortize wire fees on average`}
/>
<InputDate<CT>
readonly
name="timestamp"
label={i18n`Created at`}
tooltip={i18n`time when this contract was generated`}
label={i18n.str`Created at`}
tooltip={i18n.str`time when this contract was generated`}
/>
<InputDate<CT>
readonly
name="refund_deadline"
label={i18n`Refund deadline`}
tooltip={i18n`after this deadline has passed no refunds will be accepted`}
label={i18n.str`Refund deadline`}
tooltip={i18n.str`after this deadline has passed no refunds will be accepted`}
/>
<InputDate<CT>
readonly
name="pay_deadline"
label={i18n`Payment deadline`}
tooltip={i18n`after this deadline, the merchant won't accept payments for the contract`}
label={i18n.str`Payment deadline`}
tooltip={i18n.str`after this deadline, the merchant won't accept payments for the contract`}
/>
<InputDate<CT>
readonly
name="wire_transfer_deadline"
label={i18n`Wire transfer deadline`}
tooltip={i18n`transfer deadline for the exchange`}
label={i18n.str`Wire transfer deadline`}
tooltip={i18n.str`transfer deadline for the exchange`}
/>
<InputDate<CT>
readonly
name="delivery_date"
label={i18n`Delivery date`}
tooltip={i18n`time indicating when the order should be delivered`}
label={i18n.str`Delivery date`}
tooltip={i18n.str`time indicating when the order should be delivered`}
/>
{value.delivery_date && (
<InputGroup
name="delivery_location"
label={i18n`Location`}
tooltip={i18n`where the order will be delivered`}
label={i18n.str`Location`}
tooltip={i18n.str`where the order will be delivered`}
>
<InputLocation name="payments.delivery_location" />
</InputGroup>
@ -141,14 +141,14 @@ function ContractTerms({ value }: { value: CT }) {
<InputDuration<CT>
readonly
name="auto_refund"
label={i18n`Auto-refund delay`}
tooltip={i18n`how long the wallet should try to get an automatic refund for the purchase`}
label={i18n.str`Auto-refund delay`}
tooltip={i18n.str`how long the wallet should try to get an automatic refund for the purchase`}
/>
<Input<CT>
readonly
name="extra"
label={i18n`Extra info`}
tooltip={i18n`extra data that is only interpreted by the merchant frontend`}
label={i18n.str`Extra info`}
tooltip={i18n.str`extra data that is only interpreted by the merchant frontend`}
/>
</FormProvider>
</InputGroup>
@ -203,7 +203,7 @@ function ClaimedPage({
}
const [value, valueHandler] = useState<Partial<Claimed>>(order);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div>
@ -216,9 +216,9 @@ function ClaimedPage({
<div class="level">
<div class="level-left">
<div class="level-item">
<Translate>Order</Translate> #{id}
<i18n.Translate>Order</i18n.Translate> #{id}
<div class="tag is-info ml-4">
<Translate>claimed</Translate>
<i18n.Translate>claimed</i18n.Translate>
</div>
</div>
</div>
@ -244,7 +244,7 @@ function ClaimedPage({
>
<p>
<b>
<Translate>claimed at</Translate>:
<i18n.Translate>claimed at</i18n.Translate>:
</b>{" "}
{format(
new Date(order.contract_terms.timestamp.t_s * 1000),
@ -262,13 +262,13 @@ function ClaimedPage({
<div class="columns">
<div class="column is-4">
<div class="title">
<Translate>Timeline</Translate>
<i18n.Translate>Timeline</i18n.Translate>
</div>
<Timeline events={events} />
</div>
<div class="column is-8">
<div class="title">
<Translate>Payment details</Translate>
<i18n.Translate>Payment details</i18n.Translate>
</div>
<FormProvider<Claimed>
object={value}
@ -278,17 +278,17 @@ function ClaimedPage({
name="contract_terms.summary"
readonly
inputType="multiline"
label={i18n`Summary`}
label={i18n.str`Summary`}
/>
<InputCurrency
name="contract_terms.amount"
readonly
label={i18n`Amount`}
label={i18n.str`Amount`}
/>
<Input<Claimed>
name="order_status"
readonly
label={i18n`Order status`}
label={i18n.str`Order status`}
/>
</FormProvider>
</div>
@ -298,7 +298,7 @@ function ClaimedPage({
{order.contract_terms.products.length ? (
<Fragment>
<div class="title">
<Translate>Product list</Translate>
<i18n.Translate>Product list</i18n.Translate>
</div>
<ProductList list={order.contract_terms.products} />
</Fragment>
@ -426,7 +426,7 @@ function PaidPage({
const refundurl = `${proto}://refund/${refundHost}/${order.contract_terms.order_id}/`;
const refundable =
new Date().getTime() < order.contract_terms.refund_deadline.t_s * 1000;
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const amount = Amounts.parseOrThrow(order.contract_terms.amount);
const refund_taken = order.refund_details.reduce((prev, cur) => {
@ -446,18 +446,18 @@ function PaidPage({
<div class="level">
<div class="level-left">
<div class="level-item">
<Translate>Order</Translate> #{id}
<i18n.Translate>Order</i18n.Translate> #{id}
<div class="tag is-success ml-4">
<Translate>paid</Translate>
<i18n.Translate>paid</i18n.Translate>
</div>
{order.wired ? (
<div class="tag is-success ml-4">
<Translate>wired</Translate>
<i18n.Translate>wired</i18n.Translate>
</div>
) : null}
{order.refunded ? (
<div class="tag is-danger ml-4">
<Translate>refunded</Translate>
<i18n.Translate>refunded</i18n.Translate>
</div>
) : null}
</div>
@ -477,8 +477,8 @@ function PaidPage({
class="has-tooltip-left"
data-tooltip={
refundable
? i18n`refund order`
: i18n`not refundable`
? i18n.str`refund order`
: i18n.str`not refundable`
}
>
<button
@ -486,7 +486,7 @@ function PaidPage({
disabled={!refundable}
onClick={() => onRefund(id)}
>
<Translate>refund</Translate>
<i18n.Translate>refund</i18n.Translate>
</button>
</span>
</div>
@ -533,41 +533,41 @@ function PaidPage({
<div class="columns">
<div class="column is-4">
<div class="title">
<Translate>Timeline</Translate>
<i18n.Translate>Timeline</i18n.Translate>
</div>
<Timeline events={events} />
</div>
<div class="column is-8">
<div class="title">
<Translate>Payment details</Translate>
<i18n.Translate>Payment details</i18n.Translate>
</div>
<FormProvider<Paid>
object={value}
valueHandler={valueHandler}
>
{/* <InputCurrency<Paid> name="deposit_total" readonly label={i18n`Deposit total`} /> */}
{/* <InputCurrency<Paid> name="deposit_total" readonly label={i18n.str`Deposit total`} /> */}
{order.refunded && (
<InputCurrency<Paid>
name="refund_amount"
readonly
label={i18n`Refunded amount`}
label={i18n.str`Refunded amount`}
/>
)}
{order.refunded && (
<InputCurrency<Paid>
name="refund_taken"
readonly
label={i18n`Refund taken`}
label={i18n.str`Refund taken`}
/>
)}
<Input<Paid>
name="order_status"
readonly
label={i18n`Order status`}
label={i18n.str`Order status`}
/>
<TextField<Paid>
name="order_status_url"
label={i18n`Status URL`}
label={i18n.str`Status URL`}
>
<a
target="_blank"
@ -580,7 +580,7 @@ function PaidPage({
{order.refunded && (
<TextField<Paid>
name="order_status_url"
label={i18n`Refund URI`}
label={i18n.str`Refund URI`}
>
<a target="_blank" rel="noreferrer" href={refundurl}>
{refundurl}
@ -595,7 +595,7 @@ function PaidPage({
{order.contract_terms.products.length ? (
<Fragment>
<div class="title">
<Translate>Product list</Translate>
<i18n.Translate>Product list</i18n.Translate>
</div>
<ProductList list={order.contract_terms.products} />
</Fragment>
@ -620,7 +620,7 @@ function UnpaidPage({
order: MerchantBackend.Orders.CheckPaymentUnpaidResponse;
}) {
const [value, valueHandler] = useState<Partial<Unpaid>>(order);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div>
<section class="hero is-hero-bar">
@ -629,11 +629,11 @@ function UnpaidPage({
<div class="level-left">
<div class="level-item">
<h1 class="title">
<Translate>Order</Translate> #{id}
<i18n.Translate>Order</i18n.Translate> #{id}
</h1>
</div>
<div class="tag is-dark">
<Translate>unpaid</Translate>
<i18n.Translate>unpaid</i18n.Translate>
</div>
</div>
</div>
@ -651,7 +651,7 @@ function UnpaidPage({
>
<p>
<b>
<Translate>pay at</Translate>:
<i18n.Translate>pay at</i18n.Translate>:
</b>{" "}
<a
href={order.order_status_url}
@ -663,7 +663,7 @@ function UnpaidPage({
</p>
<p>
<b>
<Translate>created at</Translate>:
<i18n.Translate>created at</i18n.Translate>:
</b>{" "}
{order.creation_time.t_s === "never"
? "never"
@ -687,26 +687,29 @@ function UnpaidPage({
<Input<Unpaid>
readonly
name="summary"
label={i18n`Summary`}
tooltip={i18n`human-readable description of the whole purchase`}
label={i18n.str`Summary`}
tooltip={i18n.str`human-readable description of the whole purchase`}
/>
<InputCurrency<Unpaid>
readonly
name="total_amount"
label={i18n`Amount`}
tooltip={i18n`total price for the transaction`}
label={i18n.str`Amount`}
tooltip={i18n.str`total price for the transaction`}
/>
<Input<Unpaid>
name="order_status"
readonly
label={i18n`Order status`}
label={i18n.str`Order status`}
/>
<Input<Unpaid>
name="order_status_url"
readonly
label={i18n`Order status URL`}
label={i18n.str`Order status URL`}
/>
<TextField<Unpaid> name="taler_pay_uri" label={i18n`Payment URI`}>
<TextField<Unpaid>
name="taler_pay_uri"
label={i18n.str`Payment URI`}
>
<a target="_blank" rel="noreferrer" href={value.taler_pay_uri}>
{value.taler_pay_uri}
</a>
@ -722,7 +725,7 @@ function UnpaidPage({
export function DetailPage({ id, selected, onRefund, onBack }: Props): VNode {
const [showRefund, setShowRefund] = useState<string | undefined>(undefined);
const { i18n } = useTranslationContext();
const DetailByStatus = function () {
switch (selected.order_status) {
case "claimed":
@ -734,10 +737,10 @@ export function DetailPage({ id, selected, onRefund, onBack }: Props): VNode {
default:
return (
<div>
<Translate>
<i18n.Translate>
Unknown order status. This is an error, please contact the
administrator.
</Translate>
</i18n.Translate>
</div>
);
}
@ -761,7 +764,7 @@ export function DetailPage({ id, selected, onRefund, onBack }: Props): VNode {
<div class="column is-four-fifths">
<div class="buttons is-right mt-5">
<button class="button" onClick={onBack}>
<Translate>Back</Translate>
<i18n.Translate>Back</i18n.Translate>
</button>
</div>
</div>

View File

@ -13,13 +13,13 @@
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 { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js";
import { HttpError } from "../../../../hooks/backend.js";
import { useOrderDetails, useOrderAPI } from "../../../../hooks/order.js";
import { useTranslator } from "../../../../i18n/index.js";
import { useOrderAPI, useOrderDetails } from "../../../../hooks/order.js";
import { Notification } from "../../../../utils/types.js";
import { DetailPage } from "./DetailPage.js";
@ -43,7 +43,7 @@ export default function Update({
const result = useOrderDetails(oid);
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
if (result.clientError && result.isUnauthorized) return onUnauthorized();
if (result.clientError && result.isNotfound) return onNotFound();
@ -61,13 +61,13 @@ export default function Update({
refundOrder(id, value)
.then(() =>
setNotif({
message: i18n`refund created successfully`,
message: i18n.str`refund created successfully`,
type: "SUCCESS",
}),
)
.catch((error) =>
setNotif({
message: i18n`could not create the refund`,
message: i18n.str`could not create the refund`,
type: "ERROR",
description: error.message,
}),

View File

@ -19,12 +19,12 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { format } from "date-fns";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { DatePicker } from "../../../../components/picker/DatePicker.js";
import { MerchantBackend, WithId } from "../../../../declaration.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
import { CardTable } from "./Table.js";
export interface ListPageProps {
@ -74,8 +74,8 @@ export function ListPage({
isNotWiredActive,
onCreate,
}: ListPageProps): VNode {
const i18n = useTranslator();
const dateTooltip = i18n`select date to show nearby orders`;
const { i18n } = useTranslationContext();
const dateTooltip = i18n.str`select date to show nearby orders`;
const [pickDate, setPickDate] = useState(false);
const [orderId, setOrderId] = useState<string>("");
@ -91,13 +91,13 @@ export function ListPage({
type="text"
value={orderId}
onChange={(e) => setOrderId(e.currentTarget.value)}
placeholder={i18n`order id`}
placeholder={i18n.str`order id`}
/>
{errorOrderId && <p class="help is-danger">{errorOrderId}</p>}
</div>
<span
class="has-tooltip-bottom"
data-tooltip={i18n`jump to order with the given order ID`}
data-tooltip={i18n.str`jump to order with the given order ID`}
>
<button
class="button"
@ -119,40 +119,40 @@ export function ListPage({
<li class={isAllActive}>
<div
class="has-tooltip-right"
data-tooltip={i18n`remove all filters`}
data-tooltip={i18n.str`remove all filters`}
>
<a onClick={onShowAll}>
<Translate>All</Translate>
<i18n.Translate>All</i18n.Translate>
</a>
</div>
</li>
<li class={isPaidActive}>
<div
class="has-tooltip-right"
data-tooltip={i18n`only show paid orders`}
data-tooltip={i18n.str`only show paid orders`}
>
<a onClick={onShowPaid}>
<Translate>Paid</Translate>
<i18n.Translate>Paid</i18n.Translate>
</a>
</div>
</li>
<li class={isRefundedActive}>
<div
class="has-tooltip-right"
data-tooltip={i18n`only show orders with refunds`}
data-tooltip={i18n.str`only show orders with refunds`}
>
<a onClick={onShowRefunded}>
<Translate>Refunded</Translate>
<i18n.Translate>Refunded</i18n.Translate>
</a>
</div>
</li>
<li class={isNotWiredActive}>
<div
class="has-tooltip-left"
data-tooltip={i18n`only show orders where customers paid, but wire payments from payment provider are still pending`}
data-tooltip={i18n.str`only show orders where customers paid, but wire payments from payment provider are still pending`}
>
<a onClick={onShowNotWired}>
<Translate>Not wired</Translate>
<i18n.Translate>Not wired</i18n.Translate>
</a>
</div>
</li>
@ -165,7 +165,10 @@ export function ListPage({
{jumpToDate && (
<div class="control">
<a class="button" onClick={() => onSelectDate(undefined)}>
<span class="icon" data-tooltip={i18n`clear date filter`}>
<span
class="icon"
data-tooltip={i18n.str`clear date filter`}
>
<i class="mdi mdi-close" />
</span>
</a>
@ -178,7 +181,7 @@ export function ListPage({
type="text"
readonly
value={!jumpToDate ? "" : format(jumpToDate, "yyyy/MM/dd")}
placeholder={i18n`date (YYYY/MM/DD)`}
placeholder={i18n.str`date (YYYY/MM/DD)`}
onClick={() => {
setPickDate(true);
}}

View File

@ -20,6 +20,7 @@
*/
import { Amounts } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { format } from "date-fns";
import { h, VNode } from "preact";
import { StateUpdater, useState } from "preact/hooks";
@ -34,7 +35,6 @@ import { InputSelector } from "../../../../components/form/InputSelector.js";
import { ConfirmModal } from "../../../../components/modal/index.js";
import { useConfigContext } from "../../../../context/config.js";
import { MerchantBackend, WithId } from "../../../../declaration.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
import { mergeRefunds } from "../../../../utils/amount.js";
type Entity = MerchantBackend.Orders.OrderHistoryEntry & WithId;
@ -63,7 +63,7 @@ export function CardTable({
}: Props): VNode {
const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="card has-table">
@ -72,13 +72,13 @@ export function CardTable({
<span class="icon">
<i class="mdi mdi-cash-register" />
</span>
<Translate>Orders</Translate>
<i18n.Translate>Orders</i18n.Translate>
</p>
<div class="card-header-icon" aria-label="more options" />
<div class="card-header-icon" aria-label="more options">
<span class="has-tooltip-left" data-tooltip={i18n`create order`}>
<span class="has-tooltip-left" data-tooltip={i18n.str`create order`}>
<button class="button is-info" type="button" onClick={onCreate}>
<span class="icon is-small">
<i class="mdi mdi-plus mdi-36px" />
@ -135,6 +135,7 @@ function Table({
hasMoreAfter,
hasMoreBefore,
}: TableProps): VNode {
const { i18n } = useTranslationContext();
return (
<div class="table-container">
{onLoadMoreBefore && (
@ -143,20 +144,20 @@ function Table({
disabled={!hasMoreBefore}
onClick={onLoadMoreBefore}
>
<Translate>load newer orders</Translate>
<i18n.Translate>load newer orders</i18n.Translate>
</button>
)}
<table class="table is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th style={{ minWidth: 100 }}>
<Translate>Date</Translate>
<i18n.Translate>Date</i18n.Translate>
</th>
<th style={{ minWidth: 100 }}>
<Translate>Amount</Translate>
<i18n.Translate>Amount</i18n.Translate>
</th>
<th style={{ minWidth: 400 }}>
<Translate>Summary</Translate>
<i18n.Translate>Summary</i18n.Translate>
</th>
<th style={{ minWidth: 50 }} />
</tr>
@ -196,7 +197,7 @@ function Table({
type="button"
onClick={(): void => onRefund(i)}
>
<Translate>Refund</Translate>
<i18n.Translate>Refund</i18n.Translate>
</button>
)}
{!i.paid && (
@ -205,7 +206,7 @@ function Table({
type="button"
onClick={(): void => onCopyURL(i)}
>
<Translate>copy url</Translate>
<i18n.Translate>copy url</i18n.Translate>
</button>
)}
</div>
@ -221,7 +222,7 @@ function Table({
disabled={!hasMoreAfter}
onClick={onLoadMoreAfter}
>
<Translate>load older orders</Translate>
<i18n.Translate>load older orders</i18n.Translate>
</button>
)}
</div>
@ -229,6 +230,7 @@ function Table({
}
function EmptyTable(): VNode {
const { i18n } = useTranslationContext();
return (
<div class="content has-text-grey has-text-centered">
<p>
@ -237,7 +239,9 @@ function EmptyTable(): VNode {
</span>
</p>
<p>
<Translate>No orders have been found matching your query!</Translate>
<i18n.Translate>
No orders have been found matching your query!
</i18n.Translate>
</p>
</div>
);
@ -256,7 +260,7 @@ export function RefundModal({
}: RefundModalProps): VNode {
type State = { mainReason?: string; description?: string; refund?: string };
const [form, setValue] = useState<State>({});
const i18n = useTranslator();
const { i18n } = useTranslationContext();
// const [errors, setErrors] = useState<FormErrors<State>>({});
const refunds = (
@ -281,20 +285,20 @@ export function RefundModal({
: orderPrice;
const isRefundable = Amounts.isNonZero(totalRefundable);
const duplicatedText = i18n`duplicated`;
const duplicatedText = i18n.str`duplicated`;
const errors: FormErrors<State> = {
mainReason: !form.mainReason ? i18n`required` : undefined,
mainReason: !form.mainReason ? i18n.str`required` : undefined,
description:
!form.description && form.mainReason !== duplicatedText
? i18n`required`
? i18n.str`required`
: undefined,
refund: !form.refund
? i18n`required`
? i18n.str`required`
: !Amounts.parse(form.refund)
? i18n`invalid format`
? i18n.str`invalid format`
: Amounts.cmp(totalRefundable, Amounts.parse(form.refund)!) === -1
? i18n`this value exceed the refundable amount`
? i18n.str`this value exceed the refundable amount`
: undefined,
};
const hasErrors = Object.keys(errors).some(
@ -339,13 +343,13 @@ export function RefundModal({
<thead>
<tr>
<th>
<Translate>date</Translate>
<i18n.Translate>date</i18n.Translate>
</th>
<th>
<Translate>amount</Translate>
<i18n.Translate>amount</i18n.Translate>
</th>
<th>
<Translate>reason</Translate>
<i18n.Translate>reason</i18n.Translate>
</th>
</tr>
</thead>
@ -381,28 +385,28 @@ export function RefundModal({
>
<InputCurrency<State>
name="refund"
label={i18n`Refund`}
tooltip={i18n`amount to be refunded`}
label={i18n.str`Refund`}
tooltip={i18n.str`amount to be refunded`}
>
<Translate>Max refundable:</Translate>{" "}
<i18n.Translate>Max refundable:</i18n.Translate>{" "}
{Amounts.stringify(totalRefundable)}
</InputCurrency>
<InputSelector
name="mainReason"
label={i18n`Reason`}
label={i18n.str`Reason`}
values={[
i18n`Choose one...`,
i18n.str`Choose one...`,
duplicatedText,
i18n`requested by the customer`,
i18n`other`,
i18n.str`requested by the customer`,
i18n.str`other`,
]}
tooltip={i18n`why this order is being refunded`}
tooltip={i18n.str`why this order is being refunded`}
/>
{form.mainReason && form.mainReason !== duplicatedText ? (
<Input<State>
label={i18n`Description`}
label={i18n.str`Description`}
name="description"
tooltip={i18n`more information to give context`}
tooltip={i18n.str`more information to give context`}
/>
) : undefined}
</FormProvider>

View File

@ -19,7 +19,8 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { h, VNode, Fragment } from "preact";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js";
@ -31,10 +32,9 @@ import {
useOrderAPI,
useOrderDetails,
} from "../../../../hooks/order.js";
import { useTranslator } from "../../../../i18n/index.js";
import { Notification } from "../../../../utils/types.js";
import { RefundModal } from "./Table.js";
import { ListPage } from "./ListPage.js";
import { RefundModal } from "./Table.js";
interface Props {
onUnauthorized: () => VNode;
@ -64,7 +64,7 @@ export default function OrderList({
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const [errorOrderId, setErrorOrderId] = useState<string | undefined>(
undefined,
);
@ -86,7 +86,7 @@ export default function OrderList({
async function testIfOrderExistAndSelect(orderId: string): Promise<void> {
if (!orderId) {
setErrorOrderId(i18n`Enter an order id`);
setErrorOrderId(i18n.str`Enter an order id`);
return;
}
try {
@ -94,7 +94,7 @@ export default function OrderList({
onSelect(orderId);
setErrorOrderId(undefined);
} catch {
setErrorOrderId(i18n`order not found`);
setErrorOrderId(i18n.str`order not found`);
}
}
@ -136,13 +136,13 @@ export default function OrderList({
refundOrder(orderToBeRefunded.order_id, value)
.then(() =>
setNotif({
message: i18n`refund created successfully`,
message: i18n.str`refund created successfully`,
type: "SUCCESS",
}),
)
.catch((error) =>
setNotif({
message: i18n`could not create the refund`,
message: i18n.str`could not create the refund`,
type: "ERROR",
description: error.message,
}),
@ -151,7 +151,7 @@ export default function OrderList({
}
onLoadError={(error) => {
setNotif({
message: i18n`could not create the refund`,
message: i18n.str`could not create the refund`,
type: "ERROR",
description: error.message,
});
@ -161,7 +161,7 @@ export default function OrderList({
onUnauthorized={onUnauthorized}
onNotFound={() => {
setNotif({
message: i18n`could not get the order to refund`,
message: i18n.str`could not get the order to refund`,
type: "ERROR",
// description: error.message
});

View File

@ -19,12 +19,12 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
import { ProductForm } from "../../../../components/product/ProductForm.js";
import { MerchantBackend } from "../../../../declaration.js";
import { useListener } from "../../../../hooks/listener.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
type Entity = MerchantBackend.Products.ProductAddDetail & {
product_id: string;
@ -43,7 +43,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
},
);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div>
@ -56,19 +56,19 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
<div class="buttons is-right mt-5">
{onBack && (
<button class="button" onClick={onBack}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
)}
<AsyncButton
onClick={submitForm}
data-tooltip={
!submitForm
? i18n`Need to complete marked fields`
? i18n.str`Need to complete marked fields`
: "confirm operation"
}
disabled={!submitForm}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</AsyncButton>
</div>
</div>

View File

@ -19,12 +19,12 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend } from "../../../../declaration.js";
import { useProductAPI } from "../../../../hooks/product.js";
import { useTranslator } from "../../../../i18n/index.js";
import { Notification } from "../../../../utils/types.js";
import { CreatePage } from "./CreatePage.js";
@ -36,7 +36,7 @@ interface Props {
export default function CreateProduct({ onConfirm, onBack }: Props): VNode {
const { createProduct } = useProductAPI();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<Fragment>
@ -48,7 +48,7 @@ export default function CreateProduct({ onConfirm, onBack }: Props): VNode {
.then(() => onConfirm())
.catch((error) => {
setNotif({
message: i18n`could not create product`,
message: i18n.str`could not create product`,
type: "ERROR",
description: error.message,
});

View File

@ -19,19 +19,19 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { Amounts } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { format } from "date-fns";
import { ComponentChildren, Fragment, h, VNode } from "preact";
import { StateUpdater, useState } from "preact/hooks";
import emptyImage from "../../../../assets/empty.png";
import {
FormProvider,
FormErrors,
FormProvider,
} from "../../../../components/form/FormProvider.js";
import { InputCurrency } from "../../../../components/form/InputCurrency.js";
import { InputNumber } from "../../../../components/form/InputNumber.js";
import { MerchantBackend, WithId } from "../../../../declaration.js";
import emptyImage from "../../../../assets/empty.png";
import { Translate, useTranslator } from "../../../../i18n/index.js";
import { Amounts } from "@gnu-taler/taler-util";
type Entity = MerchantBackend.Products.ProductDetail & WithId;
@ -57,7 +57,7 @@ export function CardTable({
const [rowSelection, rowSelectionHandler] = useState<string | undefined>(
undefined,
);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="card has-table">
<header class="card-header">
@ -65,12 +65,12 @@ export function CardTable({
<span class="icon">
<i class="mdi mdi-shopping" />
</span>
<Translate>Products</Translate>
<i18n.Translate>Products</i18n.Translate>
</p>
<div class="card-header-icon" aria-label="more options">
<span
class="has-tooltip-left"
data-tooltip={i18n`add product to inventory`}
data-tooltip={i18n.str`add product to inventory`}
>
<button class="button is-info" type="button" onClick={onCreate}>
<span class="icon is-small">
@ -121,32 +121,32 @@ function Table({
onUpdate,
onDelete,
}: TableProps): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="table-container">
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>
<Translate>Image</Translate>
<i18n.Translate>Image</i18n.Translate>
</th>
<th>
<Translate>Description</Translate>
<i18n.Translate>Description</i18n.Translate>
</th>
<th>
<Translate>Sell</Translate>
<i18n.Translate>Sell</i18n.Translate>
</th>
<th>
<Translate>Taxes</Translate>
<i18n.Translate>Taxes</i18n.Translate>
</th>
<th>
<Translate>Profit</Translate>
<i18n.Translate>Profit</i18n.Translate>
</th>
<th>
<Translate>Stock</Translate>
<i18n.Translate>Stock</i18n.Translate>
</th>
<th>
<Translate>Sold</Translate>
<i18n.Translate>Sold</i18n.Translate>
</th>
<th />
</tr>
@ -207,7 +207,7 @@ function Table({
}
style={{ cursor: "pointer" }}
>
{isFree ? i18n`free` : `${i.price} / ${i.unit}`}
{isFree ? i18n.str`free` : `${i.price} / ${i.unit}`}
</td>
<td
onClick={() =>
@ -245,26 +245,26 @@ function Table({
<div class="buttons is-right">
<span
class="has-tooltip-bottom"
data-tooltip={i18n`go to product update page`}
data-tooltip={i18n.str`go to product update page`}
>
<button
class="button is-small is-success "
type="button"
onClick={(): void => onSelect(i)}
>
<Translate>Update</Translate>
<i18n.Translate>Update</i18n.Translate>
</button>
</span>
<span
class="has-tooltip-left"
data-tooltip={i18n`remove this product from the database`}
data-tooltip={i18n.str`remove this product from the database`}
>
<button
class="button is-small is-danger"
type="button"
onClick={(): void => onDelete(i)}
>
<Translate>Delete</Translate>
<i18n.Translate>Delete</i18n.Translate>
</button>
</span>
</div>
@ -316,7 +316,7 @@ function FastProductWithInfiniteStockUpdateForm({
onCancel,
}: FastProductUpdateFormProps) {
const [value, valueHandler] = useState<UpdatePrice>({ price: product.price });
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<Fragment>
@ -327,18 +327,18 @@ function FastProductWithInfiniteStockUpdateForm({
>
<InputCurrency<FastProductUpdate>
name="price"
label={i18n`Price`}
tooltip={i18n`update the product with new price`}
label={i18n.str`Price`}
tooltip={i18n.str`update the product with new price`}
/>
</FormProvider>
<div class="buttons is-right mt-5">
<button class="button" onClick={onCancel}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
<span
class="has-tooltip-left"
data-tooltip={i18n`update product with new price`}
data-tooltip={i18n.str`update product with new price`}
>
<button
class="button is-info"
@ -349,7 +349,7 @@ function FastProductWithInfiniteStockUpdateForm({
})
}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</button>
</span>
</div>
@ -383,7 +383,7 @@ function FastProductWithManagedStockUpdateForm({
const hasErrors = Object.keys(errors).some(
(k) => (errors as any)[k] !== undefined,
);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<Fragment>
@ -395,31 +395,31 @@ function FastProductWithManagedStockUpdateForm({
>
<InputNumber<FastProductUpdate>
name="incoming"
label={i18n`Incoming`}
tooltip={i18n`add more elements to the inventory`}
label={i18n.str`Incoming`}
tooltip={i18n.str`add more elements to the inventory`}
/>
<InputNumber<FastProductUpdate>
name="lost"
label={i18n`Lost`}
tooltip={i18n`report elements lost in the inventory`}
label={i18n.str`Lost`}
tooltip={i18n.str`report elements lost in the inventory`}
/>
<InputCurrency<FastProductUpdate>
name="price"
label={i18n`Price`}
tooltip={i18n`new price for the product`}
label={i18n.str`Price`}
tooltip={i18n.str`new price for the product`}
/>
</FormProvider>
<div class="buttons is-right mt-5">
<button class="button" onClick={onCancel}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
<span
class="has-tooltip-left"
data-tooltip={
hasErrors
? i18n`the are value with errors`
: i18n`update product with new stock and price`
? i18n.str`the are value with errors`
: i18n.str`update product with new stock and price`
}
>
<button
@ -434,7 +434,7 @@ function FastProductWithManagedStockUpdateForm({
})
}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</button>
</span>
</div>
@ -451,6 +451,7 @@ function FastProductUpdateForm(props: FastProductUpdateFormProps) {
}
function EmptyTable(): VNode {
const { i18n } = useTranslationContext();
return (
<div class="content has-text-grey has-text-centered">
<p>
@ -459,9 +460,9 @@ function EmptyTable(): VNode {
</span>
</p>
<p>
<Translate>
<i18n.Translate>
There is no products yet, add more pressing the + sign
</Translate>
</i18n.Translate>
</p>
</div>
);

View File

@ -19,6 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js";
@ -29,7 +30,6 @@ import {
useInstanceProducts,
useProductAPI,
} from "../../../../hooks/product.js";
import { useTranslator } from "../../../../i18n/index.js";
import { Notification } from "../../../../utils/types.js";
import { CardTable } from "./Table.js";
@ -51,7 +51,7 @@ export default function ProductList({
const { deleteProduct, updateProduct } = useProductAPI();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
if (result.clientError && result.isUnauthorized) return onUnauthorized();
if (result.clientError && result.isNotfound) return onNotFound();
@ -69,13 +69,13 @@ export default function ProductList({
updateProduct(id, prod)
.then(() =>
setNotif({
message: i18n`product updated successfully`,
message: i18n.str`product updated successfully`,
type: "SUCCESS",
}),
)
.catch((error) =>
setNotif({
message: i18n`could not update the product`,
message: i18n.str`could not update the product`,
type: "ERROR",
description: error.message,
}),
@ -86,13 +86,13 @@ export default function ProductList({
deleteProduct(prod.id)
.then(() =>
setNotif({
message: i18n`product delete successfully`,
message: i18n.str`product delete successfully`,
type: "SUCCESS",
}),
)
.catch((error) =>
setNotif({
message: i18n`could not delete the product`,
message: i18n.str`could not delete the product`,
type: "ERROR",
description: error.message,
}),

View File

@ -19,12 +19,12 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
import { ProductForm } from "../../../../components/product/ProductForm.js";
import { MerchantBackend, WithId } from "../../../../declaration.js";
import { MerchantBackend } from "../../../../declaration.js";
import { useListener } from "../../../../hooks/listener.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
type Entity = MerchantBackend.Products.ProductDetail & { product_id: string };
@ -42,7 +42,7 @@ export function UpdatePage({ product, onUpdate, onBack }: Props): VNode {
},
);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div>
@ -53,7 +53,7 @@ export function UpdatePage({ product, onUpdate, onBack }: Props): VNode {
<div class="level-left">
<div class="level-item">
<span class="is-size-4">
<Translate>Product id:</Translate>
<i18n.Translate>Product id:</i18n.Translate>
<b>{product.product_id}</b>
</span>
</div>
@ -75,19 +75,19 @@ export function UpdatePage({ product, onUpdate, onBack }: Props): VNode {
<div class="buttons is-right mt-5">
{onBack && (
<button class="button" onClick={onBack}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
)}
<AsyncButton
onClick={submitForm}
data-tooltip={
!submitForm
? i18n`Need to complete marked fields`
? i18n.str`Need to complete marked fields`
: "confirm operation"
}
disabled={!submitForm}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</AsyncButton>
</div>
</div>

View File

@ -19,6 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js";
@ -26,7 +27,6 @@ import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend } from "../../../../declaration.js";
import { HttpError } from "../../../../hooks/backend.js";
import { useProductAPI, useProductDetails } from "../../../../hooks/product.js";
import { useTranslator } from "../../../../i18n/index.js";
import { Notification } from "../../../../utils/types.js";
import { UpdatePage } from "./UpdatePage.js";
@ -51,7 +51,7 @@ export default function UpdateProduct({
const result = useProductDetails(pid);
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
if (result.clientError && result.isUnauthorized) return onUnauthorized();
if (result.clientError && result.isNotfound) return onNotFound();
@ -69,7 +69,7 @@ export default function UpdateProduct({
.then(onConfirm)
.catch((error) => {
setNotif({
message: i18n`could not create product`,
message: i18n.str`could not create product`,
type: "ERROR",
description: error.message,
});

View File

@ -19,24 +19,23 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { StateUpdater, useEffect, useState } from "preact/hooks";
import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
import {
FormErrors,
FormProvider,
} from "../../../../components/form/FormProvider.js";
import { Input } from "../../../../components/form/Input.js";
import { InputCurrency } from "../../../../components/form/InputCurrency.js";
import { InputSelector } from "../../../../components/form/InputSelector.js";
import { ExchangeBackend, MerchantBackend } from "../../../../declaration.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
import { canonicalizeBaseUrl, ExchangeKeysJson } from "@gnu-taler/taler-util";
import { request } from "../../../../hooks/backend.js";
import {
PAYTO_WIRE_METHOD_LOOKUP,
URL_REGEX,
} from "../../../../utils/constants.js";
import { request } from "../../../../hooks/backend.js";
import { InputSelector } from "../../../../components/form/InputSelector.js";
type Entity = MerchantBackend.Tips.ReserveCreateRequest;
@ -66,7 +65,7 @@ function ViewStep({
submitForm,
setReserve,
}: ViewProps): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const [wireMethods, setWireMethods] = useState<Array<string>>([]);
const [exchangeQueryError, setExchangeQueryError] = useState<
string | undefined
@ -82,12 +81,12 @@ function ViewStep({
initial_balance: !reserve.initial_balance
? "cannot be empty"
: !(parseInt(reserve.initial_balance.split(":")[1], 10) > 0)
? i18n`it should be greater than 0`
? i18n.str`it should be greater than 0`
: undefined,
exchange_url: !reserve.exchange_url
? i18n`cannot be empty`
? i18n.str`cannot be empty`
: !URL_REGEX.test(reserve.exchange_url)
? i18n`must be a valid URL`
? i18n.str`must be a valid URL`
: !exchangeQueryError
? undefined
: exchangeQueryError,
@ -106,20 +105,20 @@ function ViewStep({
>
<InputCurrency<Entity>
name="initial_balance"
label={i18n`Initial balance`}
tooltip={i18n`balance prior to deposit`}
label={i18n.str`Initial balance`}
tooltip={i18n.str`balance prior to deposit`}
/>
<Input<Entity>
name="exchange_url"
label={i18n`Exchange URL`}
tooltip={i18n`URL of exchange`}
label={i18n.str`Exchange URL`}
tooltip={i18n.str`URL of exchange`}
/>
</FormProvider>
<div class="buttons is-right mt-5">
{onBack && (
<button class="button" onClick={onBack}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
)}
<AsyncButton
@ -143,12 +142,12 @@ function ViewStep({
}}
data-tooltip={
hasErrors
? i18n`Need to complete marked fields`
? i18n.str`Need to complete marked fields`
: "confirm operation"
}
disabled={hasErrors}
>
<Translate>Next</Translate>
<i18n.Translate>Next</i18n.Translate>
</AsyncButton>
</div>
</Fragment>
@ -157,7 +156,9 @@ function ViewStep({
case Steps.WIRE_METHOD: {
const errors: FormErrors<Entity> = {
wire_method: !reserve.wire_method ? i18n`cannot be empty` : undefined,
wire_method: !reserve.wire_method
? i18n.str`cannot be empty`
: undefined,
};
const hasErrors = Object.keys(errors).some(
@ -172,22 +173,22 @@ function ViewStep({
>
<InputCurrency<Entity>
name="initial_balance"
label={i18n`Initial balance`}
tooltip={i18n`balance prior to deposit`}
label={i18n.str`Initial balance`}
tooltip={i18n.str`balance prior to deposit`}
readonly
/>
<Input<Entity>
name="exchange_url"
label={i18n`Exchange URL`}
tooltip={i18n`URL of exchange`}
label={i18n.str`Exchange URL`}
tooltip={i18n.str`URL of exchange`}
readonly
/>
<InputSelector<Entity>
name="wire_method"
label={i18n`Wire method`}
tooltip={i18n`method to use for wire transfer`}
label={i18n.str`Wire method`}
tooltip={i18n.str`method to use for wire transfer`}
values={wireMethods}
placeholder={i18n`Select one wire method`}
placeholder={i18n.str`Select one wire method`}
/>
</FormProvider>
<div class="buttons is-right mt-5">
@ -196,19 +197,19 @@ function ViewStep({
class="button"
onClick={() => setCurrentStep(Steps.EXCHANGE)}
>
<Translate>Back</Translate>
<i18n.Translate>Back</i18n.Translate>
</button>
)}
<AsyncButton
onClick={submitForm}
data-tooltip={
hasErrors
? i18n`Need to complete marked fields`
? i18n.str`Need to complete marked fields`
: "confirm operation"
}
disabled={hasErrors}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</AsyncButton>
</div>
</Fragment>

View File

@ -14,11 +14,11 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { QR } from "../../../../components/exception/QR.js";
import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully.js";
import { MerchantBackend } from "../../../../declaration.js";
import { Translate } from "../../../../i18n/index.js";
import { QR } from "../../../../components/exception/QR.js";
type Entity = {
request: MerchantBackend.Tips.ReserveCreateRequest;
@ -37,7 +37,7 @@ export function CreatedSuccessfully({
onCreateAnother,
}: Props): VNode {
const link = `${entity.response.payto_uri}?message=${entity.response.reserve_pub}&amount=${entity.request.initial_balance}`;
const { i18n } = useTranslationContext();
return (
<Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}>
<div class="field is-horizontal">
@ -85,16 +85,16 @@ export function CreatedSuccessfully({
</div>
</div>
<p class="is-size-5">
<Translate>
<i18n.Translate>
To complete the setup of the reserve, you must now initiate a wire
transfer using the given wire transfer subject and crediting the
specified amount to the indicated account of the exchange.
</Translate>
</i18n.Translate>
</p>
<p class="is-size-5">
<Translate>
<i18n.Translate>
If your system supports RFC 8905, you can do this by opening this URI:
</Translate>
</i18n.Translate>
</p>
<pre>
<a target="_blank" rel="noreferrer" href={link}>

View File

@ -19,12 +19,12 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend } from "../../../../declaration.js";
import { useReservesAPI } from "../../../../hooks/reserves.js";
import { useTranslator } from "../../../../i18n/index.js";
import { Notification } from "../../../../utils/types.js";
import { CreatedSuccessfully } from "./CreatedSuccessfully.js";
import { CreatePage } from "./CreatePage.js";
@ -35,7 +35,7 @@ interface Props {
export default function CreateReserve({ onBack, onConfirm }: Props): VNode {
const { createReserve } = useReservesAPI();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const [createdOk, setCreatedOk] = useState<
| {
@ -59,7 +59,7 @@ export default function CreateReserve({ onBack, onConfirm }: Props): VNode {
.then((r) => setCreatedOk({ request, response: r.data }))
.catch((error) => {
setNotif({
message: i18n`could not create reserve`,
message: i18n.str`could not create reserve`,
type: "ERROR",
description: error.message,
});

View File

@ -20,6 +20,7 @@
*/
import { Amounts } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { format } from "date-fns";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
@ -29,13 +30,9 @@ import { Input } from "../../../../components/form/Input.js";
import { InputCurrency } from "../../../../components/form/InputCurrency.js";
import { InputDate } from "../../../../components/form/InputDate.js";
import { TextField } from "../../../../components/form/TextField.js";
import {
ContinueModal,
SimpleModal,
} from "../../../../components/modal/index.js";
import { SimpleModal } from "../../../../components/modal/index.js";
import { MerchantBackend } from "../../../../declaration.js";
import { useTipDetails } from "../../../../hooks/reserves.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
import { TipInfo } from "./TipInfo.js";
type Entity = MerchantBackend.Tips.ReserveDetail;
@ -48,7 +45,7 @@ interface Props {
}
export function DetailPage({ id, selected, onBack }: Props): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const didExchangeAckTransfer = Amounts.isNonZero(
Amounts.parseOrThrow(selected.exchange_initial_amount),
);
@ -62,22 +59,22 @@ export function DetailPage({ id, selected, onBack }: Props): VNode {
<FormProvider object={{ ...selected, id }} valueHandler={null}>
<InputDate<Entity>
name="creation_time"
label={i18n`Created at`}
label={i18n.str`Created at`}
readonly
/>
<InputDate<Entity>
name="expiration_time"
label={i18n`Valid until`}
label={i18n.str`Valid until`}
readonly
/>
<InputCurrency<Entity>
name="merchant_initial_amount"
label={i18n`Created balance`}
label={i18n.str`Created balance`}
readonly
/>
<TextField<Entity>
name="exchange_url"
label={i18n`Exchange URL`}
label={i18n.str`Exchange URL`}
readonly
>
<a target="_blank" rel="noreferrer" href={selected.exchange_url}>
@ -89,27 +86,27 @@ export function DetailPage({ id, selected, onBack }: Props): VNode {
<Fragment>
<InputCurrency<Entity>
name="exchange_initial_amount"
label={i18n`Exchange balance`}
label={i18n.str`Exchange balance`}
readonly
/>
<InputCurrency<Entity>
name="pickup_amount"
label={i18n`Picked up`}
label={i18n.str`Picked up`}
readonly
/>
<InputCurrency<Entity>
name="committed_amount"
label={i18n`Committed`}
label={i18n.str`Committed`}
readonly
/>
</Fragment>
)}
<Input<Entity>
name="payto_uri"
label={i18n`Account address`}
label={i18n.str`Account address`}
readonly
/>
<Input name="id" label={i18n`Subject`} readonly />
<Input name="id" label={i18n.str`Subject`} readonly />
</FormProvider>
{didExchangeAckTransfer ? (
@ -120,7 +117,7 @@ export function DetailPage({ id, selected, onBack }: Props): VNode {
<span class="icon">
<i class="mdi mdi-cash-register" />
</span>
<Translate>Tips</Translate>
<i18n.Translate>Tips</i18n.Translate>
</p>
</header>
<div class="card-content">
@ -139,18 +136,18 @@ export function DetailPage({ id, selected, onBack }: Props): VNode {
) : (
<Fragment>
<p class="is-size-5">
<Translate>
<i18n.Translate>
To complete the setup of the reserve, you must now initiate a
wire transfer using the given wire transfer subject and
crediting the specified amount to the indicated account of the
exchange.
</Translate>
</i18n.Translate>
</p>
<p class="is-size-5">
<Translate>
<i18n.Translate>
If your system supports RFC 8905, you can do this by opening
this URI:
</Translate>
</i18n.Translate>
</p>
<pre>
<a target="_blank" rel="noreferrer" href={link}>
@ -163,7 +160,7 @@ export function DetailPage({ id, selected, onBack }: Props): VNode {
<div class="buttons is-right mt-5">
<button class="button" onClick={onBack}>
<Translate>Back</Translate>
<i18n.Translate>Back</i18n.Translate>
</button>
</div>
</div>
@ -174,6 +171,7 @@ export function DetailPage({ id, selected, onBack }: Props): VNode {
}
function EmptyTable(): VNode {
const { i18n } = useTranslationContext();
return (
<div class="content has-text-grey has-text-centered">
<p>
@ -182,7 +180,9 @@ function EmptyTable(): VNode {
</span>
</p>
<p>
<Translate>No tips has been authorized from this reserve</Translate>
<i18n.Translate>
No tips has been authorized from this reserve
</i18n.Translate>
</p>
</div>
);
@ -193,22 +193,23 @@ interface TableProps {
}
function Table({ tips }: TableProps): VNode {
const { i18n } = useTranslationContext();
return (
<div class="table-container">
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>
<Translate>Authorized</Translate>
<i18n.Translate>Authorized</i18n.Translate>
</th>
<th>
<Translate>Picked up</Translate>
<i18n.Translate>Picked up</i18n.Translate>
</th>
<th>
<Translate>Reason</Translate>
<i18n.Translate>Reason</i18n.Translate>
</th>
<th>
<Translate>Expiration</Translate>
<i18n.Translate>Expiration</i18n.Translate>
</th>
</tr>
</thead>

View File

@ -19,8 +19,10 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import * as yup from "yup";
import {
FormErrors,
FormProvider,
@ -32,10 +34,8 @@ import {
ContinueModal,
} from "../../../../components/modal/index.js";
import { MerchantBackend } from "../../../../declaration.js";
import { useTranslator } from "../../../../i18n/index.js";
import { AuthorizeTipSchema } from "../../../../schemas/index.js";
import { CreatedSuccessfully } from "./CreatedSuccessfully.js";
import * as yup from "yup";
interface AuthorizeTipModalProps {
onCancel: () => void;
@ -54,7 +54,7 @@ export function AuthorizeTipModal({
// const result = useOrderDetails(id)
type State = MerchantBackend.Tips.TipCreateRequest;
const [form, setValue] = useState<Partial<State>>({});
const i18n = useTranslator();
const { i18n } = useTranslationContext();
// const [errors, setErrors] = useState<FormErrors<State>>({})
let errors: FormErrors<State> = {};
@ -104,19 +104,19 @@ export function AuthorizeTipModal({
>
<InputCurrency<State>
name="amount"
label={i18n`Amount`}
tooltip={i18n`amount of tip`}
label={i18n.str`Amount`}
tooltip={i18n.str`amount of tip`}
/>
<Input<State>
name="justification"
label={i18n`Justification`}
label={i18n.str`Justification`}
inputType="multiline"
tooltip={i18n`reason for the tip`}
tooltip={i18n.str`reason for the tip`}
/>
<Input<State>
name="next_url"
label={i18n`URL after tip`}
tooltip={i18n`URL to visit after tip payment`}
label={i18n.str`URL after tip`}
tooltip={i18n.str`URL to visit after tip payment`}
/>
</FormProvider>
</ConfirmModal>

View File

@ -19,10 +19,10 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { format } from "date-fns";
import { Fragment, h, VNode } from "preact";
import { MerchantBackend, WithId } from "../../../../declaration.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
type Entity = MerchantBackend.Tips.ReserveStatusEntry & WithId;
@ -51,7 +51,7 @@ export function CardTable({
return prev;
}, new Array<Array<Entity>>([], []));
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<Fragment>
@ -62,7 +62,7 @@ export function CardTable({
<span class="icon">
<i class="mdi mdi-cash" />
</span>
<Translate>Reserves not yet funded</Translate>
<i18n.Translate>Reserves not yet funded</i18n.Translate>
</p>
</header>
<div class="card-content">
@ -86,11 +86,14 @@ export function CardTable({
<span class="icon">
<i class="mdi mdi-cash" />
</span>
<Translate>Reserves ready</Translate>
<i18n.Translate>Reserves ready</i18n.Translate>
</p>
<div class="card-header-icon" aria-label="more options" />
<div class="card-header-icon" aria-label="more options">
<span class="has-tooltip-left" data-tooltip={i18n`add new reserve`}>
<span
class="has-tooltip-left"
data-tooltip={i18n.str`add new reserve`}
>
<button class="button is-info" type="button" onClick={onCreate}>
<span class="icon is-small">
<i class="mdi mdi-plus mdi-36px" />
@ -127,26 +130,26 @@ interface TableProps {
}
function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="table-container">
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>
<Translate>Created at</Translate>
<i18n.Translate>Created at</i18n.Translate>
</th>
<th>
<Translate>Expires at</Translate>
<i18n.Translate>Expires at</i18n.Translate>
</th>
<th>
<Translate>Initial</Translate>
<i18n.Translate>Initial</i18n.Translate>
</th>
<th>
<Translate>Picked up</Translate>
<i18n.Translate>Picked up</i18n.Translate>
</th>
<th>
<Translate>Committed</Translate>
<i18n.Translate>Committed</i18n.Translate>
</th>
<th />
</tr>
@ -196,7 +199,7 @@ function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode {
<div class="buttons is-right">
<button
class="button is-small is-danger has-tooltip-left"
data-tooltip={i18n`delete selected reserve from the database`}
data-tooltip={i18n.str`delete selected reserve from the database`}
type="button"
onClick={(): void => onDelete(i)}
>
@ -204,7 +207,7 @@ function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode {
</button>
<button
class="button is-small is-info has-tooltip-left"
data-tooltip={i18n`authorize new tip from selected reserve`}
data-tooltip={i18n.str`authorize new tip from selected reserve`}
type="button"
onClick={(): void => onNewTip(i)}
>
@ -222,6 +225,7 @@ function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode {
}
function EmptyTable(): VNode {
const { i18n } = useTranslationContext();
return (
<div class="content has-text-grey has-text-centered">
<p>
@ -230,10 +234,10 @@ function EmptyTable(): VNode {
</span>
</p>
<p>
<Translate>
<i18n.Translate>
There is no ready reserves yet, add more pressing the + sign or fund
them
</Translate>
</i18n.Translate>
</p>
</div>
);
@ -244,20 +248,20 @@ function TableWithoutFund({
onSelect,
onDelete,
}: TableProps): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="table-container">
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>
<Translate>Created at</Translate>
<i18n.Translate>Created at</i18n.Translate>
</th>
<th>
<Translate>Expires at</Translate>
<i18n.Translate>Expires at</i18n.Translate>
</th>
<th>
<Translate>Expected Balance</Translate>
<i18n.Translate>Expected Balance</i18n.Translate>
</th>
<th />
</tr>
@ -296,7 +300,7 @@ function TableWithoutFund({
<button
class="button is-small is-danger jb-modal has-tooltip-left"
type="button"
data-tooltip={i18n`delete selected reserve from the database`}
data-tooltip={i18n.str`delete selected reserve from the database`}
onClick={(): void => onDelete(i)}
>
Delete

View File

@ -19,6 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js";
@ -29,10 +30,9 @@ import {
useInstanceReserves,
useReservesAPI,
} from "../../../../hooks/reserves.js";
import { useTranslator } from "../../../../i18n/index.js";
import { Notification } from "../../../../utils/types.js";
import { CardTable } from "./Table.js";
import { AuthorizeTipModal } from "./AutorizeTipModal.js";
import { CardTable } from "./Table.js";
interface Props {
onUnauthorized: () => VNode;
@ -57,7 +57,7 @@ export default function ListTips({
const result = useInstanceReserves();
const { deleteReserve, authorizeTipReserve } = useReservesAPI();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const [reserveForTip, setReserveForTip] = useState<string | undefined>(
undefined,
);
@ -93,7 +93,7 @@ export default function ListTips({
});
} catch (error) {
setNotif({
message: i18n`could not create the tip`,
message: i18n.str`could not create the tip`,
type: "ERROR",
description: error instanceof Error ? error.message : undefined,
});

View File

@ -19,6 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
@ -33,7 +34,6 @@ import { InputNumber } from "../../../../components/form/InputNumber.js";
import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
import { useBackendContext } from "../../../../context/backend.js";
import { MerchantBackend } from "../../../../declaration.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
import { undefinedIfEmpty } from "../../../../utils/table.js";
type Entity = MerchantBackend.Template.TemplateAddDetails;
@ -44,7 +44,7 @@ interface Props {
}
export function CreatePage({ onCreate, onBack }: Props): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const backend = useBackendContext();
const [state, setState] = useState<Partial<Entity>>({
@ -57,23 +57,23 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
});
const errors: FormErrors<Entity> = {
template_id: !state.template_id ? i18n`should not be empty` : undefined,
template_id: !state.template_id ? i18n.str`should not be empty` : undefined,
template_description: !state.template_description
? i18n`should not be empty`
? i18n.str`should not be empty`
: undefined,
template_contract: !state.template_contract
? undefined
: undefinedIfEmpty({
minimum_age:
state.template_contract.minimum_age < 0
? i18n`should be greater that 0`
? i18n.str`should be greater that 0`
: undefined,
pay_duration: !state.template_contract.pay_duration
? i18n`can't be empty`
? i18n.str`can't be empty`
: state.template_contract.pay_duration.d_us === "forever"
? undefined
: state.template_contract.pay_duration.d_us < 1000
? i18n`to short`
? i18n.str`to short`
: undefined,
}),
};
@ -101,57 +101,57 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
<InputWithAddon<Entity>
name="template_id"
addonBefore={`${backend.url}/instances/templates/`}
label={i18n`Identifier`}
tooltip={i18n`Name of the template in URLs.`}
label={i18n.str`Identifier`}
tooltip={i18n.str`Name of the template in URLs.`}
/>
<Input<Entity>
name="template_description"
label={i18n`Description`}
label={i18n.str`Description`}
help=""
tooltip={i18n`Describe what this template stands for`}
tooltip={i18n.str`Describe what this template stands for`}
/>
<Input
name="template_contract.summary"
inputType="multiline"
label={i18n`Order summary`}
tooltip={i18n`Title of the order to be shown to the customer`}
label={i18n.str`Order summary`}
tooltip={i18n.str`Title of the order to be shown to the customer`}
/>
<InputCurrency
name="template_contract.amount"
label={i18n`Order price`}
tooltip={i18n`Order price`}
label={i18n.str`Order price`}
tooltip={i18n.str`Order price`}
/>
<InputNumber
name="template_contract.minimum_age"
label={i18n`Minimum age`}
label={i18n.str`Minimum age`}
help=""
tooltip={i18n`Is this contract restricted to some age?`}
tooltip={i18n.str`Is this contract restricted to some age?`}
/>
<InputDuration
name="template_contract.pay_duration"
label={i18n`Payment timeout`}
label={i18n.str`Payment timeout`}
help=""
tooltip={i18n`How much time has the customer to complete the payment once the order was created.`}
tooltip={i18n.str`How much time has the customer to complete the payment once the order was created.`}
/>
</FormProvider>
<div class="buttons is-right mt-5">
{onBack && (
<button class="button" onClick={onBack}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
)}
<AsyncButton
disabled={hasErrors}
data-tooltip={
hasErrors
? i18n`Need to complete marked fields`
? i18n.str`Need to complete marked fields`
: "confirm operation"
}
onClick={submitForm}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</AsyncButton>
</div>
</div>

View File

@ -19,14 +19,12 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend } from "../../../../declaration.js";
import { useInstanceDetails } from "../../../../hooks/instance.js";
import { useTemplateAPI } from "../../../../hooks/templates.js";
import { useTransferAPI } from "../../../../hooks/transfer.js";
import { useTranslator } from "../../../../i18n/index.js";
import { Notification } from "../../../../utils/types.js";
import { CreatePage } from "./CreatePage.js";
@ -39,7 +37,7 @@ interface Props {
export default function CreateTransfer({ onConfirm, onBack }: Props): VNode {
const { createTemplate } = useTemplateAPI();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<>
@ -51,7 +49,7 @@ export default function CreateTransfer({ onConfirm, onBack }: Props): VNode {
.then(() => onConfirm())
.catch((error) => {
setNotif({
message: i18n`could not inform template`,
message: i18n.str`could not inform template`,
type: "ERROR",
description: error.message,
});

View File

@ -21,7 +21,7 @@
import { h, VNode } from "preact";
import { MerchantBackend } from "../../../../declaration.js";
import { useTranslator } from "../../../../i18n/index.js";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { CardTable } from "./Table.js";
export interface Props {
@ -43,7 +43,7 @@ export function ListPage({
}: Props): VNode {
const form = { payto_uri: "" };
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<section class="section is-main-section">
<CardTable

View File

@ -19,10 +19,10 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { StateUpdater, useState } from "preact/hooks";
import { MerchantBackend } from "../../../../declaration.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
type Entity = MerchantBackend.Template.TemplateEntry;
@ -49,7 +49,7 @@ export function CardTable({
}: Props): VNode {
const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="card has-table">
@ -58,10 +58,13 @@ export function CardTable({
<span class="icon">
<i class="mdi mdi-newspaper" />
</span>
<Translate>Templates</Translate>
<i18n.Translate>Templates</i18n.Translate>
</p>
<div class="card-header-icon" aria-label="more options">
<span class="has-tooltip-left" data-tooltip={i18n`add new templates`}>
<span
class="has-tooltip-left"
data-tooltip={i18n.str`add new templates`}
>
<button class="button is-info" type="button" onClick={onCreate}>
<span class="icon is-small">
<i class="mdi mdi-plus mdi-36px" />
@ -120,27 +123,27 @@ function Table({
hasMoreAfter,
hasMoreBefore,
}: TableProps): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="table-container">
{onLoadMoreBefore && (
<button
class="button is-fullwidth"
data-tooltip={i18n`load more templates before the first one`}
data-tooltip={i18n.str`load more templates before the first one`}
disabled={!hasMoreBefore}
onClick={onLoadMoreBefore}
>
<Translate>load newer templates</Translate>
<i18n.Translate>load newer templates</i18n.Translate>
</button>
)}
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>
<Translate>ID</Translate>
<i18n.Translate>ID</i18n.Translate>
</th>
<th>
<Translate>Description</Translate>
<i18n.Translate>Description</i18n.Translate>
</th>
<th />
</tr>
@ -164,7 +167,7 @@ function Table({
<td>
<button
class="button is-danger is-small has-tooltip-left"
data-tooltip={i18n`delete selected templates from the database`}
data-tooltip={i18n.str`delete selected templates from the database`}
onClick={() => onDelete(i)}
>
Delete
@ -178,11 +181,11 @@ function Table({
{onLoadMoreAfter && (
<button
class="button is-fullwidth"
data-tooltip={i18n`load more templates after the last one`}
data-tooltip={i18n.str`load more templates after the last one`}
disabled={!hasMoreAfter}
onClick={onLoadMoreAfter}
>
<Translate>load older templates</Translate>
<i18n.Translate>load older templates</i18n.Translate>
</button>
)}
</div>
@ -190,6 +193,7 @@ function Table({
}
function EmptyTable(): VNode {
const { i18n } = useTranslationContext();
return (
<div class="content has-text-grey has-text-centered">
<p>
@ -198,9 +202,9 @@ function EmptyTable(): VNode {
</span>
</p>
<p>
<Translate>
<i18n.Translate>
There is no templates yet, add more pressing the + sign
</Translate>
</i18n.Translate>
</p>
</div>
);

View File

@ -19,6 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js";
@ -29,7 +30,6 @@ import {
useInstanceTemplates,
useTemplateAPI,
} from "../../../../hooks/templates.js";
import { useTranslator } from "../../../../i18n/index.js";
import { Notification } from "../../../../utils/types.js";
import { ListPage } from "./ListPage.js";
@ -49,7 +49,7 @@ export default function ListTemplates({
onNotFound,
}: Props): VNode {
const [position, setPosition] = useState<string | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const { deleteTemplate } = useTemplateAPI();
const result = useInstanceTemplates({ position }, (id) => setPosition(id));
@ -77,13 +77,13 @@ export default function ListTemplates({
deleteTemplate(e.template_id)
.then(() =>
setNotif({
message: i18n`template delete successfully`,
message: i18n.str`template delete successfully`,
type: "SUCCESS",
}),
)
.catch((error) =>
setNotif({
message: i18n`could not delete the template`,
message: i18n.str`could not delete the template`,
type: "ERROR",
description: error.message,
}),

View File

@ -19,6 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
@ -31,11 +32,8 @@ import { InputCurrency } from "../../../../components/form/InputCurrency.js";
import { InputDuration } from "../../../../components/form/InputDuration.js";
import { InputNumber } from "../../../../components/form/InputNumber.js";
import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
import { ProductForm } from "../../../../components/product/ProductForm.js";
import { useBackendContext } from "../../../../context/backend.js";
import { MerchantBackend, WithId } from "../../../../declaration.js";
import { useListener } from "../../../../hooks/listener.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
import { undefinedIfEmpty } from "../../../../utils/table.js";
type Entity = MerchantBackend.Template.TemplatePatchDetails & WithId;
@ -47,28 +45,28 @@ interface Props {
}
export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const backend = useBackendContext();
const [state, setState] = useState<Partial<Entity>>(template);
const errors: FormErrors<Entity> = {
template_description: !state.template_description
? i18n`should not be empty`
? i18n.str`should not be empty`
: undefined,
template_contract: !state.template_contract
? undefined
: undefinedIfEmpty({
minimum_age:
state.template_contract.minimum_age < 0
? i18n`should be greater that 0`
? i18n.str`should be greater that 0`
: undefined,
pay_duration: !state.template_contract.pay_duration
? i18n`can't be empty`
? i18n.str`can't be empty`
: state.template_contract.pay_duration.d_us === "forever"
? undefined
: state.template_contract.pay_duration.d_us < 1000
? i18n`to short`
? i18n.str`to short`
: undefined,
}),
};
@ -112,57 +110,57 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
name="id"
addonBefore={`templates/`}
readonly
label={i18n`Identifier`}
tooltip={i18n`Name of the template in URLs.`}
label={i18n.str`Identifier`}
tooltip={i18n.str`Name of the template in URLs.`}
/>
<Input<Entity>
name="template_description"
label={i18n`Description`}
label={i18n.str`Description`}
help=""
tooltip={i18n`Describe what this template stands for`}
tooltip={i18n.str`Describe what this template stands for`}
/>
<Input
name="template_contract.summary"
inputType="multiline"
label={i18n`Order summary`}
tooltip={i18n`Title of the order to be shown to the customer`}
label={i18n.str`Order summary`}
tooltip={i18n.str`Title of the order to be shown to the customer`}
/>
<InputCurrency
name="template_contract.amount"
label={i18n`Order price`}
tooltip={i18n`total product price added up`}
label={i18n.str`Order price`}
tooltip={i18n.str`total product price added up`}
/>
<InputNumber
name="template_contract.minimum_age"
label={i18n`Minimum age`}
label={i18n.str`Minimum age`}
help=""
tooltip={i18n`Is this contract restricted to some age?`}
tooltip={i18n.str`Is this contract restricted to some age?`}
/>
<InputDuration
name="template_contract.pay_duration"
label={i18n`Payment timeout`}
label={i18n.str`Payment timeout`}
help=""
tooltip={i18n`How much time has the customer to complete the payment once the order was created.`}
tooltip={i18n.str`How much time has the customer to complete the payment once the order was created.`}
/>
</FormProvider>
<div class="buttons is-right mt-5">
{onBack && (
<button class="button" onClick={onBack}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
)}
<AsyncButton
disabled={hasErrors}
data-tooltip={
hasErrors
? i18n`Need to complete marked fields`
? i18n.str`Need to complete marked fields`
: "confirm operation"
}
onClick={submitForm}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</AsyncButton>
</div>
</div>

View File

@ -19,18 +19,17 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend, WithId } from "../../../../declaration.js";
import { HttpError } from "../../../../hooks/backend.js";
import { useProductAPI, useProductDetails } from "../../../../hooks/product.js";
import {
useTemplateAPI,
useTemplateDetails,
} from "../../../../hooks/templates.js";
import { useTranslator } from "../../../../i18n/index.js";
import { Notification } from "../../../../utils/types.js";
import { UpdatePage } from "./UpdatePage.js";
@ -56,7 +55,7 @@ export default function UpdateTemplate({
const result = useTemplateDetails(tid);
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
if (result.clientError && result.isUnauthorized) return onUnauthorized();
if (result.clientError && result.isNotfound) return onNotFound();
@ -74,7 +73,7 @@ export default function UpdateTemplate({
.then(onConfirm)
.catch((error) => {
setNotif({
message: i18n`could not update template`,
message: i18n.str`could not update template`,
type: "ERROR",
description: error.message,
});

View File

@ -19,6 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
@ -31,7 +32,6 @@ import { InputCurrency } from "../../../../components/form/InputCurrency.js";
import { InputSelector } from "../../../../components/form/InputSelector.js";
import { useConfigContext } from "../../../../context/config.js";
import { MerchantBackend } from "../../../../declaration.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
import {
CROCKFORD_BASE32_REGEX,
URL_REGEX,
@ -46,7 +46,7 @@ interface Props {
}
export function CreatePage({ accounts, onCreate, onBack }: Props): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const { currency } = useConfigContext();
const [state, setState] = useState<Partial<Entity>>({
@ -58,18 +58,18 @@ export function CreatePage({ accounts, onCreate, onBack }: Props): VNode {
const errors: FormErrors<Entity> = {
wtid: !state.wtid
? i18n`cannot be empty`
? i18n.str`cannot be empty`
: !CROCKFORD_BASE32_REGEX.test(state.wtid)
? i18n`check the id, does not look valid`
? i18n.str`check the id, does not look valid`
: state.wtid.length !== 52
? i18n`should have 52 characters, current ${state.wtid.length}`
? i18n.str`should have 52 characters, current ${state.wtid.length}`
: undefined,
payto_uri: !state.payto_uri ? i18n`cannot be empty` : undefined,
credit_amount: !state.credit_amount ? i18n`cannot be empty` : undefined,
payto_uri: !state.payto_uri ? i18n.str`cannot be empty` : undefined,
credit_amount: !state.credit_amount ? i18n.str`cannot be empty` : undefined,
exchange_url: !state.exchange_url
? i18n`cannot be empty`
? i18n.str`cannot be empty`
: !URL_REGEX.test(state.exchange_url)
? i18n`URL doesn't have the right format`
? i18n.str`URL doesn't have the right format`
: undefined,
};
@ -95,46 +95,46 @@ export function CreatePage({ accounts, onCreate, onBack }: Props): VNode {
>
<InputSelector
name="payto_uri"
label={i18n`Credited bank account`}
label={i18n.str`Credited bank account`}
values={accounts}
placeholder={i18n`Select one account`}
tooltip={i18n`Bank account of the merchant where the payment was received`}
placeholder={i18n.str`Select one account`}
tooltip={i18n.str`Bank account of the merchant where the payment was received`}
/>
<Input<Entity>
name="wtid"
label={i18n`Wire transfer ID`}
label={i18n.str`Wire transfer ID`}
help=""
tooltip={i18n`unique identifier of the wire transfer used by the exchange, must be 52 characters long`}
tooltip={i18n.str`unique identifier of the wire transfer used by the exchange, must be 52 characters long`}
/>
<Input<Entity>
name="exchange_url"
label={i18n`Exchange URL`}
tooltip={i18n`Base URL of the exchange that made the transfer, should have been in the wire transfer subject`}
label={i18n.str`Exchange URL`}
tooltip={i18n.str`Base URL of the exchange that made the transfer, should have been in the wire transfer subject`}
help="http://exchange.taler:8081/"
/>
<InputCurrency<Entity>
name="credit_amount"
label={i18n`Amount credited`}
tooltip={i18n`Actual amount that was wired to the merchant's bank account`}
label={i18n.str`Amount credited`}
tooltip={i18n.str`Actual amount that was wired to the merchant's bank account`}
/>
</FormProvider>
<div class="buttons is-right mt-5">
{onBack && (
<button class="button" onClick={onBack}>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
)}
<AsyncButton
disabled={hasErrors}
data-tooltip={
hasErrors
? i18n`Need to complete marked fields`
? i18n.str`Need to complete marked fields`
: "confirm operation"
}
onClick={submitForm}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</AsyncButton>
</div>
</div>

View File

@ -19,13 +19,13 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend } from "../../../../declaration.js";
import { useInstanceDetails } from "../../../../hooks/instance.js";
import { useTransferAPI } from "../../../../hooks/transfer.js";
import { useTranslator } from "../../../../i18n/index.js";
import { Notification } from "../../../../utils/types.js";
import { CreatePage } from "./CreatePage.js";
@ -38,7 +38,7 @@ interface Props {
export default function CreateTransfer({ onConfirm, onBack }: Props): VNode {
const { informTransfer } = useTransferAPI();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const instance = useInstanceDetails();
const accounts = !instance.ok
? []
@ -55,7 +55,7 @@ export default function CreateTransfer({ onConfirm, onBack }: Props): VNode {
.then(() => onConfirm())
.catch((error) => {
setNotif({
message: i18n`could not inform transfer`,
message: i18n.str`could not inform transfer`,
type: "ERROR",
description: error.message,
});

View File

@ -19,11 +19,11 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { FormProvider } from "../../../../components/form/FormProvider.js";
import { InputSelector } from "../../../../components/form/InputSelector.js";
import { MerchantBackend } from "../../../../declaration.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
import { CardTable } from "./Table.js";
export interface Props {
@ -61,7 +61,7 @@ export function ListPage({
}: Props): VNode {
const form = { payto_uri: payTo };
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<section class="section is-main-section">
<div class="columns">
@ -73,10 +73,10 @@ export function ListPage({
>
<InputSelector
name="payto_uri"
label={i18n`Address`}
label={i18n.str`Address`}
values={accounts}
placeholder={i18n`Select one account`}
tooltip={i18n`filter by account address`}
placeholder={i18n.str`Select one account`}
tooltip={i18n.str`filter by account address`}
/>
</FormProvider>
</div>
@ -87,30 +87,30 @@ export function ListPage({
<li class={isAllTransfers ? "is-active" : ""}>
<div
class="has-tooltip-right"
data-tooltip={i18n`remove all filters`}
data-tooltip={i18n.str`remove all filters`}
>
<a onClick={onShowAll}>
<Translate>All</Translate>
<i18n.Translate>All</i18n.Translate>
</a>
</div>
</li>
<li class={isVerifiedTransfers ? "is-active" : ""}>
<div
class="has-tooltip-right"
data-tooltip={i18n`only show wire transfers confirmed by the merchant`}
data-tooltip={i18n.str`only show wire transfers confirmed by the merchant`}
>
<a onClick={onShowVerified}>
<Translate>Verified</Translate>
<i18n.Translate>Verified</i18n.Translate>
</a>
</div>
</li>
<li class={isNonVerifiedTransfers ? "is-active" : ""}>
<div
class="has-tooltip-right"
data-tooltip={i18n`only show wire transfers claimed by the exchange`}
data-tooltip={i18n.str`only show wire transfers claimed by the exchange`}
>
<a onClick={onShowUnverified}>
<Translate>Unverified</Translate>
<i18n.Translate>Unverified</i18n.Translate>
</a>
</div>
</li>

View File

@ -19,11 +19,11 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { format } from "date-fns";
import { h, VNode } from "preact";
import { StateUpdater, useState } from "preact/hooks";
import { MerchantBackend, WithId } from "../../../../declaration.js";
import { Translate, useTranslator } from "../../../../i18n/index.js";
type Entity = MerchantBackend.Transfers.TransferDetails & WithId;
@ -49,7 +49,7 @@ export function CardTable({
}: Props): VNode {
const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="card has-table">
@ -58,10 +58,13 @@ export function CardTable({
<span class="icon">
<i class="mdi mdi-bank" />
</span>
<Translate>Transfers</Translate>
<i18n.Translate>Transfers</i18n.Translate>
</p>
<div class="card-header-icon" aria-label="more options">
<span class="has-tooltip-left" data-tooltip={i18n`add new transfer`}>
<span
class="has-tooltip-left"
data-tooltip={i18n.str`add new transfer`}
>
<button class="button is-info" type="button" onClick={onCreate}>
<span class="icon is-small">
<i class="mdi mdi-plus mdi-36px" />
@ -117,42 +120,42 @@ function Table({
hasMoreAfter,
hasMoreBefore,
}: TableProps): VNode {
const i18n = useTranslator();
const { i18n } = useTranslationContext();
return (
<div class="table-container">
{onLoadMoreBefore && (
<button
class="button is-fullwidth"
data-tooltip={i18n`load more transfers before the first one`}
data-tooltip={i18n.str`load more transfers before the first one`}
disabled={!hasMoreBefore}
onClick={onLoadMoreBefore}
>
<Translate>load newer transfers</Translate>
<i18n.Translate>load newer transfers</i18n.Translate>
</button>
)}
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>
<Translate>ID</Translate>
<i18n.Translate>ID</i18n.Translate>
</th>
<th>
<Translate>Credit</Translate>
<i18n.Translate>Credit</i18n.Translate>
</th>
<th>
<Translate>Address</Translate>
<i18n.Translate>Address</i18n.Translate>
</th>
<th>
<Translate>Exchange URL</Translate>
<i18n.Translate>Exchange URL</i18n.Translate>
</th>
<th>
<Translate>Confirmed</Translate>
<i18n.Translate>Confirmed</i18n.Translate>
</th>
<th>
<Translate>Verified</Translate>
<i18n.Translate>Verified</i18n.Translate>
</th>
<th>
<Translate>Executed at</Translate>
<i18n.Translate>Executed at</i18n.Translate>
</th>
<th />
</tr>
@ -165,23 +168,23 @@ function Table({
<td>{i.credit_amount}</td>
<td>{i.payto_uri}</td>
<td>{i.exchange_url}</td>
<td>{i.confirmed ? i18n`yes` : i18n`no`}</td>
<td>{i.verified ? i18n`yes` : i18n`no`}</td>
<td>{i.confirmed ? i18n.str`yes` : i18n.str`no`}</td>
<td>{i.verified ? i18n.str`yes` : i18n.str`no`}</td>
<td>
{i.execution_time
? i.execution_time.t_s == "never"
? i18n`never`
? i18n.str`never`
: format(
i.execution_time.t_s * 1000,
"yyyy/MM/dd HH:mm:ss",
)
: i18n`unknown`}
: i18n.str`unknown`}
</td>
<td>
{i.verified === undefined ? (
<button
class="button is-danger is-small has-tooltip-left"
data-tooltip={i18n`delete selected transfer from the database`}
data-tooltip={i18n.str`delete selected transfer from the database`}
onClick={() => onDelete(i)}
>
Delete
@ -196,11 +199,11 @@ function Table({
{onLoadMoreAfter && (
<button
class="button is-fullwidth"
data-tooltip={i18n`load more transfer after the last one`}
data-tooltip={i18n.str`load more transfer after the last one`}
disabled={!hasMoreAfter}
onClick={onLoadMoreAfter}
>
<Translate>load older transfers</Translate>
<i18n.Translate>load older transfers</i18n.Translate>
</button>
)}
</div>
@ -208,6 +211,7 @@ function Table({
}
function EmptyTable(): VNode {
const { i18n } = useTranslationContext();
return (
<div class="content has-text-grey has-text-centered">
<p>
@ -216,9 +220,9 @@ function EmptyTable(): VNode {
</span>
</p>
<p>
<Translate>
<i18n.Translate>
There is no transfer yet, add more pressing the + sign
</Translate>
</i18n.Translate>
</p>
</div>
);

View File

@ -19,21 +19,20 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import { Amounts } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import * as yup from "yup";
import { AsyncButton } from "../../../components/exception/AsyncButton.js";
import {
FormProvider,
FormErrors,
FormProvider,
} from "../../../components/form/FormProvider.js";
import { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields.js";
import { UpdateTokenModal } from "../../../components/modal/index.js";
import { useInstanceContext } from "../../../context/instance.js";
import { MerchantBackend } from "../../../declaration.js";
import { Translate, useTranslator } from "../../../i18n/index.js";
import { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields.js";
import { PAYTO_REGEX } from "../../../utils/constants.js";
import { Amounts } from "@gnu-taler/taler-util";
import { undefinedIfEmpty } from "../../../utils/table.js";
type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & {
@ -95,50 +94,52 @@ export function UpdatePage({
const [value, valueHandler] = useState<Partial<Entity>>(convert(selected));
const i18n = useTranslator();
const { i18n } = useTranslationContext();
const errors: FormErrors<Entity> = {
name: !value.name ? i18n`required` : undefined,
name: !value.name ? i18n.str`required` : undefined,
payto_uris:
!value.payto_uris || !value.payto_uris.length
? i18n`required`
? i18n.str`required`
: undefinedIfEmpty(
value.payto_uris.map((p) => {
return !PAYTO_REGEX.test(p) ? i18n`is not valid` : undefined;
return !PAYTO_REGEX.test(p) ? i18n.str`is not valid` : undefined;
}),
),
default_max_deposit_fee: !value.default_max_deposit_fee
? i18n`required`
? i18n.str`required`
: !Amounts.parse(value.default_max_deposit_fee)
? i18n`invalid format`
? i18n.str`invalid format`
: undefined,
default_max_wire_fee: !value.default_max_wire_fee
? i18n`required`
? i18n.str`required`
: !Amounts.parse(value.default_max_wire_fee)
? i18n`invalid format`
? i18n.str`invalid format`
: undefined,
default_wire_fee_amortization:
value.default_wire_fee_amortization === undefined
? i18n`required`
? i18n.str`required`
: isNaN(value.default_wire_fee_amortization)
? i18n`is not a number`
? i18n.str`is not a number`
: value.default_wire_fee_amortization < 1
? i18n`must be 1 or greater`
? i18n.str`must be 1 or greater`
: undefined,
default_pay_delay: !value.default_pay_delay ? i18n`required` : undefined,
default_pay_delay: !value.default_pay_delay
? i18n.str`required`
: undefined,
default_wire_transfer_delay: !value.default_wire_transfer_delay
? i18n`required`
? i18n.str`required`
: undefined,
address: undefinedIfEmpty({
address_lines:
value.address?.address_lines && value.address?.address_lines.length > 7
? i18n`max 7 lines`
? i18n.str`max 7 lines`
: undefined,
}),
jurisdiction: undefinedIfEmpty({
address_lines:
value.address?.address_lines && value.address?.address_lines.length > 7
? i18n`max 7 lines`
? i18n.str`max 7 lines`
: undefined,
}),
};
@ -160,7 +161,7 @@ export function UpdatePage({
<div class="level-left">
<div class="level-item">
<span class="is-size-4">
<Translate>Instance id</Translate>: <b>{id}</b>
<i18n.Translate>Instance id</i18n.Translate>: <b>{id}</b>
</span>
</div>
</div>
@ -169,7 +170,7 @@ export function UpdatePage({
<h1 class="title">
<button
class="button is-danger"
data-tooltip={i18n`Change the authorization method use for this instance.`}
data-tooltip={i18n.str`Change the authorization method use for this instance.`}
onClick={(): void => {
setActive(!active);
}}
@ -178,7 +179,7 @@ export function UpdatePage({
<i class="mdi mdi-lock-reset" />
</div>
<span>
<Translate>Manage access token</Translate>
<i18n.Translate>Manage access token</i18n.Translate>
</span>
</button>
</h1>
@ -229,19 +230,19 @@ export function UpdatePage({
onClick={onBack}
data-tooltip="cancel operation"
>
<Translate>Cancel</Translate>
<i18n.Translate>Cancel</i18n.Translate>
</button>
<AsyncButton
onClick={submit}
data-tooltip={
hasErrors
? i18n`Need to complete marked fields`
? i18n.str`Need to complete marked fields`
: "confirm operation"
}
disabled={hasErrors}
>
<Translate>Confirm</Translate>
<i18n.Translate>Confirm</i18n.Translate>
</AsyncButton>
</div>
</div>

View File

@ -13,6 +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 { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Loading } from "../../../components/exception/loading.js";
@ -26,7 +27,6 @@ import {
useManagedInstanceDetails,
useManagementAPI,
} from "../../../hooks/instance.js";
import { useTranslator } from "../../../i18n/index.js";
import { Notification } from "../../../utils/types.js";
import { UpdatePage } from "./UpdatePage.js";
@ -70,7 +70,7 @@ function CommonUpdate(
): VNode {
const { changeToken } = useInstanceContext();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const i18n = useTranslator();
const { i18n } = useTranslationContext();
if (result.clientError && result.isUnauthorized) return onUnauthorized();
if (result.clientError && result.isNotfound) return onNotFound();
@ -91,7 +91,7 @@ function CommonUpdate(
.then(onConfirm)
.catch((error: Error) =>
setNotif({
message: i18n`Failed to create instance`,
message: i18n.str`Failed to create instance`,
type: "ERROR",
description: error.message,
}),

View File

@ -20,15 +20,13 @@
*/
import { setupI18n } from "@gnu-taler/taler-util";
import { parseGroupImport } from "@gnu-taler/web-util/lib/index.browser";
import { h as create } from "preact";
import { render as renderToString } from "preact-render-to-string";
import * as admin from "./paths/admin/index.stories.js";
import * as instance from "./paths/instance/index.stories.js";
setupI18n("en", { en: {} });
import { h as create } from "preact"
import { render as renderToString } from "preact-render-to-string";
describe("All the examples:", () => {
const cms = parseGroupImport({ admin, instance });
cms.forEach((group) => {
@ -37,8 +35,11 @@ describe("All the examples:", () => {
describe(`Component: ${component.name}`, () => {
component.examples.forEach((example) => {
it(`should render example: ${example.name}`, () => {
const vdom = create(example.render.component, example.render.props)
const html = renderToString(vdom)
const vdom = create(
example.render.component,
example.render.props,
);
const html = renderToString(vdom);
// console.log(html)
});
});

View File

@ -14,10 +14,10 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { AMOUNT_REGEX, PAYTO_REGEX } from "../../src/utils/constants.js";
@ -29,11 +29,11 @@ describe('payto uri format', () => {
'payto://void/?amount=EUR:10.5',
'payto://ilp/g.acme.bob'
]
test('should be valid', () => {
it('should be valid', () => {
valids.forEach(v => expect(v).toMatch(PAYTO_REGEX))
});
const invalids = [
// has two question marks
'payto://iban/DE75?512108001245126199?amount=EUR:200.0&message=hello',
@ -46,10 +46,10 @@ describe('payto uri format', () => {
// payto:// is incomplete
'payto: //ilp/g.acme.bob'
]
test('should not be valid', () => {
it('should not be valid', () => {
invalids.forEach(v => expect(v).not.toMatch(PAYTO_REGEX))
});
});
})
describe('amount format', () => {
@ -62,11 +62,11 @@ describe('amount format', () => {
'ARSCOL:10',
'THISISTHEMOTHERCOIN:1,000,000.123,123',
]
test('should be valid', () => {
it('should be valid', () => {
valids.forEach(v => expect(v).toMatch(AMOUNT_REGEX))
});
const invalids = [
//no currency name
':10',
@ -79,9 +79,9 @@ describe('amount format', () => {
//missing value
'USD:',
]
test('should not be valid', () => {
it('should not be valid', () => {
invalids.forEach(v => expect(v).not.toMatch(AMOUNT_REGEX))
});
});
})

View File

@ -23,23 +23,23 @@ import { h } from "preact";
import { ProductList } from "../src/components/product/ProductList.js";
// See: https://github.com/preactjs/enzyme-adapter-preact-pure
// import { shallow } from 'enzyme';
import { render } from "@testing-library/preact";
import * as backend from "../src/context/config.js";
import { render, findAllByText } from "@testing-library/preact";
import * as i18n from "../src/context/translation.js";
// import * as i18n from "../src/context/translation.js";
import * as jedLib from "jed";
const handler = new jedLib.Jed("en");
// import * as jedLib from "jed";
// const handler = new jedLib.Jed("en");
describe("Initial Test of the Sidebar", () => {
beforeEach(() => {
jest
.spyOn(backend, "useConfigContext")
.mockImplementation(() => ({ version: "", currency: "" }));
jest.spyOn(i18n, "useTranslationContext").mockImplementation(() => ({
changeLanguage: () => null,
handler,
lang: "en",
}));
// jest.spyOn(i18n, "useTranslationContext").mockImplementation(() => ({
// changeLanguage: () => null,
// handler,
// lang: "en",
// }));
});
test("Product list renders a table", () => {
const context = render(

View File

@ -120,6 +120,7 @@ interface Mounted<T> {
*
* @returns testing API
*/
// eslint-disable-next-line @typescript-eslint/ban-types
export function mountHook<T extends object>(
hookToBeTested: () => RecursiveState<T>,
Context?: ({ children }: { children: any }) => VNode | null,
@ -242,6 +243,7 @@ interface HookTestResultError {
*
* @returns testing result, should also be checked to be "ok"
*/
// eslint-disable-next-line @typescript-eslint/ban-types
export async function hookBehaveLikeThis<T extends object, PropsType>(
hookFunction: (p: PropsType) => RecursiveState<T>,
props: PropsType,

View File

@ -374,7 +374,7 @@ importers:
jest: ^26.6.3
jest-preset-preact: ^4.0.2
mocha: ^9.2.0
preact: 10.6.5
preact: 10.11.3
preact-render-to-string: ^5.2.6
preact-router: 3.2.1
qrcode-generator: 1.4.4
@ -391,16 +391,16 @@ importers:
date-fns: 2.29.3
history: 4.10.1
jed: 1.1.1
preact: 10.6.5
preact-router: 3.2.1_preact@10.6.5
preact: 10.11.3
preact-router: 3.2.1_preact@10.11.3
qrcode-generator: 1.4.4
swr: 1.3.0
yup: 0.32.11
devDependencies:
'@creativebulma/bulma-tooltip': 1.2.0
'@gnu-taler/pogen': link:../pogen
'@testing-library/preact': 2.0.1_preact@10.6.5
'@testing-library/preact-hooks': 1.1.0_vfcmu6iy7nffpurikpgxo6gwxi
'@testing-library/preact': 2.0.1_preact@10.11.3
'@testing-library/preact-hooks': 1.1.0_eng4adldpgibddgycwaukopxga
'@types/history': 4.7.11
'@types/jest': 26.0.24
'@types/mocha': 8.2.3
@ -424,9 +424,9 @@ importers:
html-webpack-skip-assets-plugin: 1.0.3
inline-chunk-html-plugin: 1.1.1
jest: 26.6.3
jest-preset-preact: 4.0.5_moqeqtbsr7edkxzj3jgnhqkxsm
jest-preset-preact: 4.0.5_w5bq6jgm3cbfmbu2zwqko4iate
mocha: 9.2.2
preact-render-to-string: 5.2.6_preact@10.6.5
preact-render-to-string: 5.2.6_preact@10.11.3
rimraf: 3.0.2
sass: 1.56.1
typedoc: 0.20.37_typescript@4.8.4
@ -5795,14 +5795,14 @@ packages:
preact: 10.11.2
dev: true
/@testing-library/preact-hooks/1.1.0_vfcmu6iy7nffpurikpgxo6gwxi:
/@testing-library/preact-hooks/1.1.0_eng4adldpgibddgycwaukopxga:
resolution: {integrity: sha512-+JIor+NsOHkK3oIrwMDGKGHXTN0JJi462dBJlj4FNbGaDPTlctE6eu2ranWQirh7/FJMkWfzQCP+tk7jmY8ZrQ==}
peerDependencies:
'@testing-library/preact': ^2.0.0
preact: ^10.4.8
dependencies:
'@testing-library/preact': 2.0.1_preact@10.6.5
preact: 10.6.5
'@testing-library/preact': 2.0.1_preact@10.11.3
preact: 10.11.3
dev: true
/@testing-library/preact/2.0.1_preact@10.11.2:
@ -5815,14 +5815,14 @@ packages:
preact: 10.11.2
dev: true
/@testing-library/preact/2.0.1_preact@10.6.5:
/@testing-library/preact/2.0.1_preact@10.11.3:
resolution: {integrity: sha512-79kwVOY+3caoLgaPbiPzikjgY0Aya7Fc7TvGtR1upCnz2wrtmPDnN2t9vO7I7vDP2zoA+feSwOH5Q0BFErhaaQ==}
engines: {node: '>= 10'}
peerDependencies:
preact: '>=10 || ^10.0.0-alpha.0 || ^10.0.0-beta.0'
dependencies:
'@testing-library/dom': 7.31.2
preact: 10.6.5
preact: 10.11.3
dev: true
/@tootallnate/once/1.1.2:
@ -14567,7 +14567,7 @@ packages:
- supports-color
dev: true
/jest-preset-preact/4.0.5_moqeqtbsr7edkxzj3jgnhqkxsm:
/jest-preset-preact/4.0.5_w5bq6jgm3cbfmbu2zwqko4iate:
resolution: {integrity: sha512-MnU7mfpnwopJkdx0WoEyRmrNDIvRN+w6sOur0zEhaRYYMo0gJM7UdZHWTV8k6uo0+ypY+m0kQW6kMukUx4v8JQ==}
peerDependencies:
jest: 26.x || 27.x
@ -14584,8 +14584,8 @@ packages:
isomorphic-unfetch: 3.1.0
jest: 26.6.3
jest-watch-typeahead: 0.6.5_jest@26.6.3
preact: 10.6.5
preact-render-to-string: 5.2.6_preact@10.6.5
preact: 10.11.3
preact-render-to-string: 5.2.6_preact@10.11.3
transitivePeerDependencies:
- encoding
- supports-color
@ -17955,15 +17955,6 @@ packages:
preact: 10.11.3
pretty-format: 3.8.0
/preact-render-to-string/5.2.6_preact@10.6.5:
resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==}
peerDependencies:
preact: '>=10'
dependencies:
preact: 10.6.5
pretty-format: 3.8.0
dev: true
/preact-router/3.2.1_preact@10.11.2:
resolution: {integrity: sha512-KEN2VN1DxUlTwzW5IFkF13YIA2OdQ2OvgJTkQREF+AA2NrHRLaGbB68EjS4IeZOa1shvQ1FvEm3bSLta4sXBhg==}
peerDependencies:
@ -17980,23 +17971,12 @@ packages:
preact: 10.11.3
dev: false
/preact-router/3.2.1_preact@10.6.5:
resolution: {integrity: sha512-KEN2VN1DxUlTwzW5IFkF13YIA2OdQ2OvgJTkQREF+AA2NrHRLaGbB68EjS4IeZOa1shvQ1FvEm3bSLta4sXBhg==}
peerDependencies:
preact: '>=10'
dependencies:
preact: 10.6.5
dev: false
/preact/10.11.2:
resolution: {integrity: sha512-skAwGDFmgxhq1DCBHke/9e12ewkhc7WYwjuhHB8HHS8zkdtITXLRmUMTeol2ldxvLwYtwbFeifZ9uDDWuyL4Iw==}
/preact/10.11.3:
resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==}
/preact/10.6.5:
resolution: {integrity: sha512-i+LXM6JiVjQXSt2jG2vZZFapGpCuk1fl8o6ii3G84MA3xgj686FKjs4JFDkmUVhtxyq21+4ay74zqPykz9hU6w==}
/prelude-ls/1.1.2:
resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
engines: {node: '>= 0.8.0'}