use translation context from web-utils, don't use match react-router since is broken
This commit is contained in:
parent
382e66b179
commit
c59f9a2556
@ -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;
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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}
|
||||
/>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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>
|
||||
|
@ -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`}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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) => (
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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.
|
||||
"USD:2.3".
|
||||
</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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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>"{instance}"</b>.{" "}
|
||||
<a
|
||||
href="#/instances"
|
||||
onClick={(e) => {
|
||||
setInstanceName("default");
|
||||
}}
|
||||
>
|
||||
go back
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</WithTitle>
|
||||
);
|
||||
}}
|
||||
</Match>
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
</WithTitle>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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>"{element.name}"</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>"{element.name}"</b> (ID:{" "}
|
||||
<b>{element.id}</b>), you will also delete all it'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'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>
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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 = ({
|
||||
|
@ -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);
|
@ -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);
|
@ -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>;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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
|
||||
// }
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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, {
|
||||
|
@ -15,4 +15,3 @@
|
||||
*/
|
||||
|
||||
export * as details from "./details/stories.js";
|
||||
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
}),
|
||||
|
@ -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);
|
||||
}}
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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,
|
||||
}),
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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}>
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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,
|
||||
}),
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
}),
|
||||
|
@ -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)
|
||||
});
|
||||
});
|
||||
|
@ -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))
|
||||
});
|
||||
});
|
||||
|
||||
})
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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'}
|
||||
|
Loading…
Reference in New Issue
Block a user