summaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'packages/merchant-backoffice/src/components')
-rw-r--r--packages/merchant-backoffice/src/components/exception/AsyncButton.tsx49
-rw-r--r--packages/merchant-backoffice/src/components/exception/QR.tsx49
-rw-r--r--packages/merchant-backoffice/src/components/exception/loading.tsx32
-rw-r--r--packages/merchant-backoffice/src/components/exception/login.tsx143
-rw-r--r--packages/merchant-backoffice/src/components/form/FormProvider.tsx81
-rw-r--r--packages/merchant-backoffice/src/components/form/Input.tsx71
-rw-r--r--packages/merchant-backoffice/src/components/form/InputArray.tsx97
-rw-r--r--packages/merchant-backoffice/src/components/form/InputBoolean.tsx72
-rw-r--r--packages/merchant-backoffice/src/components/form/InputCurrency.tsx47
-rw-r--r--packages/merchant-backoffice/src/components/form/InputDate.tsx159
-rw-r--r--packages/merchant-backoffice/src/components/form/InputDuration.tsx172
-rw-r--r--packages/merchant-backoffice/src/components/form/InputGroup.tsx66
-rw-r--r--packages/merchant-backoffice/src/components/form/InputImage.tsx95
-rw-r--r--packages/merchant-backoffice/src/components/form/InputLocation.tsx43
-rw-r--r--packages/merchant-backoffice/src/components/form/InputNumber.tsx42
-rw-r--r--packages/merchant-backoffice/src/components/form/InputPayto.tsx39
-rw-r--r--packages/merchant-backoffice/src/components/form/InputPaytoForm.tsx392
-rw-r--r--packages/merchant-backoffice/src/components/form/InputSearchProduct.tsx139
-rw-r--r--packages/merchant-backoffice/src/components/form/InputSecured.stories.tsx55
-rw-r--r--packages/merchant-backoffice/src/components/form/InputSecured.tsx119
-rw-r--r--packages/merchant-backoffice/src/components/form/InputSelector.tsx86
-rw-r--r--packages/merchant-backoffice/src/components/form/InputStock.stories.tsx162
-rw-r--r--packages/merchant-backoffice/src/components/form/InputStock.tsx171
-rw-r--r--packages/merchant-backoffice/src/components/form/InputTaxes.tsx97
-rw-r--r--packages/merchant-backoffice/src/components/form/InputWithAddon.tsx77
-rw-r--r--packages/merchant-backoffice/src/components/form/TextField.tsx53
-rw-r--r--packages/merchant-backoffice/src/components/form/useField.tsx86
-rw-r--r--packages/merchant-backoffice/src/components/form/useGroupField.tsx40
-rw-r--r--packages/merchant-backoffice/src/components/instance/DefaultInstanceFormFields.tsx135
-rw-r--r--packages/merchant-backoffice/src/components/menu/LangSelector.tsx73
-rw-r--r--packages/merchant-backoffice/src/components/menu/NavigationBar.tsx58
-rw-r--r--packages/merchant-backoffice/src/components/menu/SideBar.tsx227
-rw-r--r--packages/merchant-backoffice/src/components/menu/index.tsx210
-rw-r--r--packages/merchant-backoffice/src/components/modal/index.tsx262
-rw-r--r--packages/merchant-backoffice/src/components/notifications/CreatedSuccessfully.tsx49
-rw-r--r--packages/merchant-backoffice/src/components/notifications/Notifications.stories.tsx57
-rw-r--r--packages/merchant-backoffice/src/components/notifications/index.tsx52
-rw-r--r--packages/merchant-backoffice/src/components/picker/DatePicker.tsx324
-rw-r--r--packages/merchant-backoffice/src/components/picker/DurationPicker.stories.tsx50
-rw-r--r--packages/merchant-backoffice/src/components/picker/DurationPicker.tsx211
-rw-r--r--packages/merchant-backoffice/src/components/product/InventoryProductForm.stories.tsx58
-rw-r--r--packages/merchant-backoffice/src/components/product/InventoryProductForm.tsx95
-rw-r--r--packages/merchant-backoffice/src/components/product/NonInventoryProductForm.tsx146
-rw-r--r--packages/merchant-backoffice/src/components/product/ProductForm.tsx176
-rw-r--r--packages/merchant-backoffice/src/components/product/ProductList.tsx105
45 files changed, 0 insertions, 5022 deletions
diff --git a/packages/merchant-backoffice/src/components/exception/AsyncButton.tsx b/packages/merchant-backoffice/src/components/exception/AsyncButton.tsx
deleted file mode 100644
index 92bab4b..0000000
--- a/packages/merchant-backoffice/src/components/exception/AsyncButton.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { ComponentChildren, h } from "preact";
-import { LoadingModal } from "../modal";
-import { useAsync } from "../../hooks/async";
-import { Translate } from "../../i18n";
-
-type Props = {
- children: ComponentChildren,
- disabled: boolean;
- onClick?: () => Promise<void>;
- [rest:string]: any,
-};
-
-export function AsyncButton({ onClick, disabled, children, ...rest }: Props) {
- const { isSlow, isLoading, request, cancel } = useAsync(onClick);
-
- if (isSlow) {
- return <LoadingModal onCancel={cancel} />;
- }
- if (isLoading) {
- return <button class="button"><Translate>Loading...</Translate></button>;
- }
-
- return <span {...rest}>
- <button class="button is-success" onClick={request} disabled={disabled}>
- {children}
- </button>
- </span>;
-}
diff --git a/packages/merchant-backoffice/src/components/exception/QR.tsx b/packages/merchant-backoffice/src/components/exception/QR.tsx
deleted file mode 100644
index bcb9964..0000000
--- a/packages/merchant-backoffice/src/components/exception/QR.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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/>
- */
-
-import { h, VNode } from "preact";
-import { useEffect, useRef } from "preact/hooks";
-import qrcode from "qrcode-generator";
-
-export function QR({ text }: { text: string }): VNode {
- const divRef = useRef<HTMLDivElement>(null);
- useEffect(() => {
- const qr = qrcode(0, "L");
- qr.addData(text);
- qr.make();
- if (divRef.current) {
- divRef.current.innerHTML = qr.createSvgTag({
- scalable: true,
- });
- }
- });
-
- return (
- <div
- style={{
- width: "100%",
- display: "flex",
- flexDirection: "column",
- alignItems: "center",
- }}
- >
- <div
- style={{ width: "50%", minWidth: 200, maxWidth: 300 }}
- ref={divRef}
- />
- </div>
- );
-}
diff --git a/packages/merchant-backoffice/src/components/exception/loading.tsx b/packages/merchant-backoffice/src/components/exception/loading.tsx
deleted file mode 100644
index f2139a1..0000000
--- a/packages/merchant-backoffice/src/components/exception/loading.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-
-import { h, VNode } from "preact";
-
-export function Loading(): VNode {
- return <div class="columns is-centered is-vcentered" style={{ height: 'calc(100% - 3rem)', position: 'absolute', width: '100%' }}>
- <Spinner />
- </div>
-}
-
-export function Spinner(): VNode {
- return <div class="lds-ring"><div /><div /><div /><div /></div>
-} \ No newline at end of file
diff --git a/packages/merchant-backoffice/src/components/exception/login.tsx b/packages/merchant-backoffice/src/components/exception/login.tsx
deleted file mode 100644
index 498d994..0000000
--- a/packages/merchant-backoffice/src/components/exception/login.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { useBackendContext } from "../../context/backend";
-import { useInstanceContext } from "../../context/instance";
-import { Translate, useTranslator } from "../../i18n";
-import { Notification } from "../../utils/types";
-
-interface Props {
- withMessage?: Notification;
- onConfirm: (backend: string, token?: string) => void;
-}
-
-function getTokenValuePart(t?: string): string | undefined {
- if (!t) return t;
- const match = /secret-token:(.*)/.exec(t);
- if (!match || !match[1]) return undefined;
- return match[1];
-}
-
-function normalizeToken(r: string | undefined): string | undefined {
- return r ? `secret-token:${encodeURIComponent(r)}` : undefined;
-}
-
-export function LoginModal({ onConfirm, withMessage }: Props): VNode {
- const { url: backendUrl, token: baseToken } = useBackendContext();
- const { admin, token: instanceToken } = useInstanceContext();
- const currentToken = getTokenValuePart(
- !admin ? baseToken : instanceToken || ""
- );
- const [token, setToken] = useState(currentToken);
-
- const [url, setURL] = useState(backendUrl);
- const i18n = useTranslator();
-
- return (
- <div class="columns is-centered">
- <div class="column is-two-thirds ">
- <div class="modal-card" style={{ width: "100%", margin: 0 }}>
- <header
- class="modal-card-head"
- style={{ border: "1px solid", borderBottom: 0 }}
- >
- <p class="modal-card-title">{i18n`Login required`}</p>
- </header>
- <section
- class="modal-card-body"
- style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
- >
- <Translate>Please enter your access token.</Translate>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">URL</label>
- </div>
- <div class="field-body">
- <div class="field">
- <p class="control is-expanded">
- <input
- class="input"
- type="text"
- placeholder="set new url"
- name="id"
- value={url}
- onKeyPress={(e) =>
- e.keyCode === 13
- ? onConfirm(url, normalizeToken(token))
- : null
- }
- onInput={(e): void => setURL(e?.currentTarget.value)}
- />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- <Translate>Access Token</Translate>
- </label>
- </div>
- <div class="field-body">
- <div class="field">
- <p class="control is-expanded">
- <input
- class="input"
- type="password"
- placeholder={"set new access token"}
- name="token"
- onKeyPress={(e) =>
- e.keyCode === 13
- ? onConfirm(url, normalizeToken(token))
- : null
- }
- value={token}
- onInput={(e): void => setToken(e?.currentTarget.value)}
- />
- </p>
- </div>
- </div>
- </div>
- </section>
- <footer
- class="modal-card-foot "
- style={{
- justifyContent: "flex-end",
- border: "1px solid",
- borderTop: 0,
- }}
- >
- <button
- class="button is-info"
- onClick={(): void => {
- onConfirm(url, normalizeToken(token));
- }}
- >
- <Translate>Confirm</Translate>
- </button>
- </footer>
- </div>
- </div>
- </div>
- );
-}
diff --git a/packages/merchant-backoffice/src/components/form/FormProvider.tsx b/packages/merchant-backoffice/src/components/form/FormProvider.tsx
deleted file mode 100644
index aef410c..0000000
--- a/packages/merchant-backoffice/src/components/form/FormProvider.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { ComponentChildren, createContext, h, VNode } from "preact";
-import { useContext, useMemo } from "preact/hooks";
-
-type Updater<S> = (value: ((prevState: S) => S) ) => void;
-
-export interface Props<T> {
- object?: Partial<T>;
- errors?: FormErrors<T>;
- name?: string;
- valueHandler: Updater<Partial<T>> | null;
- children: ComponentChildren
-}
-
-const noUpdater: Updater<Partial<unknown>> = () => (s: unknown) => s
-
-export function FormProvider<T>({ object = {}, errors = {}, name = '', valueHandler, children }: Props<T>): VNode {
- const initialObject = useMemo(() => object, []);
- const value = useMemo<FormType<T>>(() => ({ errors, object, initialObject, valueHandler: valueHandler ? valueHandler : noUpdater, name, toStr: {}, fromStr: {} }), [errors, object, valueHandler]);
-
- return <FormContext.Provider value={value}>
- <form class="field" onSubmit={(e) => {
- e.preventDefault();
- // if (valueHandler) valueHandler(object);
- }}>
- {children}
- </form>
- </FormContext.Provider>;
-}
-
-export interface FormType<T> {
- object: Partial<T>;
- initialObject: Partial<T>;
- errors: FormErrors<T>;
- toStr: FormtoStr<T>;
- name: string;
- fromStr: FormfromStr<T>;
- valueHandler: Updater<Partial<T>>;
-}
-
-const FormContext = createContext<FormType<unknown>>(null!)
-
-export function useFormContext<T>() {
- return useContext<FormType<T>>(FormContext)
-}
-
-export type FormErrors<T> = {
- [P in keyof T]?: string | FormErrors<T[P]>
-}
-
-export type FormtoStr<T> = {
- [P in keyof T]?: ((f?: T[P]) => string)
-}
-
-export type FormfromStr<T> = {
- [P in keyof T]?: ((f: string) => T[P])
-}
-
-export type FormUpdater<T> = {
- [P in keyof T]?: (f: keyof T) => (v: T[P]) => void
-}
diff --git a/packages/merchant-backoffice/src/components/form/Input.tsx b/packages/merchant-backoffice/src/components/form/Input.tsx
deleted file mode 100644
index 9a9691e..0000000
--- a/packages/merchant-backoffice/src/components/form/Input.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { ComponentChildren, h, VNode } from "preact";
-import { useField, InputProps } from "./useField";
-
-interface Props<T> extends InputProps<T> {
- inputType?: 'text' | 'number' | 'multiline' | 'password';
- expand?: boolean;
- toStr?: (v?: any) => string;
- fromStr?: (s: string) => any;
- inputExtra?: any,
- side?: ComponentChildren;
- children?: ComponentChildren;
-}
-
-const defaultToString = (f?: any): string => f || ''
-const defaultFromString = (v: string): any => v as any
-
-const TextInput = ({ inputType, error, ...rest }: any) => inputType === 'multiline' ?
- <textarea {...rest} class={error ? "textarea is-danger" : "textarea"} rows="3" /> :
- <input {...rest} class={error ? "input is-danger" : "input"} type={inputType} />;
-
-export function Input<T>({ name, readonly, placeholder, tooltip, label, expand, help, children, inputType, inputExtra, side, fromStr = defaultFromString, toStr = defaultToString }: Props<keyof T>): VNode {
- const { error, value, onChange, required } = useField<T>(name);
- return <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- {label}
- {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class={expand ? "control is-expanded has-icons-right" : "control has-icons-right"}>
- <TextInput error={error} {...inputExtra}
- inputType={inputType}
- placeholder={placeholder} readonly={readonly}
- name={String(name)} value={toStr(value)}
- onChange={(e: h.JSX.TargetedEvent<HTMLInputElement>): void => onChange(fromStr(e.currentTarget.value))} />
- {help}
- {children}
- { required && <span class="icon has-text-danger is-right">
- <i class="mdi mdi-alert" />
- </span> }
- </p>
- {error && <p class="help is-danger">{error}</p>}
- </div>
- {side}
- </div>
- </div>;
-}
diff --git a/packages/merchant-backoffice/src/components/form/InputArray.tsx b/packages/merchant-backoffice/src/components/form/InputArray.tsx
deleted file mode 100644
index 984c6dc..0000000
--- a/packages/merchant-backoffice/src/components/form/InputArray.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { Translate, useTranslator } from "../../i18n";
-import { InputProps, useField } from "./useField";
-
-export interface Props<T> extends InputProps<T> {
- isValid?: (e: any) => boolean;
- addonBefore?: string;
- toStr?: (v?: any) => string;
- fromStr?: (s: string) => any;
-}
-
-const defaultToString = (f?: any): string => f || ''
-const defaultFromString = (v: string): any => v as any
-
-export function InputArray<T>({ name, readonly, placeholder, tooltip, label, help, addonBefore, isValid = () => true, fromStr = defaultFromString, toStr = defaultToString }: Props<keyof T>): VNode {
- const { error: formError, value, onChange, required } = useField<T>(name);
- const [localError, setLocalError] = useState<string | null>(null)
-
- const error = localError || formError
-
- const array: any[] = (value ? value! : []) as any;
- const [currentValue, setCurrentValue] = useState('');
- const i18n = useTranslator();
-
- return <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- {label}
- {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <div class="field has-addons">
- {addonBefore && <div class="control">
- <a class="button is-static">{addonBefore}</a>
- </div>}
- <p class="control is-expanded has-icons-right">
- <input class={error ? "input is-danger" : "input"} type="text"
- placeholder={placeholder} readonly={readonly} disabled={readonly}
- name={String(name)} value={currentValue}
- onChange={(e): void => setCurrentValue(e.currentTarget.value)} />
- {required && <span class="icon has-text-danger is-right">
- <i class="mdi mdi-alert" />
- </span>}
- </p>
- <p class="control">
- <button class="button is-info has-tooltip-left" disabled={!currentValue} onClick={(): void => {
- const v = fromStr(currentValue)
- if (!isValid(v)) {
- setLocalError(i18n`The value ${v} is invalid for a payment url`)
- return;
- }
- setLocalError(null)
- onChange([v, ...array] as any);
- setCurrentValue('');
- }} data-tooltip={i18n`add element to the list`}><Translate>add</Translate></button>
- </p>
- </div>
- {help}
- {error && <p class="help is-danger"> {error} </p>}
- {array.map((v, i) => <div key={i} class="tags has-addons mt-3 mb-0">
- <span class="tag is-medium is-info mb-0" style={{ maxWidth: '90%' }}>{v}</span>
- <a class="tag is-medium is-danger is-delete mb-0" onClick={() => {
- onChange(array.filter(f => f !== v) as any);
- setCurrentValue(toStr(v));
- }} />
- </div>
- )}
- </div>
-
- </div>
- </div>;
-}
diff --git a/packages/merchant-backoffice/src/components/form/InputBoolean.tsx b/packages/merchant-backoffice/src/components/form/InputBoolean.tsx
deleted file mode 100644
index 2771fe4..0000000
--- a/packages/merchant-backoffice/src/components/form/InputBoolean.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-import { h, VNode } from "preact";
-import { InputProps, useField } from "./useField";
-
-interface Props<T> extends InputProps<T> {
- name: T;
- readonly?: boolean;
- expand?: boolean;
- threeState?: boolean;
- toBoolean?: (v?: any) => boolean | undefined;
- fromBoolean?: (s: boolean | undefined) => any;
-}
-
-const defaultToBoolean = (f?: any): boolean | undefined => f || ''
-const defaultFromBoolean = (v: boolean | undefined): any => v as any
-
-
-export function InputBoolean<T>({ name, readonly, placeholder, tooltip, label, help, threeState, expand, fromBoolean = defaultFromBoolean, toBoolean = defaultToBoolean }: Props<keyof T>): VNode {
- const { error, value, onChange } = useField<T>(name);
-
- const onCheckboxClick = (): void => {
- const c = toBoolean(value)
- if (c === false && threeState) return onChange(undefined as any)
- return onChange(fromBoolean(!c))
- }
-
- return <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- {label}
- {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class={expand ? "control is-expanded" : "control"}>
- <label class="b-checkbox checkbox">
- <input type="checkbox" class={toBoolean(value) === undefined ? "is-indeterminate" : ""}
- checked={toBoolean(value)}
- placeholder={placeholder} readonly={readonly}
- name={String(name)} disabled={readonly}
- onChange={onCheckboxClick} />
- <span class="check" />
- </label>
- {help}
- </p>
- {error && <p class="help is-danger">{error}</p>}
- </div>
- </div>
- </div>;
-}
diff --git a/packages/merchant-backoffice/src/components/form/InputCurrency.tsx b/packages/merchant-backoffice/src/components/form/InputCurrency.tsx
deleted file mode 100644
index d3a46f4..0000000
--- a/packages/merchant-backoffice/src/components/form/InputCurrency.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { ComponentChildren, h } from "preact";
-import { useConfigContext } from "../../context/config";
-import { Amount } from "../../declaration";
-import { InputWithAddon } from "./InputWithAddon";
-import { InputProps } from "./useField";
-
-export interface Props<T> extends InputProps<T> {
- expand?: boolean;
- addonAfter?: ComponentChildren;
- children?: ComponentChildren;
- side?: ComponentChildren;
-}
-
-export function InputCurrency<T>({ name, readonly, label, placeholder, help, tooltip, expand, addonAfter, children, side }: Props<keyof T>) {
- const config = useConfigContext()
- return <InputWithAddon<T> name={name} readonly={readonly} addonBefore={config.currency}
- side={side}
- label={label} placeholder={placeholder} help={help} tooltip={tooltip}
- addonAfter={addonAfter}
- inputType='number' expand={expand}
- toStr={(v?: Amount) => v?.split(':')[1] || ''}
- fromStr={(v: string) => !v ? '' : `${config.currency}:${v}`}
- inputExtra={{ min: 0 }}
- children={children}
- />
-}
-
diff --git a/packages/merchant-backoffice/src/components/form/InputDate.tsx b/packages/merchant-backoffice/src/components/form/InputDate.tsx
deleted file mode 100644
index 7719952..0000000
--- a/packages/merchant-backoffice/src/components/form/InputDate.tsx
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { format } from "date-fns";
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { Translate, useTranslator } from "../../i18n";
-import { DatePicker } from "../picker/DatePicker";
-import { InputProps, useField } from "./useField";
-
-export interface Props<T> extends InputProps<T> {
- readonly?: boolean;
- expand?: boolean;
- //FIXME: create separated components InputDate and InputTimestamp
- withTimestampSupport?: boolean;
-}
-
-export function InputDate<T>({
- name,
- readonly,
- label,
- placeholder,
- help,
- tooltip,
- expand,
- withTimestampSupport,
-}: Props<keyof T>): VNode {
- const [opened, setOpened] = useState(false);
- const i18n = useTranslator();
-
- const { error, required, value, onChange } = useField<T>(name);
-
- let strValue = "";
- if (!value) {
- strValue = withTimestampSupport ? "unknown" : "";
- } else if (value instanceof Date) {
- strValue = format(value, "yyyy/MM/dd");
- } else if (value.t_s) {
- strValue =
- value.t_s === "never"
- ? withTimestampSupport
- ? "never"
- : ""
- : format(new Date(value.t_s * 1000), "yyyy/MM/dd");
- }
-
- return (
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- {label}
- {tooltip && (
- <span class="icon has-tooltip-right" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>
- )}
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <div class="field has-addons">
- <p
- class={
- expand
- ? "control is-expanded has-icons-right"
- : "control has-icons-right"
- }
- >
- <input
- class="input"
- type="text"
- readonly
- value={strValue}
- placeholder={placeholder}
- onClick={() => {
- if (!readonly) setOpened(true);
- }}
- />
- {required && (
- <span class="icon has-text-danger is-right">
- <i class="mdi mdi-alert" />
- </span>
- )}
- {help}
- </p>
- <div
- class="control"
- onClick={() => {
- if (!readonly) setOpened(true);
- }}
- >
- <a class="button is-static">
- <span class="icon">
- <i class="mdi mdi-calendar" />
- </span>
- </a>
- </div>
- </div>
- {error && <p class="help is-danger">{error}</p>}
- </div>
-
- {!readonly && (
- <span
- data-tooltip={
- withTimestampSupport
- ? i18n`change value to unknown date`
- : i18n`change value to empty`
- }
- >
- <button
- class="button is-info mr-3"
- onClick={() => onChange(undefined as any)}
- >
- <Translate>clear</Translate>
- </button>
- </span>
- )}
- {withTimestampSupport && (
- <span data-tooltip={i18n`change value to never`}>
- <button
- class="button is-info"
- onClick={() => onChange({ t_s: "never" } as any)}
- >
- <Translate>never</Translate>
- </button>
- </span>
- )}
- </div>
- <DatePicker
- opened={opened}
- closeFunction={() => setOpened(false)}
- dateReceiver={(d) => {
- if (withTimestampSupport) {
- onChange({ t_s: d.getTime() / 1000 } as any);
- } else {
- onChange(d as any);
- }
- }}
- />
- </div>
- );
-}
diff --git a/packages/merchant-backoffice/src/components/form/InputDuration.tsx b/packages/merchant-backoffice/src/components/form/InputDuration.tsx
deleted file mode 100644
index d5c208e..0000000
--- a/packages/merchant-backoffice/src/components/form/InputDuration.tsx
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { intervalToDuration, formatDuration } from "date-fns";
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { Translate, useTranslator } from "../../i18n";
-import { SimpleModal } from "../modal";
-import { DurationPicker } from "../picker/DurationPicker";
-import { InputProps, useField } from "./useField";
-
-export interface Props<T> extends InputProps<T> {
- expand?: boolean;
- readonly?: boolean;
- withForever?: boolean;
-}
-
-export function InputDuration<T>({
- name,
- expand,
- placeholder,
- tooltip,
- label,
- help,
- readonly,
- withForever,
-}: Props<keyof T>): VNode {
- const [opened, setOpened] = useState(false);
- const i18n = useTranslator();
-
- const { error, required, value, onChange } = useField<T>(name);
- let strValue = "";
- if (!value) {
- strValue = "";
- } else if (value.d_us === "forever") {
- strValue = i18n`forever`;
- } else {
- strValue = formatDuration(
- intervalToDuration({ start: 0, end: value.d_us / 1000 }),
- {
- locale: {
- formatDistance: (name, value) => {
- switch (name) {
- case "xMonths":
- return i18n`${value}M`;
- case "xYears":
- return i18n`${value}Y`;
- case "xDays":
- return i18n`${value}d`;
- case "xHours":
- return i18n`${value}h`;
- case "xMinutes":
- return i18n`${value}min`;
- case "xSeconds":
- return i18n`${value}sec`;
- }
- },
- localize: {
- day: () => "s",
- month: () => "m",
- ordinalNumber: () => "th",
- dayPeriod: () => "p",
- quarter: () => "w",
- era: () => "e",
- },
- },
- }
- );
- }
-
- return (
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- {label}
- {tooltip && (
- <span class="icon" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>
- )}
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <div class="field has-addons">
- <p class={expand ? "control is-expanded " : "control "}>
- <input
- class="input"
- type="text"
- readonly
- value={strValue}
- placeholder={placeholder}
- onClick={() => {
- if (!readonly) setOpened(true);
- }}
- />
- {required && (
- <span class="icon has-text-danger is-right">
- <i class="mdi mdi-alert" />
- </span>
- )}
- {help}
- </p>
- <div
- class="control"
- onClick={() => {
- if (!readonly) setOpened(true);
- }}
- >
- <a class="button is-static">
- <span class="icon">
- <i class="mdi mdi-clock" />
- </span>
- </a>
- </div>
- </div>
- {error && <p class="help is-danger">{error}</p>}
- </div>
- {withForever && (
- <span data-tooltip={i18n`change value to never`}>
- <button
- class="button is-info mr-3"
- onClick={() => onChange({ d_us: "forever" } as any)}
- >
- <Translate>forever</Translate>
- </button>
- </span>
- )}
- {!readonly && (
- <span data-tooltip={i18n`change value to empty`}>
- <button
- class="button is-info "
- onClick={() => onChange(undefined as any)}
- >
- <Translate>clear</Translate>
- </button>
- </span>
- )}
- </div>
- {opened && (
- <SimpleModal onCancel={() => setOpened(false)}>
- <DurationPicker
- days
- hours
- minutes
- value={!value || value.d_us === "forever" ? 0 : value.d_us}
- onChange={(v) => {
- onChange({ d_us: v } as any);
- }}
- />
- </SimpleModal>
- )}
- </div>
- );
-}
diff --git a/packages/merchant-backoffice/src/components/form/InputGroup.tsx b/packages/merchant-backoffice/src/components/form/InputGroup.tsx
deleted file mode 100644
index 8af9c7d..0000000
--- a/packages/merchant-backoffice/src/components/form/InputGroup.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { ComponentChildren, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { useGroupField } from "./useGroupField";
-
-export interface Props<T> {
- name: T;
- children: ComponentChildren;
- label: ComponentChildren;
- tooltip?: ComponentChildren;
- alternative?: ComponentChildren;
- fixed?: boolean;
- initialActive?: boolean;
-}
-
-export function InputGroup<T>({ name, label, children, tooltip, alternative, fixed, initialActive }: Props<keyof T>): VNode {
- const [active, setActive] = useState(initialActive || fixed);
- const group = useGroupField<T>(name);
-
- return <div class="card">
- <header class="card-header">
- <p class="card-header-title">
- {label}
- {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- {group?.hasError && <span class="icon has-text-danger" data-tooltip={tooltip}>
- <i class="mdi mdi-alert" />
- </span>}
- </p>
- { !fixed && <button class="card-header-icon" aria-label="more options" onClick={(): void => setActive(!active)}>
- <span class="icon">
- {active ?
- <i class="mdi mdi-arrow-up" /> :
- <i class="mdi mdi-arrow-down" />}
- </span>
- </button> }
- </header>
- {active ? <div class="card-content">
- {children}
- </div> : (
- alternative ? <div class="card-content">
- {alternative}
- </div> : undefined
- )}
- </div>;
-}
diff --git a/packages/merchant-backoffice/src/components/form/InputImage.tsx b/packages/merchant-backoffice/src/components/form/InputImage.tsx
deleted file mode 100644
index 6cc9b9d..0000000
--- a/packages/merchant-backoffice/src/components/form/InputImage.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { ComponentChildren, h, VNode } from "preact";
-import { useRef, useState } from "preact/hooks";
-import emptyImage from "../../assets/empty.png";
-import { Translate } from "../../i18n";
-import { MAX_IMAGE_SIZE as MAX_IMAGE_UPLOAD_SIZE } from "../../utils/constants";
-import { InputProps, useField } from "./useField";
-
-export interface Props<T> extends InputProps<T> {
- expand?: boolean;
- addonAfter?: ComponentChildren;
- children?: ComponentChildren;
-}
-
-export function InputImage<T>({ name, readonly, placeholder, tooltip, label, help, children, expand }: Props<keyof T>): VNode {
- const { error, value, onChange } = useField<T>(name);
-
- const image = useRef<HTMLInputElement>(null)
-
- const [sizeError, setSizeError] = useState(false)
-
- return <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- {label}
- {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class={expand ? "control is-expanded" : "control"}>
- {value &&
- <img src={value} style={{ width: 200, height: 200 }} onClick={() => image.current?.click()} />
- }
- <input
- ref={image} style={{ display: 'none' }}
- type="file" name={String(name)}
- placeholder={placeholder} readonly={readonly}
- onChange={e => {
- const f: FileList | null = e.currentTarget.files
- if (!f || f.length != 1) {
- return onChange(undefined!)
- }
- if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) {
- setSizeError(true)
- return onChange(undefined!)
- }
- setSizeError(false)
- return f[0].arrayBuffer().then(b => {
- const b64 = btoa(
- new Uint8Array(b)
- .reduce((data, byte) => data + String.fromCharCode(byte), '')
- )
- return onChange(`data:${f[0].type};base64,${b64}` as any)
- })
- }} />
- {help}
- {children}
- </p>
- {error && <p class="help is-danger">{error}</p>}
- {sizeError && <p class="help is-danger">
- <Translate>Image should be smaller than 1 MB</Translate>
- </p>}
- {!value &&
- <button class="button" onClick={() => image.current?.click()} ><Translate>Add</Translate></button>
- }
- {value &&
- <button class="button" onClick={() => onChange(undefined!)} ><Translate>Remove</Translate></button>
- }
- </div>
- </div>
- </div>
-}
-
diff --git a/packages/merchant-backoffice/src/components/form/InputLocation.tsx b/packages/merchant-backoffice/src/components/form/InputLocation.tsx
deleted file mode 100644
index 12755f4..0000000
--- a/packages/merchant-backoffice/src/components/form/InputLocation.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { Fragment, h } from "preact";
-import { useTranslator } from "../../i18n";
-import { Input } from "./Input";
-
-export function InputLocation({name}:{name:string}) {
- const i18n = useTranslator()
- return <>
- <Input name={`${name}.country`} label={i18n`Country`} />
- <Input name={`${name}.address_lines`} inputType="multiline"
- label={i18n`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}.country_subdivision`} label={i18n`Country subdivision`} />
- </>
-} \ No newline at end of file
diff --git a/packages/merchant-backoffice/src/components/form/InputNumber.tsx b/packages/merchant-backoffice/src/components/form/InputNumber.tsx
deleted file mode 100644
index 046cda5..0000000
--- a/packages/merchant-backoffice/src/components/form/InputNumber.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { ComponentChildren, h } from "preact";
-import { InputWithAddon } from "./InputWithAddon";
-import { InputProps } from "./useField";
-
-export interface Props<T> extends InputProps<T> {
- readonly?: boolean;
- expand?: boolean;
- side?: ComponentChildren;
- children?: ComponentChildren;
-}
-
-export function InputNumber<T>({ name, readonly, placeholder, tooltip, label, help, expand, children, side }: Props<keyof T>) {
- return <InputWithAddon<T> name={name} readonly={readonly}
- fromStr={(v) => !v ? undefined : parseInt(v, 10) } toStr={(v) => `${v}`}
- inputType='number' expand={expand}
- label={label} placeholder={placeholder} help={help} tooltip={tooltip}
- inputExtra={{ min: 0 }}
- children={children}
- side={side}
- />
-}
-
diff --git a/packages/merchant-backoffice/src/components/form/InputPayto.tsx b/packages/merchant-backoffice/src/components/form/InputPayto.tsx
deleted file mode 100644
index 4425231..0000000
--- a/packages/merchant-backoffice/src/components/form/InputPayto.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-import { h, VNode } from "preact";
-import { InputArray } from "./InputArray";
-import { PAYTO_REGEX } from "../../utils/constants";
-import { InputProps } from "./useField";
-
-export type Props<T> = InputProps<T>;
-
-const PAYTO_START_REGEX = /^payto:\/\//
-
-export function InputPayto<T>({ name, readonly, placeholder, tooltip, label, help }: Props<keyof T>): VNode {
- return <InputArray<T> name={name} readonly={readonly}
- addonBefore="payto://"
- label={label} placeholder={placeholder} help={help} tooltip={tooltip}
- isValid={(v) => v && PAYTO_REGEX.test(v) }
- toStr={(v?: string) => !v ? '': v.replace(PAYTO_START_REGEX, '')}
- fromStr={(v: string) => `payto://${v}` }
- />
-}
-
diff --git a/packages/merchant-backoffice/src/components/form/InputPaytoForm.tsx b/packages/merchant-backoffice/src/components/form/InputPaytoForm.tsx
deleted file mode 100644
index 9cfef07..0000000
--- a/packages/merchant-backoffice/src/components/form/InputPaytoForm.tsx
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-import { h, VNode, Fragment } from "preact";
-import { useCallback, useState } from "preact/hooks";
-import { Translate, Translator, useTranslator } from "../../i18n";
-import { COUNTRY_TABLE } from "../../utils/constants";
-import { FormErrors, FormProvider } from "./FormProvider";
-import { Input } from "./Input";
-import { InputGroup } from "./InputGroup";
-import { InputSelector } from "./InputSelector";
-import { InputProps, useField } from "./useField";
-
-export interface Props<T> extends InputProps<T> {
- isValid?: (e: any) => boolean;
-}
-
-// https://datatracker.ietf.org/doc/html/rfc8905
-type Entity = {
- // iban, bitcoin, x-taler-bank. it defined the format
- target: string;
- // path1 if the first field to be used
- path1: string;
- // path2 if the second field to be used, optional
- path2?: string;
- // options of the payto uri
- options: {
- "receiver-name"?: string;
- sender?: string;
- message?: string;
- amount?: string;
- instruction?: string;
- [name: string]: string | undefined;
- };
-};
-
-function isEthereumAddress(address: string) {
- if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) {
- return false;
- } else if (
- /^(0x|0X)?[0-9a-f]{40}$/.test(address) ||
- /^(0x|0X)?[0-9A-F]{40}$/.test(address)
- ) {
- return true;
- }
- return checkAddressChecksum(address);
-}
-
-function checkAddressChecksum(address: string) {
- //TODO implement ethereum checksum
- return true;
-}
-
-function validateBitcoin(addr: string, i18n: Translator): 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.`;
-}
-
-function validateEthereum(addr: string, i18n: Translator): 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.`;
-}
-
-/**
- * An IBAN is validated by converting it into an integer and performing a
- * basic mod-97 operation (as described in ISO 7064) on it.
- * If the IBAN is valid, the remainder equals 1.
- *
- * The algorithm of IBAN validation is as follows:
- * 1.- Check that the total IBAN length is correct as per the country. If not, the IBAN is invalid
- * 2.- Move the four initial characters to the end of the string
- * 3.- Replace each letter in the string with two digits, thereby expanding the string, where A = 10, B = 11, ..., Z = 35
- * 4.- Interpret the string as a decimal integer and compute the remainder of that number on division by 97
- *
- * 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 {
- // Check total length
- if (iban.length < 4)
- return i18n`IBAN numbers usually have more that 4 digits`;
- if (iban.length > 34)
- return i18n`IBAN numbers usually have less that 34 digits`;
-
- const A_code = "A".charCodeAt(0);
- const Z_code = "Z".charCodeAt(0);
- const IBAN = iban.toUpperCase();
- // check supported country
- const code = IBAN.substr(0, 2);
- const found = code in COUNTRY_TABLE;
- if (!found) return i18n`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);
- const step3 = Array.from(step2)
- .map((letter) => {
- const code = letter.charCodeAt(0);
- if (code < A_code || code > Z_code) return letter;
- return `${letter.charCodeAt(0) - "A".charCodeAt(0) + 10}`;
- })
- .join("");
-
- function calculate_iban_checksum(str: string): number {
- const numberStr = str.substr(0, 5);
- const rest = str.substr(5);
- const number = parseInt(numberStr, 10);
- const result = number % 97;
- if (rest.length > 0) {
- return calculate_iban_checksum(`${result}${rest}`);
- }
- return result;
- }
-
- const checksum = calculate_iban_checksum(step3);
- if (checksum !== 1) return i18n`IBAN number is not valid, checksum is wrong`;
- return undefined;
-}
-
-// const targets = ['ach', 'bic', 'iban', 'upi', 'bitcoin', 'ilp', 'void', 'x-taler-bank']
-const targets = [
- "Choose one...",
- "iban",
- "x-taler-bank",
- "bitcoin",
- "ethereum",
-];
-const noTargetValue = targets[0];
-const defaultTarget = { target: noTargetValue, options: {} };
-
-function undefinedIfEmpty<T>(obj: T): T | undefined {
- return Object.keys(obj).some((k) => (obj as any)[k] !== undefined)
- ? obj
- : undefined;
-}
-
-export function InputPaytoForm<T>({
- name,
- readonly,
- label,
- tooltip,
-}: Props<keyof T>): VNode {
- const { value: paytos, onChange } = useField<T>(name);
-
- const [value, valueHandler] = useState<Partial<Entity>>(defaultTarget);
-
- let payToPath;
- if (value.target === "iban" && value.path1) {
- payToPath = `/${value.path1.toUpperCase()}`;
- } else if (value.path1) {
- if (value.path2) {
- payToPath = `/${value.path1}/${value.path2}`;
- } else {
- payToPath = `/${value.path1}`;
- }
- }
- const i18n = useTranslator();
-
- const ops = value.options!;
- const url = tryUrl(`payto://${value.target}${payToPath}`);
- if (url) {
- Object.keys(ops).forEach((opt_key) => {
- const opt_value = ops[opt_key];
- if (opt_value) url.searchParams.set(opt_key, opt_value);
- });
- }
- const paytoURL = !url ? "" : url.toString();
-
- const errors: FormErrors<Entity> = {
- target: value.target === noTargetValue ? i18n`required` : undefined,
- path1: !value.path1
- ? i18n`required`
- : value.target === "iban"
- ? validateIBAN(value.path1, i18n)
- : value.target === "bitcoin"
- ? validateBitcoin(value.path1, i18n)
- : value.target === "ethereum"
- ? validateEthereum(value.path1, i18n)
- : undefined,
- path2:
- value.target === "x-taler-bank"
- ? !value.path2
- ? i18n`required`
- : undefined
- : undefined,
- options: undefinedIfEmpty({
- "receiver-name": !value.options?.["receiver-name"]
- ? i18n`required`
- : undefined,
- }),
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined
- );
-
- const submit = useCallback((): void => {
- const alreadyExists =
- paytos.findIndex((x: string) => x === paytoURL) !== -1;
- if (!alreadyExists) {
- onChange([paytoURL, ...paytos] as any);
- }
- valueHandler(defaultTarget);
- }, [value]);
-
- //FIXME: translating plural singular
- return (
- <InputGroup name="payto" label={label} fixed tooltip={tooltip}>
- <FormProvider<Entity>
- name="tax"
- errors={errors}
- object={value}
- valueHandler={valueHandler}
- >
- <InputSelector<Entity>
- name="target"
- label={i18n`Target type`}
- tooltip={i18n`Method to use for wire transfer`}
- values={targets}
- toStr={(v) => (v === noTargetValue ? i18n`Choose one...` : v)}
- />
-
- {value.target === "ach" && (
- <Fragment>
- <Input<Entity>
- name="path1"
- label={i18n`Routing`}
- tooltip={i18n`Routing number.`}
- />
- <Input<Entity>
- name="path2"
- label={i18n`Account`}
- tooltip={i18n`Account number.`}
- />
- </Fragment>
- )}
- {value.target === "bic" && (
- <Fragment>
- <Input<Entity>
- name="path1"
- label={i18n`Code`}
- tooltip={i18n`Business Identifier Code.`}
- />
- </Fragment>
- )}
- {value.target === "iban" && (
- <Fragment>
- <Input<Entity>
- name="path1"
- label={i18n`Account`}
- tooltip={i18n`Bank Account Number.`}
- inputExtra={{ style: { textTransform: "uppercase" } }}
- />
- </Fragment>
- )}
- {value.target === "upi" && (
- <Fragment>
- <Input<Entity>
- name="path1"
- label={i18n`Account`}
- tooltip={i18n`Unified Payment Interface.`}
- />
- </Fragment>
- )}
- {value.target === "bitcoin" && (
- <Fragment>
- <Input<Entity>
- name="path1"
- label={i18n`Address`}
- tooltip={i18n`Bitcoin protocol.`}
- />
- </Fragment>
- )}
- {value.target === "ethereum" && (
- <Fragment>
- <Input<Entity>
- name="path1"
- label={i18n`Address`}
- tooltip={i18n`Ethereum protocol.`}
- />
- </Fragment>
- )}
- {value.target === "ilp" && (
- <Fragment>
- <Input<Entity>
- name="path1"
- label={i18n`Address`}
- tooltip={i18n`Interledger protocol.`}
- />
- </Fragment>
- )}
- {value.target === "void" && <Fragment />}
- {value.target === "x-taler-bank" && (
- <Fragment>
- <Input<Entity>
- name="path1"
- label={i18n`Host`}
- tooltip={i18n`Bank host.`}
- />
- <Input<Entity>
- name="path2"
- label={i18n`Account`}
- tooltip={i18n`Bank account.`}
- />
- </Fragment>
- )}
-
- {value.target !== noTargetValue && (
- <Input
- name="options.receiver-name"
- label={i18n`Name`}
- tooltip={i18n`Bank account owner's name.`}
- />
- )}
-
- <div class="field is-horizontal">
- <div class="field-label is-normal" />
- <div class="field-body" style={{ display: "block" }}>
- {paytos.map((v: any, i: number) => (
- <div
- key={i}
- class="tags has-addons mt-3 mb-0 mr-3"
- style={{ flexWrap: "nowrap" }}
- >
- <span
- class="tag is-medium is-info mb-0"
- style={{ maxWidth: "90%" }}
- >
- {v}
- </span>
- <a
- class="tag is-medium is-danger is-delete mb-0"
- onClick={() => {
- onChange(paytos.filter((f: any) => f !== v) as any);
- }}
- />
- </div>
- ))}
- {!paytos.length && i18n`No accounts yet.`}
- </div>
- </div>
-
- {value.target !== noTargetValue && (
- <div class="buttons is-right mt-5">
- <button
- class="button is-info"
- data-tooltip={i18n`add tax to the tax list`}
- disabled={hasErrors}
- onClick={submit}
- >
- <Translate>Add</Translate>
- </button>
- </div>
- )}
- </FormProvider>
- </InputGroup>
- );
-}
-
-function tryUrl(s: string): URL | undefined {
- try {
- return new URL(s);
- } catch (e) {
- return undefined;
- }
-}
diff --git a/packages/merchant-backoffice/src/components/form/InputSearchProduct.tsx b/packages/merchant-backoffice/src/components/form/InputSearchProduct.tsx
deleted file mode 100644
index 51f84fd..0000000
--- a/packages/merchant-backoffice/src/components/form/InputSearchProduct.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import emptyImage from "../../assets/empty.png";
-import { MerchantBackend, WithId } from "../../declaration";
-import { useInstanceProducts } from "../../hooks/product";
-import { Translate, useTranslator } from "../../i18n";
-import { FormErrors, FormProvider } from "./FormProvider";
-import { InputWithAddon } from "./InputWithAddon";
-
-type Entity = MerchantBackend.Products.ProductDetail & WithId
-
-export interface Props {
- selected?: Entity;
- onChange: (p?: Entity) => void;
- products: (MerchantBackend.Products.ProductDetail & WithId)[],
-}
-
-interface ProductSearch {
- name: string;
-}
-
-export function InputSearchProduct({ selected, onChange, products }: Props): VNode {
- const [prodForm, setProdName] = useState<Partial<ProductSearch>>({ name: '' })
-
- const errors: FormErrors<ProductSearch> = {
- name: undefined
- }
- const i18n = useTranslator()
-
-
- if (selected) {
- return <article class="media">
- <figure class="media-left">
- <p class="image is-128x128">
- <img src={selected.image ? selected.image : emptyImage} />
- </p>
- </figure>
- <div class="media-content">
- <div class="content">
- <p class="media-meta"><Translate>Product id</Translate>: <b>{selected.id}</b></p>
- <p><Translate>Description</Translate>: {selected.description}</p>
- <div class="buttons is-right mt-5">
- <button class="button is-info" onClick={() => onChange(undefined)}>clear</button>
- </div>
- </div>
- </div>
- </article>
- }
-
- return <FormProvider<ProductSearch> errors={errors} object={prodForm} valueHandler={setProdName} >
-
- <InputWithAddon<ProductSearch>
- name="name"
- label={i18n`Product`}
- tooltip={i18n`search products by it's description or id`}
- addonAfter={<span class="icon" ><i class="mdi mdi-magnify" /></span>}
- >
- <div>
- <ProductList
- name={prodForm.name}
- list={products}
- onSelect={(p) => {
- setProdName({ name: '' })
- onChange(p)
- }}
- />
- </div>
- </InputWithAddon>
-
- </FormProvider>
-
-}
-
-interface ProductListProps {
- name?: string;
- onSelect: (p: MerchantBackend.Products.ProductDetail & WithId) => void;
- list: (MerchantBackend.Products.ProductDetail & WithId)[]
-}
-
-function ProductList({ name, onSelect, list }: ProductListProps) {
- if (!name) {
- /* FIXME
- this BR is added to occupy the space that will be added when the
- dropdown appears
- */
- return <div ><br /></div>
- }
- const filtered = list.filter(p => p.id.includes(name) || p.description.includes(name))
-
- return <div class="dropdown is-active">
- <div class="dropdown-menu" id="dropdown-menu" role="menu" style={{ minWidth: '20rem' }}>
- <div class="dropdown-content">
- {!filtered.length ?
- <div class="dropdown-item" >
- <Translate>no products found with that description</Translate>
- </div> :
- filtered.map(p => (
- <div key={p.id} class="dropdown-item" onClick={() => onSelect(p)} style={{ cursor: 'pointer' }}>
- <article class="media">
- <div class="media-left">
- <div class="image" style={{ minWidth: 64 }}><img src={p.image ? p.image : emptyImage} style={{ width: 64, height: 64 }} /></div>
- </div>
- <div class="media-content">
- <div class="content">
- <p>
- <strong>{p.id}</strong> <small>{p.price}</small>
- <br />
- {p.description}
- </p>
- </div>
- </div>
- </article>
- </div>
- ))
- }
- </div>
- </div>
- </div>
-}
diff --git a/packages/merchant-backoffice/src/components/form/InputSecured.stories.tsx b/packages/merchant-backoffice/src/components/form/InputSecured.stories.tsx
deleted file mode 100644
index 1990eee..0000000
--- a/packages/merchant-backoffice/src/components/form/InputSecured.stories.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-
-import { h, VNode } from 'preact';
-import { useState } from 'preact/hooks';
-import { FormProvider } from "./FormProvider";
-import { InputSecured } from './InputSecured';
-
-export default {
- title: 'Components/Form/InputSecured',
- component: InputSecured,
-};
-
-type T = { auth_token: string | null }
-
-export const InitialValueEmpty = (): VNode => {
- const [state, setState] = useState<Partial<T>>({ auth_token: '' })
- return <FormProvider<T> object={state} errors={{}} valueHandler={setState}>
- Initial value: ''
- <InputSecured<T> name="auth_token" label="Access token" />
- </FormProvider>
-}
-
-export const InitialValueToken = (): VNode => {
- const [state, setState] = useState<Partial<T>>({ auth_token: 'token' })
- return <FormProvider<T> object={state} errors={{}} valueHandler={setState}>
- <InputSecured<T> name="auth_token" label="Access token" />
- </FormProvider>
-}
-
-export const InitialValueNull = (): VNode => {
- const [state, setState] = useState<Partial<T>>({ auth_token: null })
- return <FormProvider<T> object={state} errors={{}} valueHandler={setState}>
- Initial value: ''
- <InputSecured<T> name="auth_token" label="Access token" />
- </FormProvider>
-}
diff --git a/packages/merchant-backoffice/src/components/form/InputSecured.tsx b/packages/merchant-backoffice/src/components/form/InputSecured.tsx
deleted file mode 100644
index c9b0f43..0000000
--- a/packages/merchant-backoffice/src/components/form/InputSecured.tsx
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { Translate, useTranslator } from "../../i18n";
-import { InputProps, useField } from "./useField";
-
-export type Props<T> = InputProps<T>;
-
-const TokenStatus = ({ prev, post }: any) => {
- if ((prev === undefined || prev === null) && (post === undefined || post === null))
- return null
- return (prev === post) ? null : (
- post === null ?
- <span class="tag is-danger is-align-self-center ml-2"><Translate>Deleting</Translate></span> :
- <span class="tag is-warning is-align-self-center ml-2"><Translate>Changing</Translate></span>
- )
-}
-
-export function InputSecured<T>({ name, readonly, placeholder, tooltip, label, help }: Props<keyof T>): VNode {
- const { error, value, initial, onChange, toStr, fromStr } = useField<T>(name);
-
- const [active, setActive] = useState(false);
- const [newValue, setNuewValue] = useState("")
-
- const i18n = useTranslator()
-
- return <Fragment>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- {label}
- {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- {!active ?
- <Fragment>
- <div class="field has-addons">
- <button class="button"
- onClick={(): void => { setActive(!active); }} >
- <div class="icon is-left"><i class="mdi mdi-lock-reset" /></div>
- <span><Translate>Manage access token</Translate></span>
- </button>
- <TokenStatus prev={initial} post={value} />
- </div>
- </Fragment> :
- <Fragment>
- <div class="field has-addons">
- <div class="control">
- <a class="button is-static">secret-token:</a>
- </div>
- <div class="control is-expanded">
- <input class="input" type="text"
- placeholder={placeholder} readonly={readonly || !active}
- disabled={readonly || !active}
- name={String(name)} value={newValue}
- onInput={(e): void => {
- setNuewValue(e.currentTarget.value)
- }} />
- {help}
- </div>
- <div class="control">
- <button class="button is-info" disabled={fromStr(newValue) === value} onClick={(): void => { onChange(fromStr(newValue)); setActive(!active); setNuewValue(""); }} >
- <div class="icon is-left"><i class="mdi mdi-lock-outline" /></div>
- <span><Translate>Update</Translate></span>
- </button>
- </div>
- </div>
- </Fragment>
- }
- {error ? <p class="help is-danger">{error}</p> : null}
- </div>
- </div>
- {active &&
- <div class="field is-horizontal">
- <div class="field-body is-flex-grow-3">
- <div class="level" style={{ width: '100%' }}>
- <div class="level-right is-flex-grow-1">
- <div class="level-item">
- <button class="button is-danger" disabled={null === value || undefined === value} onClick={(): void => { onChange(null!); setActive(!active); setNuewValue(""); }} >
- <div class="icon is-left"><i class="mdi mdi-lock-open-variant" /></div>
- <span><Translate>Remove</Translate></span>
- </button>
- </div>
- <div class="level-item">
- <button class="button " onClick={(): void => { onChange(initial!); setActive(!active); setNuewValue(""); }} >
- <div class="icon is-left"><i class="mdi mdi-lock-open-variant" /></div>
- <span><Translate>Cancel</Translate></span>
- </button>
- </div>
- </div>
-
- </div>
- </div>
- </div>
- }
- </Fragment >;
-}
diff --git a/packages/merchant-backoffice/src/components/form/InputSelector.tsx b/packages/merchant-backoffice/src/components/form/InputSelector.tsx
deleted file mode 100644
index 86f4de7..0000000
--- a/packages/merchant-backoffice/src/components/form/InputSelector.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-import { h, VNode } from "preact";
-import { InputProps, useField } from "./useField";
-
-interface Props<T> extends InputProps<T> {
- readonly?: boolean;
- expand?: boolean;
- values: string[];
- toStr?: (v?: any) => string;
- fromStr?: (s: string) => any;
-}
-
-const defaultToString = (f?: any): string => f || "";
-const defaultFromString = (v: string): any => v as any;
-
-export function InputSelector<T>({
- name,
- readonly,
- expand,
- placeholder,
- tooltip,
- label,
- help,
- values,
- toStr = defaultToString,
-}: Props<keyof T>): VNode {
- const { error, value, onChange } = useField<T>(name);
-
- return (
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- {label}
- {tooltip && (
- <span class="icon has-tooltip-right" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>
- )}
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class={expand ? "control is-expanded select" : "control select"}>
- <select
- class={error ? "select is-danger" : "select"}
- name={String(name)}
- disabled={readonly}
- readonly={readonly}
- onChange={(e) => {
- onChange(e.currentTarget.value as any);
- }}
- >
- {placeholder && <option>{placeholder}</option>}
- {values.map((v, i) => (
- <option key={i} value={v} selected={value === v}>
- {toStr(v)}
- </option>
- ))}
- </select>
- {help}
- </p>
- {error && <p class="help is-danger">{error}</p>}
- </div>
- </div>
- </div>
- );
-}
diff --git a/packages/merchant-backoffice/src/components/form/InputStock.stories.tsx b/packages/merchant-backoffice/src/components/form/InputStock.stories.tsx
deleted file mode 100644
index 63c7e41..0000000
--- a/packages/merchant-backoffice/src/components/form/InputStock.stories.tsx
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { addDays } from "date-fns";
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { FormProvider } from "./FormProvider";
-import { InputStock, Stock } from "./InputStock";
-
-export default {
- title: "Components/Form/InputStock",
- component: InputStock,
-};
-
-type T = { stock?: Stock };
-
-export const CreateStockEmpty = () => {
- const [state, setState] = useState<Partial<T>>({});
- return (
- <FormProvider<T>
- name="product"
- object={state}
- errors={{}}
- valueHandler={setState}
- >
- <InputStock<T> name="stock" label="Stock" />
- <div>
- <pre>{JSON.stringify(state, undefined, 2)}</pre>
- </div>
- </FormProvider>
- );
-};
-
-export const CreateStockUnknownRestock = () => {
- const [state, setState] = useState<Partial<T>>({
- stock: {
- current: 10,
- lost: 0,
- sold: 0,
- },
- });
- return (
- <FormProvider<T>
- name="product"
- object={state}
- errors={{}}
- valueHandler={setState}
- >
- <InputStock<T> name="stock" label="Stock" />
- <div>
- <pre>{JSON.stringify(state, undefined, 2)}</pre>
- </div>
- </FormProvider>
- );
-};
-
-export const CreateStockNoRestock = () => {
- const [state, setState] = useState<Partial<T>>({
- stock: {
- current: 10,
- lost: 0,
- sold: 0,
- nextRestock: { t_s: "never" },
- },
- });
- return (
- <FormProvider<T>
- name="product"
- object={state}
- errors={{}}
- valueHandler={setState}
- >
- <InputStock<T> name="stock" label="Stock" />
- <div>
- <pre>{JSON.stringify(state, undefined, 2)}</pre>
- </div>
- </FormProvider>
- );
-};
-
-export const CreateStockWithRestock = () => {
- const [state, setState] = useState<Partial<T>>({
- stock: {
- current: 15,
- lost: 0,
- sold: 0,
- nextRestock: { t_s: addDays(new Date(), 1).getTime() / 1000 },
- },
- });
- return (
- <FormProvider<T>
- name="product"
- object={state}
- errors={{}}
- valueHandler={setState}
- >
- <InputStock<T> name="stock" label="Stock" />
- <div>
- <pre>{JSON.stringify(state, undefined, 2)}</pre>
- </div>
- </FormProvider>
- );
-};
-
-export const UpdatingProductWithManagedStock = () => {
- const [state, setState] = useState<Partial<T>>({
- stock: {
- current: 100,
- lost: 0,
- sold: 0,
- nextRestock: { t_s: addDays(new Date(), 1).getTime() / 1000 },
- },
- });
- return (
- <FormProvider<T>
- name="product"
- object={state}
- errors={{}}
- valueHandler={setState}
- >
- <InputStock<T> name="stock" label="Stock" alreadyExist />
- <div>
- <pre>{JSON.stringify(state, undefined, 2)}</pre>
- </div>
- </FormProvider>
- );
-};
-
-export const UpdatingProductWithInfiniteStock = () => {
- const [state, setState] = useState<Partial<T>>({});
- return (
- <FormProvider<T>
- name="product"
- object={state}
- errors={{}}
- valueHandler={setState}
- >
- <InputStock<T> name="stock" label="Stock" alreadyExist />
- <div>
- <pre>{JSON.stringify(state, undefined, 2)}</pre>
- </div>
- </FormProvider>
- );
-};
diff --git a/packages/merchant-backoffice/src/components/form/InputStock.tsx b/packages/merchant-backoffice/src/components/form/InputStock.tsx
deleted file mode 100644
index 158f441..0000000
--- a/packages/merchant-backoffice/src/components/form/InputStock.tsx
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { Fragment, h } from "preact";
-import { MerchantBackend, Timestamp } from "../../declaration";
-import { InputProps, useField } from "./useField";
-import { FormProvider, FormErrors } from "./FormProvider";
-import { useLayoutEffect, useState } from "preact/hooks";
-import { Input } from "./Input";
-import { InputGroup } from "./InputGroup";
-import { InputNumber } from "./InputNumber";
-import { InputDate } from "./InputDate";
-import { Translate, useTranslator } from "../../i18n";
-import { InputLocation } from "./InputLocation";
-
-export interface Props<T> extends InputProps<T> {
- alreadyExist?: boolean;
-}
-
-
-type Entity = Stock
-
-export interface Stock {
- current: number;
- lost: number;
- sold: number;
- address?: MerchantBackend.Location;
- nextRestock?: Timestamp;
-}
-
-interface StockDelta {
- incoming: number;
- lost: number;
-}
-
-
-export function InputStock<T>({ name, tooltip, label, alreadyExist }: Props<keyof T>) {
- const { error, value, onChange } = useField<T>(name);
-
- const [errors, setErrors] = useState<FormErrors<Entity>>({})
-
- const [formValue, valueHandler] = useState<Partial<Entity>>(value)
- const [addedStock, setAddedStock] = useState<StockDelta>({ incoming: 0, lost: 0 })
- const i18n = useTranslator()
-
-
- useLayoutEffect(() => {
- if (!formValue) {
- onChange(undefined as any)
- } else {
- onChange({
- ...formValue,
- current: (formValue?.current || 0) + addedStock.incoming,
- lost: (formValue?.lost || 0) + addedStock.lost
- } as any)
- }
- }, [formValue, addedStock])
-
- if (!formValue) {
- return <Fragment>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- {label}
- {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field has-addons">
- {!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`}
- onClick={(): void => { valueHandler({ current: 0, lost: 0, sold: 0 } as Stock as any); }} >
- <span><Translate>Manage stock</Translate></span>
- </button> : <button class="button"
- data-tooltip={i18n`this product has been configured without stock control`}
- disabled >
- <span><Translate>Infinite</Translate></span>
- </button>
- }
- </div>
- </div>
- </div>
- </Fragment >
- }
-
- const currentStock = (formValue.current || 0) - (formValue.lost || 0) - (formValue.sold || 0)
-
- const stockAddedErrors: FormErrors<typeof addedStock> = {
- lost: currentStock + addedStock.incoming < addedStock.lost ?
- i18n`lost cannot be greater than current and incoming (max ${currentStock + addedStock.incoming})`
- : undefined
- }
-
- // 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}`
- // )
-
- return <Fragment>
- <div class="card">
- <header class="card-header">
- <p class="card-header-title">
- {label}
- {tooltip && <span class="icon" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </p>
- </header>
- <div class="card-content">
- <FormProvider<Entity> name="stock" errors={errors} object={formValue} valueHandler={valueHandler}>
- {alreadyExist ? <Fragment>
-
- <FormProvider name="added" errors={stockAddedErrors} object={addedStock} valueHandler={setAddedStock as any}>
- <InputNumber name="incoming" label={i18n`Incoming`} />
- <InputNumber name="lost" label={i18n`Lost`} />
- </FormProvider>
-
- {/* <div class="field is-horizontal">
- <div class="field-label is-normal" />
- <div class="field-body is-flex-grow-3">
- <div class="field">
- {stockUpdateDescription}
- </div>
- </div>
- </div> */}
-
- </Fragment> : <InputNumber<Entity> name="current"
- label={i18n`Current`}
- side={
- <button class="button is-danger"
- data-tooltip={i18n`remove stock control for this product`}
- onClick={(): void => { valueHandler(undefined as any) }} >
- <span><Translate>without stock</Translate></span>
- </button>
- }
- />}
-
- <InputDate<Entity> name="nextRestock" label={i18n`Next restock`} withTimestampSupport />
-
- <InputGroup<Entity> name="address" label={i18n`Delivery address`}>
- <InputLocation name="address" />
- </InputGroup>
- </FormProvider>
- </div>
- </div>
- </Fragment>
-}
- // (
-
-
diff --git a/packages/merchant-backoffice/src/components/form/InputTaxes.tsx b/packages/merchant-backoffice/src/components/form/InputTaxes.tsx
deleted file mode 100644
index 507a612..0000000
--- a/packages/merchant-backoffice/src/components/form/InputTaxes.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-import { h, VNode } from "preact";
-import { useCallback, useState } from "preact/hooks";
-import * as yup from 'yup';
-import { MerchantBackend } from "../../declaration";
-import { Translate, useTranslator } from "../../i18n";
-import { TaxSchema as schema } from '../../schemas';
-import { FormErrors, FormProvider } from "./FormProvider";
-import { Input } from "./Input";
-import { InputGroup } from "./InputGroup";
-import { InputProps, useField } from "./useField";
-
-export interface Props<T> extends InputProps<T> {
- isValid?: (e: any) => boolean;
-}
-
-type Entity = MerchantBackend.Tax
-export function InputTaxes<T>({ name, readonly, label }: Props<keyof T>): VNode {
- const { value: taxes, onChange, } = useField<T>(name);
-
- const [value, valueHandler] = useState<Partial<Entity>>({})
- // const [errors, setErrors] = useState<FormErrors<Entity>>({})
-
- let errors: FormErrors<Entity> = {}
-
- try {
- schema.validateSync(value, { abortEarly: false })
- } catch (err) {
- if (err instanceof yup.ValidationError) {
- const yupErrors = err.inner as yup.ValidationError[]
- errors = yupErrors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, [cur.path]: cur.message }), {})
- }
- }
- const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined)
-
- const submit = useCallback((): void => {
- onChange([value as any, ...taxes] as any)
- valueHandler({})
- }, [value])
-
- const i18n = useTranslator()
-
- //FIXME: translating plural singular
- return (
- <InputGroup name="tax" label={label} alternative={taxes.length > 0 && <p>This product has {taxes.length} applicable taxes configured.</p>}>
- <FormProvider<Entity> name="tax" errors={errors} object={value} valueHandler={valueHandler} >
-
- <div class="field is-horizontal">
- <div class="field-label is-normal" />
- <div class="field-body" style={{ display: 'block' }}>
- {taxes.map((v: any, i: number) => <div key={i} class="tags has-addons mt-3 mb-0 mr-3" style={{ flexWrap: 'nowrap' }}>
- <span class="tag is-medium is-info mb-0" style={{ maxWidth: '90%' }}><b>{v.tax}</b>: {v.name}</span>
- <a class="tag is-medium is-danger is-delete mb-0" onClick={() => {
- onChange(taxes.filter((f: any) => f !== v) as any);
- valueHandler(v);
- }} />
- </div>
- )}
- {!taxes.length && i18n`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.`}>
- <Translate>Enter currency and value separated with a colon, e.g. "USD:2.3".</Translate>
- </Input>
-
- <Input<Entity> name="name" label={i18n`Description`} tooltip={i18n`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`}
- disabled={hasErrors}
- onClick={submit}><Translate>Add</Translate></button>
- </div>
- </FormProvider>
- </InputGroup>
- )
-}
diff --git a/packages/merchant-backoffice/src/components/form/InputWithAddon.tsx b/packages/merchant-backoffice/src/components/form/InputWithAddon.tsx
deleted file mode 100644
index a16ebc2..0000000
--- a/packages/merchant-backoffice/src/components/form/InputWithAddon.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { ComponentChildren, h, VNode } from "preact";
-import { InputProps, useField } from "./useField";
-
-export interface Props<T> extends InputProps<T> {
- expand?: boolean;
- inputType?: 'text' | 'number';
- addonBefore?: ComponentChildren;
- addonAfter?: ComponentChildren;
- toStr?: (v?: any) => string;
- fromStr?: (s: string) => any;
- inputExtra?: any,
- children?: ComponentChildren,
- side?: ComponentChildren;
-}
-
-const defaultToString = (f?: any): string => f || ''
-const defaultFromString = (v: string): any => v as any
-
-export function InputWithAddon<T>({ name, readonly, addonBefore, children, expand, label, placeholder, help, tooltip, inputType, inputExtra, side, addonAfter, toStr = defaultToString, fromStr = defaultFromString }: Props<keyof T>): VNode {
- const { error, value, onChange, required } = useField<T>(name);
-
- return <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- {label}
- {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <div class="field has-addons">
- {addonBefore && <div class="control">
- <a class="button is-static">{addonBefore}</a>
- </div>}
- <p class={`control${expand ? " is-expanded" :""}${required ? " has-icons-right" : ''}`}>
- <input {...(inputExtra || {})} class={error ? "input is-danger" : "input"} type={inputType}
- placeholder={placeholder} readonly={readonly}
- name={String(name)} value={toStr(value)}
- onChange={(e): void => onChange(fromStr(e.currentTarget.value))} />
- {required && <span class="icon has-text-danger is-right">
- <i class="mdi mdi-alert" />
- </span>}
- {help}
- {children}
- </p>
- {addonAfter && <div class="control">
- <a class="button is-static">{addonAfter}</a>
- </div>}
- </div>
- {error && <p class="help is-danger">{error}</p>}
- </div>
- {side}
- </div>
- </div>;
-}
diff --git a/packages/merchant-backoffice/src/components/form/TextField.tsx b/packages/merchant-backoffice/src/components/form/TextField.tsx
deleted file mode 100644
index 2579a27..0000000
--- a/packages/merchant-backoffice/src/components/form/TextField.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { ComponentChildren, h, VNode } from "preact";
-import { useField, InputProps } from "./useField";
-
-interface Props<T> extends InputProps<T> {
- inputType?: 'text' | 'number' | 'multiline' | 'password';
- expand?: boolean;
- side?: ComponentChildren;
- children: ComponentChildren;
-}
-
-export function TextField<T>({ name, tooltip, label, expand, help, children, side}: Props<keyof T>): VNode {
- const { error } = useField<T>(name);
- return <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- {label}
- {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}>
- <i class="mdi mdi-information" />
- </span>}
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class={expand ? "control is-expanded has-icons-right" : "control has-icons-right"}>
- {children}
- {help}
- </p>
- {error && <p class="help is-danger">{error}</p>}
- </div>
- {side}
- </div>
- </div>;
-}
diff --git a/packages/merchant-backoffice/src/components/form/useField.tsx b/packages/merchant-backoffice/src/components/form/useField.tsx
deleted file mode 100644
index 8479d7a..0000000
--- a/packages/merchant-backoffice/src/components/form/useField.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { ComponentChildren, VNode } from "preact";
-import { useFormContext } from "./FormProvider";
-
-interface Use<V> {
- error?: string;
- required: boolean;
- value: any;
- initial: any;
- onChange: (v: V) => void;
- toStr: (f: V | undefined) => string;
- fromStr: (v: string) => V
-}
-
-export function useField<T>(name: keyof T): Use<T[typeof name]> {
- const { errors, object, initialObject, toStr, fromStr, valueHandler } = useFormContext<T>()
- type P = typeof name
- type V = T[P]
-
- const updateField = (field: P) => (value: V): void => {
- return valueHandler((prev) => {
- return setValueDeeper(prev, String(field).split('.'), value)
- })
- }
-
- const defaultToString = ((f?: V): string => String(!f ? '' : f))
- const defaultFromString = ((v: string): V => v as any)
- const value = readField(object, String(name))
- const initial = readField(initialObject, String(name))
- const isDirty = value !== initial
- const hasError = readField(errors, String(name))
- return {
- error: isDirty ? hasError : undefined,
- required: !isDirty && hasError,
- value,
- initial,
- onChange: updateField(name) as any,
- toStr: toStr[name] ? toStr[name]! : defaultToString,
- fromStr: fromStr[name] ? fromStr[name]! : defaultFromString,
- }
-}
-/**
- * read the field of an object an support accessing it using '.'
- *
- * @param object
- * @param name
- * @returns
- */
-const readField = (object: any, name: string) => {
- return name.split('.').reduce((prev, current) => prev && prev[current], object)
-}
-
-const setValueDeeper = (object: any, names: string[], value: any): any => {
- if (names.length === 0) return value
- const [head, ...rest] = names
- return { ...object, [head]: setValueDeeper(object[head] || {}, rest, value) }
-}
-
-export interface InputProps<T> {
- name: T;
- label: ComponentChildren;
- placeholder?: string;
- tooltip?: ComponentChildren;
- readonly?: boolean;
- help?: ComponentChildren;
-} \ No newline at end of file
diff --git a/packages/merchant-backoffice/src/components/form/useGroupField.tsx b/packages/merchant-backoffice/src/components/form/useGroupField.tsx
deleted file mode 100644
index a73f464..0000000
--- a/packages/merchant-backoffice/src/components/form/useGroupField.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { useFormContext } from "./FormProvider";
-
-interface Use {
- hasError?: boolean;
-}
-
-export function useGroupField<T>(name: keyof T): Use {
- const f = useFormContext<T>();
- if (!f)
- return {};
-
- return {
- hasError: readField(f.errors, String(name))
- };
-}
-
-const readField = (object: any, name: string) => {
- return name.split('.').reduce((prev, current) => prev && prev[current], object)
-}
diff --git a/packages/merchant-backoffice/src/components/instance/DefaultInstanceFormFields.tsx b/packages/merchant-backoffice/src/components/instance/DefaultInstanceFormFields.tsx
deleted file mode 100644
index d80c65c..0000000
--- a/packages/merchant-backoffice/src/components/instance/DefaultInstanceFormFields.tsx
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { Fragment, h, VNode } from "preact";
-import { useBackendContext } from "../../context/backend";
-import { useTranslator } from "../../i18n";
-import { Entity } from "../../paths/admin/create/CreatePage";
-import { Input } from "../form/Input";
-import { InputCurrency } from "../form/InputCurrency";
-import { InputDuration } from "../form/InputDuration";
-import { InputGroup } from "../form/InputGroup";
-import { InputImage } from "../form/InputImage";
-import { InputLocation } from "../form/InputLocation";
-import { InputPaytoForm } from "../form/InputPaytoForm";
-import { InputWithAddon } from "../form/InputWithAddon";
-
-export function DefaultInstanceFormFields({
- readonlyId,
- showId,
-}: {
- readonlyId?: boolean;
- showId: boolean;
-}): VNode {
- const i18n = useTranslator();
- const backend = useBackendContext();
- return (
- <Fragment>
- {showId && (
- <InputWithAddon<Entity>
- 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.`}
- />
- )}
-
- <Input<Entity>
- name="name"
- label={i18n`Business name`}
- tooltip={i18n`Legal name of the business represented by this instance.`}
- />
-
- <Input<Entity>
- name="email"
- label={i18n`Email`}
- tooltip={i18n`Contact email`}
- />
-
- <Input<Entity>
- name="website"
- label={i18n`Website URL`}
- tooltip={i18n`URL.`}
- />
-
- <InputImage<Entity>
- name="logo"
- label={i18n`Logo`}
- tooltip={i18n`Logo image.`}
- />
-
- <InputPaytoForm<Entity>
- name="payto_uris"
- label={i18n`Bank account`}
- tooltip={i18n`URI specifying bank account for crediting revenue.`}
- />
-
- <InputCurrency<Entity>
- name="default_max_deposit_fee"
- label={i18n`Default max deposit fee`}
- tooltip={i18n`Maximum deposit fees this merchant is willing to pay per order by default.`}
- />
-
- <InputCurrency<Entity>
- name="default_max_wire_fee"
- label={i18n`Default max wire fee`}
- tooltip={i18n`Maximum wire fees this merchant is willing to pay per wire transfer by default.`}
- />
-
- <Input<Entity>
- name="default_wire_fee_amortization"
- label={i18n`Default wire fee amortization`}
- tooltip={i18n`Number of orders excess wire transfer fees will be divided by to compute per order surcharge.`}
- />
-
- <InputGroup
- name="address"
- label={i18n`Address`}
- tooltip={i18n`Physical location of the merchant.`}
- >
- <InputLocation name="address" />
- </InputGroup>
-
- <InputGroup
- name="jurisdiction"
- label={i18n`Jurisdiction`}
- tooltip={i18n`Jurisdiction for legal disputes with the merchant.`}
- >
- <InputLocation name="jurisdiction" />
- </InputGroup>
-
- <InputDuration<Entity>
- name="default_pay_delay"
- label={i18n`Default payment delay`}
- withForever
- tooltip={i18n`Time customers have to pay an order before the offer expires by default.`}
- />
-
- <InputDuration<Entity>
- name="default_wire_transfer_delay"
- label={i18n`Default wire transfer delay`}
- tooltip={i18n`Maximum time an exchange is allowed to delay wiring funds to the merchant, enabling it to aggregate smaller payments into larger wire transfers and reducing wire fees.`}
- withForever
- />
- </Fragment>
- );
-}
diff --git a/packages/merchant-backoffice/src/components/menu/LangSelector.tsx b/packages/merchant-backoffice/src/components/menu/LangSelector.tsx
deleted file mode 100644
index 41d08a5..0000000
--- a/packages/merchant-backoffice/src/components/menu/LangSelector.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import langIcon from '../../assets/icons/languageicon.svg';
-import { useTranslationContext } from "../../context/translation";
-import { strings as messages } from '../../i18n/strings'
-
-type LangsNames = {
- [P in keyof typeof messages]: string
-}
-
-const names: LangsNames = {
- es: 'Español [es]',
- en: 'English [en]',
- fr: 'Français [fr]',
- de: 'Deutsch [de]',
- sv: 'Svenska [sv]',
- it: 'Italiano [it]',
-}
-
-function getLangName(s: keyof LangsNames | string) {
- if (names[s]) return names[s]
- return s
-}
-
-export function LangSelector(): VNode {
- const [updatingLang, setUpdatingLang] = useState(false)
- const { lang, changeLanguage } = useTranslationContext()
-
- return <div class="dropdown is-active ">
- <div class="dropdown-trigger">
- <button class="button has-tooltip-left"
- data-tooltip="change language selection"
- aria-haspopup="true"
- aria-controls="dropdown-menu" onClick={() => setUpdatingLang(!updatingLang)}>
- <div class="icon is-small is-left">
- <img src={langIcon} />
- </div>
- <span>{getLangName(lang)}</span>
- <div class="icon is-right">
- <i class="mdi mdi-chevron-down" />
- </div>
- </button>
- </div>
- {updatingLang && <div class="dropdown-menu" id="dropdown-menu" role="menu">
- <div class="dropdown-content">
- {Object.keys(messages)
- .filter((l) => l !== lang)
- .map(l => <a key={l} class="dropdown-item" value={l} onClick={() => { changeLanguage(l); setUpdatingLang(false) }}>{getLangName(l)}</a>)}
- </div>
- </div>}
- </div>
-} \ No newline at end of file
diff --git a/packages/merchant-backoffice/src/components/menu/NavigationBar.tsx b/packages/merchant-backoffice/src/components/menu/NavigationBar.tsx
deleted file mode 100644
index e1bb4c7..0000000
--- a/packages/merchant-backoffice/src/components/menu/NavigationBar.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-
-import { h, VNode } from 'preact';
-import logo from '../../assets/logo.jpeg';
-import { LangSelector } from './LangSelector';
-
-interface Props {
- onMobileMenu: () => void;
- title: string;
-}
-
-export function NavigationBar({ onMobileMenu, title }: Props): VNode {
- return (<nav class="navbar is-fixed-top" role="navigation" aria-label="main navigation">
- <div class="navbar-brand">
- <span class="navbar-item" style={{ fontSize: 24, fontWeight: 900 }}>{title}</span>
-
- <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" onClick={(e) => {
- onMobileMenu()
- e.stopPropagation()
- }}>
- <span aria-hidden="true" />
- <span aria-hidden="true" />
- <span aria-hidden="true" />
- </a>
- </div>
-
- <div class="navbar-menu ">
- <a class="navbar-start is-justify-content-center is-flex-grow-1" href="https://taler.net">
- <img src={logo} style={{ height: 50, maxHeight: 50 }} />
- </a>
- <div class="navbar-end">
- <div class="navbar-item" style={{ paddingTop: 4, paddingBottom: 4 }}>
- <LangSelector />
- </div>
- </div>
- </div>
- </nav>
- );
-} \ No newline at end of file
diff --git a/packages/merchant-backoffice/src/components/menu/SideBar.tsx b/packages/merchant-backoffice/src/components/menu/SideBar.tsx
deleted file mode 100644
index e9c5ef8..0000000
--- a/packages/merchant-backoffice/src/components/menu/SideBar.tsx
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { Fragment, h, VNode } from "preact";
-import { useCallback } from "preact/hooks";
-import { useBackendContext } from "../../context/backend";
-import { useConfigContext } from "../../context/config";
-import { useInstanceContext } from "../../context/instance";
-import { useInstanceKYCDetails } from "../../hooks/instance";
-import { Translate } from "../../i18n";
-import { LangSelector } from "./LangSelector";
-
-interface Props {
- onLogout: () => void;
- mobile?: boolean;
- instance: string;
- admin?: boolean;
- mimic?: boolean;
-}
-
-export function Sidebar({
- mobile,
- instance,
- onLogout,
- admin,
- mimic,
-}: Props): VNode {
- const config = useConfigContext();
- const backend = useBackendContext();
-
- const kycStatus = useInstanceKYCDetails();
- const needKYC = kycStatus.ok && kycStatus.data.type === "redirect";
- // const withInstanceIdIfNeeded = useCallback(function (path: string) {
- // if (mimic) {
- // return path + '?instance=' + instance
- // }
- // return path
- // },[instance])
-
- return (
- <aside class="aside is-placed-left is-expanded">
- {mobile && (
- <div
- class="footer"
- onClick={(e) => {
- return e.stopImmediatePropagation();
- }}
- >
- <LangSelector />
- </div>
- )}
- <div class="aside-tools">
- <div class="aside-tools-label">
- <div>
- <b>Taler</b> Backoffice
- </div>
- <div
- class="is-size-7 has-text-right"
- style={{ lineHeight: 0, marginTop: -10 }}
- >
- {process.env.__VERSION__} ({config.version})
- </div>
- </div>
- </div>
- <div class="menu is-menu-main">
- {instance ? (
- <Fragment>
- <p class="menu-label">
- <Translate>Instance</Translate>
- </p>
- <ul class="menu-list">
- <li>
- <a href={"/update"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-square-edit-outline" />
- </span>
- <span class="menu-item-label">
- <Translate>Settings</Translate>
- </span>
- </a>
- </li>
- <li>
- <a href={"/orders"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-cash-register" />
- </span>
- <span class="menu-item-label">
- <Translate>Orders</Translate>
- </span>
- </a>
- </li>
- <li>
- <a href={"/products"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-shopping" />
- </span>
- <span class="menu-item-label">
- <Translate>Products</Translate>
- </span>
- </a>
- </li>
- <li>
- <a href={"/transfers"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-bank" />
- </span>
- <span class="menu-item-label">
- <Translate>Transfers</Translate>
- </span>
- </a>
- </li>
- <li>
- <a href={"/reserves"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-cash" />
- </span>
- <span class="menu-item-label">Reserves</span>
- </a>
- </li>
- {needKYC && (
- <li>
- <a href={"/kyc"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-account-check" />
- </span>
- <span class="menu-item-label">KYC Status</span>
- </a>
- </li>
- )}
- </ul>
- </Fragment>
- ) : undefined}
- <p class="menu-label">
- <Translate>Connection</Translate>
- </p>
- <ul class="menu-list">
- <li>
- <div>
- <span style={{ width: "3rem" }} class="icon">
- <i class="mdi mdi-currency-eur" />
- </span>
- <span class="menu-item-label">{config.currency}</span>
- </div>
- </li>
- <li>
- <div>
- <span style={{ width: "3rem" }} class="icon">
- <i class="mdi mdi-web" />
- </span>
- <span class="menu-item-label">
- {new URL(backend.url).hostname}
- </span>
- </div>
- </li>
- <li>
- <div>
- <span style={{ width: "3rem" }} class="icon">
- ID
- </span>
- <span class="menu-item-label">
- {!instance ? "default" : instance}
- </span>
- </div>
- </li>
- {admin && !mimic && (
- <Fragment>
- <p class="menu-label">
- <Translate>Instances</Translate>
- </p>
- <li>
- <a href={"/instance/new"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-plus" />
- </span>
- <span class="menu-item-label">
- <Translate>New</Translate>
- </span>
- </a>
- </li>
- <li>
- <a href={"/instances"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-format-list-bulleted" />
- </span>
- <span class="menu-item-label">
- <Translate>List</Translate>
- </span>
- </a>
- </li>
- </Fragment>
- )}
- <li>
- <a
- class="has-icon is-state-info is-hoverable"
- onClick={(): void => onLogout()}
- >
- <span class="icon">
- <i class="mdi mdi-logout default" />
- </span>
- <span class="menu-item-label">
- <Translate>Log out</Translate>
- </span>
- </a>
- </li>
- </ul>
- </div>
- </aside>
- );
-}
diff --git a/packages/merchant-backoffice/src/components/menu/index.tsx b/packages/merchant-backoffice/src/components/menu/index.tsx
deleted file mode 100644
index 0a621af..0000000
--- a/packages/merchant-backoffice/src/components/menu/index.tsx
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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/>
- */
-
-import { ComponentChildren, Fragment, h, VNode } from "preact";
-import Match from "preact-router/match";
-import { useEffect, useState } from "preact/hooks";
-import { AdminPaths } from "../../AdminRoutes";
-import { InstancePaths } from "../../InstanceRoutes";
-import { Notification } from "../../utils/types";
-import { NavigationBar } from "./NavigationBar";
-import { Sidebar } from "./SideBar";
-
-function getInstanceTitle(path: string, id: string): string {
- switch (path) {
- case InstancePaths.update:
- return `${id}: Settings`;
- case InstancePaths.order_list:
- return `${id}: Orders`;
- case InstancePaths.order_new:
- return `${id}: New order`;
- case InstancePaths.product_list:
- return `${id}: Products`;
- case InstancePaths.product_new:
- return `${id}: New product`;
- case InstancePaths.product_update:
- return `${id}: Update product`;
- case InstancePaths.reserves_new:
- return `${id}: New reserve`;
- case InstancePaths.reserves_list:
- return `${id}: Reserves`;
- case InstancePaths.transfers_list:
- return `${id}: Transfers`;
- case InstancePaths.transfers_new:
- return `${id}: New transfer`;
- default:
- return "";
- }
-}
-
-function getAdminTitle(path: string, instance: string) {
- if (path === AdminPaths.new_instance) return `New instance`;
- if (path === AdminPaths.list_instances) return `Instances`;
- return getInstanceTitle(path, instance);
-}
-
-interface MenuProps {
- title?: string;
- instance: string;
- admin?: boolean;
- onLogout?: () => void;
- setInstanceName: (s: string) => void;
-}
-
-function WithTitle({
- title,
- children,
-}: {
- title: string;
- children: ComponentChildren;
-}): VNode {
- useEffect(() => {
- document.title = `Taler Backoffice: ${title}`;
- }, [title]);
- return <Fragment>{children}</Fragment>;
-}
-
-export function Menu({
- onLogout,
- title,
- instance,
- admin,
- setInstanceName,
-}: MenuProps): VNode {
- const [mobileOpen, setMobileOpen] = useState(false);
-
- 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}
- />
-
- {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>
- )}
- </div>
- </WithTitle>
- );
- }}
- </Match>
- );
-}
-
-interface NotYetReadyAppMenuProps {
- title: string;
- onLogout?: () => void;
-}
-
-interface NotifProps {
- notification?: Notification;
-}
-export function NotificationCard({
- notification: n,
-}: NotifProps): VNode | null {
- if (!n) return null;
- return (
- <div class="notification">
- <div class="columns is-vcentered">
- <div class="column is-12">
- <article
- class={
- n.type === "ERROR"
- ? "message is-danger"
- : n.type === "WARN"
- ? "message is-warning"
- : "message is-info"
- }
- >
- <div class="message-header">
- <p>{n.message}</p>
- </div>
- {n.description && (
- <div class="message-body">
- <div>{n.description}</div>
- {n.details && <pre>{n.details}</pre>}
- </div>
- )}
- </article>
- </div>
- </div>
- </div>
- );
-}
-
-export function NotYetReadyAppMenu({
- onLogout,
- title,
-}: NotYetReadyAppMenuProps): VNode {
- const [mobileOpen, setMobileOpen] = useState(false);
-
- useEffect(() => {
- document.title = `Taler Backoffice: ${title}`;
- }, [title]);
-
- return (
- <div
- class={mobileOpen ? "has-aside-mobile-expanded" : ""}
- onClick={() => setMobileOpen(false)}
- >
- <NavigationBar
- onMobileMenu={() => setMobileOpen(!mobileOpen)}
- title={title}
- />
- {onLogout && (
- <Sidebar onLogout={onLogout} instance="" mobile={mobileOpen} />
- )}
- </div>
- );
-}
diff --git a/packages/merchant-backoffice/src/components/modal/index.tsx b/packages/merchant-backoffice/src/components/modal/index.tsx
deleted file mode 100644
index a7edb9e..0000000
--- a/packages/merchant-backoffice/src/components/modal/index.tsx
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { ComponentChildren, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { useInstanceContext } from "../../context/instance";
-import { Translate, useTranslator } from "../../i18n";
-import { DEFAULT_REQUEST_TIMEOUT } from "../../utils/constants";
-import { Loading, Spinner } from "../exception/loading";
-import { FormProvider } from "../form/FormProvider";
-import { Input } from "../form/Input";
-
-interface Props {
- active?: boolean;
- description?: string;
- onCancel?: () => void;
- onConfirm?: () => void;
- label?: string;
- children?: ComponentChildren;
- danger?: boolean;
- disabled?: boolean;
-}
-
-export function ConfirmModal({ active, description, onCancel, onConfirm, children, danger, disabled, label = 'Confirm' }: Props): VNode {
- return <div class={active ? "modal is-active" : "modal"}>
- <div class="modal-background " onClick={onCancel} />
- <div class="modal-card" style={{maxWidth: 700}}>
- <header class="modal-card-head">
- {!description ? null : <p class="modal-card-title"><b>{description}</b></p>}
- <button class="delete " aria-label="close" onClick={onCancel} />
- </header>
- <section class="modal-card-body">
- {children}
- </section>
- <footer class="modal-card-foot">
- <div class="buttons is-right" style={{ width: '100%' }}>
- <button class="button " onClick={onCancel} ><Translate>Cancel</Translate></button>
- <button class={danger ? "button is-danger " : "button is-info "} disabled={disabled} onClick={onConfirm} ><Translate>{label}</Translate></button>
- </div>
- </footer>
- </div>
- <button class="modal-close is-large " aria-label="close" onClick={onCancel} />
- </div>
-}
-
-export function ContinueModal({ active, description, onCancel, onConfirm, children, disabled }: Props): VNode {
- return <div class={active ? "modal is-active" : "modal"}>
- <div class="modal-background " onClick={onCancel} />
- <div class="modal-card">
- <header class="modal-card-head has-background-success">
- {!description ? null : <p class="modal-card-title">{description}</p>}
- <button class="delete " aria-label="close" onClick={onCancel} />
- </header>
- <section class="modal-card-body">
- {children}
- </section>
- <footer class="modal-card-foot">
- <div class="buttons is-right" style={{ width: '100%' }}>
- <button class="button is-success " disabled={disabled} onClick={onConfirm} ><Translate>Continue</Translate></button>
- </div>
- </footer>
- </div>
- <button class="modal-close is-large " aria-label="close" onClick={onCancel} />
- </div>
-}
-
-export function SimpleModal({ onCancel, children }: any): VNode {
- return <div class="modal is-active">
- <div class="modal-background " onClick={onCancel} />
- <div class="modal-card">
- <section class="modal-card-body is-main-section">
- {children}
- </section>
- </div>
- <button class="modal-close is-large " aria-label="close" onClick={onCancel} />
- </div>
-}
-
-export function ClearConfirmModal({ description, onCancel, onClear, onConfirm, children }: Props & { onClear?: () => void }): VNode {
- return <div class="modal is-active">
- <div class="modal-background " onClick={onCancel} />
- <div class="modal-card">
- <header class="modal-card-head">
- {!description ? null : <p class="modal-card-title">{description}</p>}
- <button class="delete " aria-label="close" onClick={onCancel} />
- </header>
- <section class="modal-card-body is-main-section">
- {children}
- </section>
- <footer class="modal-card-foot">
- {onClear && <button class="button is-danger" onClick={onClear} disabled={onClear === undefined} ><Translate>Clear</Translate></button>}
- <div class="buttons is-right" style={{ width: '100%' }}>
- <button class="button " onClick={onCancel} ><Translate>Cancel</Translate></button>
- <button class="button is-info" onClick={onConfirm} disabled={onConfirm === undefined} ><Translate>Confirm</Translate></button>
- </div>
- </footer>
- </div>
- <button class="modal-close is-large " aria-label="close" onClick={onCancel} />
- </div>
-}
-
-interface DeleteModalProps {
- element: { id: string, name: string };
- onCancel: () => void;
- onConfirm: (id: string) => void;
-}
-
-export function DeleteModal({ element, onCancel, onConfirm }: DeleteModalProps): VNode {
- return <ConfirmModal label={`Delete instance`} description={`Delete the instance "${element.name}"`} danger active onCancel={onCancel} onConfirm={() => onConfirm(element.id)}>
- <p>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>
- <p>This action deletes the instance private key, but preserves all transaction data. You can still access that data after deleting the instance.</p>
- <p class="warning">Deleting an instance <b>cannot be undone</b>.</p>
- </ConfirmModal>
-}
-
-export function PurgeModal({ element, onCancel, onConfirm }: DeleteModalProps): VNode {
- return <ConfirmModal label={`Purge the instance`} description={`Purge the instance "${element.name}"`} danger active onCancel={onCancel} 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.</p>
- <p>The instance will disappear from your list, and you will no longer be able to access it's data.</p>
- <p class="warning">Purging an instance <b>cannot be undone</b>.</p>
- </ConfirmModal>
-}
-
-interface UpdateTokenModalProps {
- oldToken?: string;
- onCancel: () => void;
- onConfirm: (value: string) => void;
- onClear: () => void;
-}
-
-//FIXME: merge UpdateTokenModal with SetTokenNewInstanceModal
-export function UpdateTokenModal({ onCancel, onClear, onConfirm, oldToken }: UpdateTokenModalProps): VNode {
- type State = { old_token: string, new_token: string, repeat_token: string }
- const [form, setValue] = useState<Partial<State>>({
- old_token: '', new_token: '', repeat_token: '',
- })
- const i18n = useTranslator()
-
- const hasInputTheCorrectOldToken = oldToken && oldToken !== form.old_token
- const errors = {
- old_token: hasInputTheCorrectOldToken ? i18n`is not the same as the current access token` : undefined,
- new_token: !form.new_token ? i18n`cannot be empty` : (form.new_token === form.old_token ? i18n`cannot be the same as the old token` : undefined),
- repeat_token: form.new_token !== form.repeat_token ? i18n`is not the same` : undefined
- }
-
- const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined)
-
- const instance = useInstanceContext()
-
- const text = i18n`You are updating the access token from instance with id ${instance.id}`
-
- return <ClearConfirmModal description={text}
- onCancel={onCancel}
- onConfirm={!hasErrors ? () => onConfirm(form.new_token!) : undefined}
- onClear={!hasInputTheCorrectOldToken && oldToken ? onClear : undefined}
- >
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths" >
- <FormProvider errors={errors} object={form} valueHandler={setValue}>
- {oldToken && <Input<State> name="old_token" label={i18n`Old access token`} tooltip={i18n`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`} inputType="password" />
- <Input<State> name="repeat_token" label={i18n`Repeat access token`} tooltip={i18n`confirm the same access token`} inputType="password" />
- </FormProvider>
- <p><Translate>Clearing the access token will mean public access to the instance</Translate></p>
- </div>
- <div class="column" />
- </div>
- </ClearConfirmModal>
-}
-
-export function SetTokenNewInstanceModal({ onCancel, onClear, onConfirm }: UpdateTokenModalProps): VNode {
- type State = { old_token: string, new_token: string, repeat_token: string }
- const [form, setValue] = useState<Partial<State>>({
- new_token: '', repeat_token: '',
- })
- const i18n = useTranslator()
-
- const errors = {
- new_token: !form.new_token ? i18n`cannot be empty` : (form.new_token === form.old_token ? i18n`cannot be the same as the old access token` : undefined),
- repeat_token: form.new_token !== form.repeat_token ? i18n`is not the same` : undefined
- }
-
- const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined)
-
-
- return <div class="modal is-active">
- <div class="modal-background " onClick={onCancel} />
- <div class="modal-card">
- <header class="modal-card-head">
- <p class="modal-card-title">{i18n`You are setting the access token for the new instance`}</p>
- <button class="delete " aria-label="close" onClick={onCancel} />
- </header>
- <section class="modal-card-body is-main-section">
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths" >
- <FormProvider errors={errors} object={form} valueHandler={setValue}>
- <Input<State> name="new_token" label={i18n`New access token`} tooltip={i18n`next access token to be used`} inputType="password" />
- <Input<State> name="repeat_token" label={i18n`Repeat access token`} tooltip={i18n`confirm the same access token`} inputType="password" />
- </FormProvider>
- <p><Translate>With external authorization method no check will be done by the merchant backend</Translate></p>
- </div>
- <div class="column" />
- </div>
- </section>
- <footer class="modal-card-foot">
- {onClear && <button class="button is-danger" onClick={onClear} disabled={onClear === undefined} ><Translate>Set external authorization</Translate></button>}
- <div class="buttons is-right" style={{ width: '100%' }}>
- <button class="button " onClick={onCancel} ><Translate>Cancel</Translate></button>
- <button class="button is-info" onClick={() => onConfirm(form.new_token!)} disabled={hasErrors} ><Translate>Set access token</Translate></button>
- </div>
- </footer>
- </div>
- <button class="modal-close is-large " aria-label="close" onClick={onCancel} />
- </div>
-}
-
-export function LoadingModal({ onCancel }: { onCancel: () => void }): VNode {
- const i18n = useTranslator()
- 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></p>
- </header>
- <section class="modal-card-body">
- <div class="columns">
- <div class="column" />
- <Spinner />
- <div class="column" />
- </div>
- <p>{i18n`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></button>
- </div>
- </footer>
- </div>
- <button class="modal-close is-large " aria-label="close" onClick={onCancel} />
- </div>
-}
diff --git a/packages/merchant-backoffice/src/components/notifications/CreatedSuccessfully.tsx b/packages/merchant-backoffice/src/components/notifications/CreatedSuccessfully.tsx
deleted file mode 100644
index e0b355c..0000000
--- a/packages/merchant-backoffice/src/components/notifications/CreatedSuccessfully.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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 { ComponentChildren, h, VNode } from "preact";
-
-interface Props {
- onCreateAnother?: () => void;
- onConfirm: () => void;
- children: ComponentChildren;
-}
-
-export function CreatedSuccessfully({ children, onConfirm, onCreateAnother }: Props): VNode {
- return <div class="columns is-fullwidth is-vcentered mt-3">
- <div class="column" />
- <div class="column is-four-fifths">
- <div class="card">
- <header class="card-header has-background-success">
- <p class="card-header-title has-text-white-ter">
- Success.
- </p>
- </header>
- <div class="card-content">
- {children}
- </div>
- </div>
- <div class="buttons is-right">
- {onCreateAnother && <button class="button is-info" onClick={onCreateAnother}>Create another</button>}
- <button class="button is-info" onClick={onConfirm}>Continue</button>
- </div>
- </div>
- <div class="column" />
- </div>
-}
diff --git a/packages/merchant-backoffice/src/components/notifications/Notifications.stories.tsx b/packages/merchant-backoffice/src/components/notifications/Notifications.stories.tsx
deleted file mode 100644
index 3b95295..0000000
--- a/packages/merchant-backoffice/src/components/notifications/Notifications.stories.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-
-import { h } from 'preact';
-import { Notifications } from './index';
-
-
-export default {
- title: 'Components/Notification',
- component: Notifications,
- argTypes: {
- removeNotification: { action: 'removeNotification' },
- },
-};
-
-export const Info = (a: any) => <Notifications {...a} />;
-Info.args = {
- notifications: [{
- message: 'Title',
- description: 'Some large description',
- type: 'INFO',
- }]
-}
-export const Warn = (a: any) => <Notifications {...a} />;
-Warn.args = {
- notifications: [{
- message: 'Title',
- description: 'Some large description',
- type: 'WARN',
- }]
-}
-export const Error = (a: any) => <Notifications {...a} />;
-Error.args = {
- notifications: [{
- message: 'Title',
- description: 'Some large description',
- type: 'ERROR',
- }]
-}
diff --git a/packages/merchant-backoffice/src/components/notifications/index.tsx b/packages/merchant-backoffice/src/components/notifications/index.tsx
deleted file mode 100644
index 34bd40e..0000000
--- a/packages/merchant-backoffice/src/components/notifications/index.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-
-import { h, VNode } from "preact";
-import { MessageType, Notification } from "../../utils/types";
-
-interface Props {
- notifications: Notification[];
- removeNotification?: (n: Notification) => void;
-}
-
-function messageStyle(type: MessageType): string {
- switch (type) {
- case "INFO": return "message is-info";
- case "WARN": return "message is-warning";
- case "ERROR": return "message is-danger";
- case "SUCCESS": return "message is-success";
- default: return "message"
- }
-}
-
-export function Notifications({ notifications, removeNotification }: Props): VNode {
- return <div class="toast">
- {notifications.map((n,i) => <article key={i} class={messageStyle(n.type)}>
- <div class="message-header">
- <p>{n.message}</p>
- <button class="delete" onClick={() => removeNotification && removeNotification(n)} />
- </div>
- {n.description && <div class="message-body">
- {n.description}
- </div>}
- </article>)}
- </div>
-} \ No newline at end of file
diff --git a/packages/merchant-backoffice/src/components/picker/DatePicker.tsx b/packages/merchant-backoffice/src/components/picker/DatePicker.tsx
deleted file mode 100644
index 084b7b0..0000000
--- a/packages/merchant-backoffice/src/components/picker/DatePicker.tsx
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-
-import { h, Component } from "preact";
-
-interface Props {
- closeFunction?: () => void;
- dateReceiver?: (d: Date) => void;
- opened?: boolean;
-}
-interface State {
- displayedMonth: number;
- displayedYear: number;
- selectYearMode: boolean;
- currentDate: Date;
-}
-
-// inspired by https://codepen.io/m4r1vs/pen/MOOxyE
-export class DatePicker extends Component<Props, State> {
-
- closeDatePicker() {
- this.props.closeFunction && this.props.closeFunction(); // Function gets passed by parent
- }
-
- /**
- * Gets fired when a day gets clicked.
- * @param {object} e The event thrown by the <span /> element clicked
- */
- dayClicked(e: any) {
-
- const element = e.target; // the actual element clicked
-
- if (element.innerHTML === '') return false; // don't continue if <span /> empty
-
- // get date from clicked element (gets attached when rendered)
- const date = new Date(element.getAttribute('data-value'));
-
- // update the state
- this.setState({ currentDate: date });
- this.passDateToParent(date)
- }
-
- /**
- * returns days in month as array
- * @param {number} month the month to display
- * @param {number} year the year to display
- */
- getDaysByMonth(month: number, year: number) {
-
- const calendar = [];
-
- const date = new Date(year, month, 1); // month to display
-
- const firstDay = new Date(year, month, 1).getDay(); // first weekday of month
- const lastDate = new Date(year, month + 1, 0).getDate(); // last date of month
-
- let day: number | null = 0;
-
- // the calendar is 7*6 fields big, so 42 loops
- for (let i = 0; i < 42; i++) {
-
- if (i >= firstDay && day !== null) day = day + 1;
- if (day !== null && day > lastDate) day = null;
-
- // append the calendar Array
- calendar.push({
- day: (day === 0 || day === null) ? null : day, // null or number
- date: (day === 0 || day === null) ? null : new Date(year, month, day), // null or Date()
- today: (day === now.getDate() && month === now.getMonth() && year === now.getFullYear()) // boolean
- });
- }
-
- return calendar;
- }
-
- /**
- * Display previous month by updating state
- */
- displayPrevMonth() {
- if (this.state.displayedMonth <= 0) {
- this.setState({
- displayedMonth: 11,
- displayedYear: this.state.displayedYear - 1
- });
- }
- else {
- this.setState({
- displayedMonth: this.state.displayedMonth - 1
- });
- }
- }
-
- /**
- * Display next month by updating state
- */
- displayNextMonth() {
- if (this.state.displayedMonth >= 11) {
- this.setState({
- displayedMonth: 0,
- displayedYear: this.state.displayedYear + 1
- });
- }
- else {
- this.setState({
- displayedMonth: this.state.displayedMonth + 1
- });
- }
- }
-
- /**
- * Display the selected month (gets fired when clicking on the date string)
- */
- displaySelectedMonth() {
- if (this.state.selectYearMode) {
- this.toggleYearSelector();
- }
- else {
- if (!this.state.currentDate) return false;
- this.setState({
- displayedMonth: this.state.currentDate.getMonth(),
- displayedYear: this.state.currentDate.getFullYear()
- });
- }
- }
-
- toggleYearSelector() {
- this.setState({ selectYearMode: !this.state.selectYearMode });
- }
-
- changeDisplayedYear(e: any) {
- const element = e.target;
- this.toggleYearSelector();
- this.setState({ displayedYear: parseInt(element.innerHTML, 10), displayedMonth: 0 });
- }
-
- /**
- * Pass the selected date to parent when 'OK' is clicked
- */
- passSavedDateDateToParent() {
- this.passDateToParent(this.state.currentDate)
- }
- passDateToParent(date: Date) {
- if (typeof this.props.dateReceiver === 'function') this.props.dateReceiver(date);
- this.closeDatePicker();
- }
-
- componentDidUpdate() {
- if (this.state.selectYearMode) {
- document.getElementsByClassName('selected')[0].scrollIntoView(); // works in every browser incl. IE, replace with scrollIntoViewIfNeeded when browsers support it
- }
- }
-
- constructor() {
- super();
-
- this.closeDatePicker = this.closeDatePicker.bind(this);
- this.dayClicked = this.dayClicked.bind(this);
- this.displayNextMonth = this.displayNextMonth.bind(this);
- this.displayPrevMonth = this.displayPrevMonth.bind(this);
- this.getDaysByMonth = this.getDaysByMonth.bind(this);
- this.changeDisplayedYear = this.changeDisplayedYear.bind(this);
- this.passDateToParent = this.passDateToParent.bind(this);
- this.toggleYearSelector = this.toggleYearSelector.bind(this);
- this.displaySelectedMonth = this.displaySelectedMonth.bind(this);
-
-
- this.state = {
- currentDate: now,
- displayedMonth: now.getMonth(),
- displayedYear: now.getFullYear(),
- selectYearMode: false
- }
- }
-
- render() {
-
- const { currentDate, displayedMonth, displayedYear, selectYearMode } = this.state;
-
- return (
- <div>
- <div class={`datePicker ${ this.props.opened && "datePicker--opened"}`} >
-
- <div class="datePicker--titles">
- <h3 style={{
- color: selectYearMode ? 'rgba(255,255,255,.87)' : 'rgba(255,255,255,.57)'
- }} onClick={this.toggleYearSelector}>{currentDate.getFullYear()}</h3>
- <h2 style={{
- color: !selectYearMode ? 'rgba(255,255,255,.87)' : 'rgba(255,255,255,.57)'
- }} onClick={this.displaySelectedMonth}>
- {dayArr[currentDate.getDay()]}, {monthArrShort[currentDate.getMonth()]} {currentDate.getDate()}
- </h2>
- </div>
-
- {!selectYearMode && <nav>
- <span onClick={this.displayPrevMonth} class="icon"><i style={{ transform: 'rotate(180deg)' }} class="mdi mdi-forward" /></span>
- <h4>{monthArrShortFull[displayedMonth]} {displayedYear}</h4>
- <span onClick={this.displayNextMonth} class="icon"><i class="mdi mdi-forward" /></span>
- </nav>}
-
- <div class="datePicker--scroll">
-
- {!selectYearMode && <div class="datePicker--calendar" >
-
- <div class="datePicker--dayNames">
- {['S', 'M', 'T', 'W', 'T', 'F', 'S'].map((day,i) => <span key={i}>{day}</span>)}
- </div>
-
- <div onClick={this.dayClicked} class="datePicker--days">
-
- {/*
- Loop through the calendar object returned by getDaysByMonth().
- */}
-
- {this.getDaysByMonth(this.state.displayedMonth, this.state.displayedYear)
- .map(
- day => {
- let selected = false;
-
- if (currentDate && day.date) selected = (currentDate.toLocaleDateString() === day.date.toLocaleDateString());
-
- return (<span key={day.day}
- class={(day.today ? 'datePicker--today ' : '') + (selected ? 'datePicker--selected' : '')}
- disabled={!day.date}
- data-value={day.date}
- >
- {day.day}
- </span>)
- }
- )
- }
-
- </div>
-
- </div>}
-
- {selectYearMode && <div class="datePicker--selectYear">
-
- {yearArr.map(year => (
- <span key={year} class={(year === displayedYear) ? 'selected' : ''} onClick={this.changeDisplayedYear}>
- {year}
- </span>
- ))}
-
- </div>}
-
- </div>
- </div>
-
- <div class="datePicker--background" onClick={this.closeDatePicker} style={{
- display: this.props.opened ? 'block' : 'none'
- }}
- />
-
- </div>
- )
- }
-}
-
-
-const monthArrShortFull = [
- 'January',
- 'February',
- 'March',
- 'April',
- 'May',
- 'June',
- 'July',
- 'August',
- 'September',
- 'October',
- 'November',
- 'December'
-]
-
-const monthArrShort = [
- 'Jan',
- 'Feb',
- 'Mar',
- 'Apr',
- 'May',
- 'Jun',
- 'Jul',
- 'Aug',
- 'Sep',
- 'Oct',
- 'Nov',
- 'Dec'
-]
-
-const dayArr = [
- 'Sun',
- 'Mon',
- 'Tue',
- 'Wed',
- 'Thu',
- 'Fri',
- 'Sat'
-]
-
-const now = new Date()
-
-const yearArr: number[] = []
-
-for (let i = 2010; i <= now.getFullYear() + 10; i++) {
- yearArr.push(i);
-}
diff --git a/packages/merchant-backoffice/src/components/picker/DurationPicker.stories.tsx b/packages/merchant-backoffice/src/components/picker/DurationPicker.stories.tsx
deleted file mode 100644
index 275c80f..0000000
--- a/packages/merchant-backoffice/src/components/picker/DurationPicker.stories.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-
-import { h, FunctionalComponent } from 'preact';
-import { useState } from 'preact/hooks';
-import { DurationPicker as TestedComponent } from './DurationPicker';
-
-
-export default {
- title: 'Components/Picker/Duration',
- component: TestedComponent,
- argTypes: {
- onCreate: { action: 'onCreate' },
- goBack: { action: 'goBack' },
- }
-};
-
-function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
- const r = (args: any) => <Component {...args} />
- r.args = props
- return r
-}
-
-export const Example = createExample(TestedComponent, {
- days: true, minutes: true, hours: true, seconds: true,
- value: 10000000
-});
-
-export const WithState = () => {
- const [v,s] = useState<number>(1000000)
- return <TestedComponent value={v} onChange={s} days minutes hours seconds />
-}
diff --git a/packages/merchant-backoffice/src/components/picker/DurationPicker.tsx b/packages/merchant-backoffice/src/components/picker/DurationPicker.tsx
deleted file mode 100644
index f32a48f..0000000
--- a/packages/merchant-backoffice/src/components/picker/DurationPicker.tsx
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { useTranslator } from "../../i18n";
-import "../../scss/DurationPicker.scss";
-
-export interface Props {
- hours?: boolean;
- minutes?: boolean;
- seconds?: boolean;
- days?: boolean;
- onChange: (value: number) => void;
- value: number;
-}
-
-// inspiration taken from https://github.com/flurmbo/react-duration-picker
-export function DurationPicker({
- days,
- hours,
- minutes,
- seconds,
- onChange,
- value,
-}: Props): VNode {
- const ss = 1000 * 1000;
- const ms = ss * 60;
- const hs = ms * 60;
- const ds = hs * 24;
- const i18n = useTranslator();
-
- return (
- <div class="rdp-picker">
- {days && (
- <DurationColumn
- unit={i18n`days`}
- max={99}
- value={Math.floor(value / ds)}
- onDecrease={value >= ds ? () => onChange(value - ds) : undefined}
- onIncrease={value < 99 * ds ? () => onChange(value + ds) : undefined}
- onChange={(diff) => onChange(value + diff * ds)}
- />
- )}
- {hours && (
- <DurationColumn
- unit={i18n`hours`}
- max={23}
- min={1}
- value={Math.floor(value / hs) % 24}
- onDecrease={value >= hs ? () => onChange(value - hs) : undefined}
- onIncrease={value < 99 * ds ? () => onChange(value + hs) : undefined}
- onChange={(diff) => onChange(value + diff * hs)}
- />
- )}
- {minutes && (
- <DurationColumn
- unit={i18n`minutes`}
- max={59}
- min={1}
- value={Math.floor(value / ms) % 60}
- onDecrease={value >= ms ? () => onChange(value - ms) : undefined}
- onIncrease={value < 99 * ds ? () => onChange(value + ms) : undefined}
- onChange={(diff) => onChange(value + diff * ms)}
- />
- )}
- {seconds && (
- <DurationColumn
- unit={i18n`seconds`}
- max={59}
- value={Math.floor(value / ss) % 60}
- onDecrease={value >= ss ? () => onChange(value - ss) : undefined}
- onIncrease={value < 99 * ds ? () => onChange(value + ss) : undefined}
- onChange={(diff) => onChange(value + diff * ss)}
- />
- )}
- </div>
- );
-}
-
-interface ColProps {
- unit: string;
- min?: number;
- max: number;
- value: number;
- onIncrease?: () => void;
- onDecrease?: () => void;
- onChange?: (diff: number) => void;
-}
-
-function InputNumber({
- initial,
- onChange,
-}: {
- initial: number;
- onChange: (n: number) => void;
-}) {
- const [value, handler] = useState<{ v: string }>({
- v: toTwoDigitString(initial),
- });
-
- return (
- <input
- value={value.v}
- onBlur={(e) => onChange(parseInt(value.v, 10))}
- onInput={(e) => {
- e.preventDefault();
- const n = Number.parseInt(e.currentTarget.value, 10);
- if (isNaN(n)) return handler({ v: toTwoDigitString(initial) });
- return handler({ v: toTwoDigitString(n) });
- }}
- style={{
- width: 50,
- border: "none",
- fontSize: "inherit",
- background: "inherit",
- }}
- />
- );
-}
-
-function DurationColumn({
- unit,
- min = 0,
- max,
- value,
- onIncrease,
- onDecrease,
- onChange,
-}: ColProps): VNode {
- const cellHeight = 35;
- return (
- <div class="rdp-column-container">
- <div class="rdp-masked-div">
- <hr class="rdp-reticule" style={{ top: cellHeight * 2 - 1 }} />
- <hr class="rdp-reticule" style={{ top: cellHeight * 3 - 1 }} />
-
- <div class="rdp-column" style={{ top: 0 }}>
- <div class="rdp-cell" key={value - 2}>
- {onDecrease && (
- <button
- style={{ width: "100%", textAlign: "center", margin: 5 }}
- onClick={onDecrease}
- >
- <span class="icon">
- <i class="mdi mdi-chevron-up" />
- </span>
- </button>
- )}
- </div>
- <div class="rdp-cell" key={value - 1}>
- {value > min ? toTwoDigitString(value - 1) : ""}
- </div>
- <div class="rdp-cell rdp-center" key={value}>
- {onChange ? (
- <InputNumber
- initial={value}
- onChange={(n) => onChange(n - value)}
- />
- ) : (
- toTwoDigitString(value)
- )}
- <div>{unit}</div>
- </div>
-
- <div class="rdp-cell" key={value + 1}>
- {value < max ? toTwoDigitString(value + 1) : ""}
- </div>
-
- <div class="rdp-cell" key={value + 2}>
- {onIncrease && (
- <button
- style={{ width: "100%", textAlign: "center", margin: 5 }}
- onClick={onIncrease}
- >
- <span class="icon">
- <i class="mdi mdi-chevron-down" />
- </span>
- </button>
- )}
- </div>
- </div>
- </div>
- </div>
- );
-}
-
-function toTwoDigitString(n: number) {
- if (n < 10) {
- return `0${n}`;
- }
- return `${n}`;
-}
diff --git a/packages/merchant-backoffice/src/components/product/InventoryProductForm.stories.tsx b/packages/merchant-backoffice/src/components/product/InventoryProductForm.stories.tsx
deleted file mode 100644
index 6504d85..0000000
--- a/packages/merchant-backoffice/src/components/product/InventoryProductForm.stories.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
-
-import { h, VNode, FunctionalComponent } from 'preact';
-import { InventoryProductForm as TestedComponent } from './InventoryProductForm';
-
-
-export default {
- title: 'Components/Product/Add',
- component: TestedComponent,
- argTypes: {
- onAddProduct: { action: 'onAddProduct' },
- },
-};
-
-function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
- const r = (args: any) => <Component {...args} />
- r.args = props
- return r
-}
-
-export const WithASimpleList = createExample(TestedComponent, {
- inventory:[{
- id: 'this id',
- description: 'this is the description',
- } as any]
-});
-
-export const WithAProductSelected = createExample(TestedComponent, {
- inventory:[],
- currentProducts: {
- thisid: {
- quantity: 1,
- product: {
- id: 'asd',
- description: 'asdsadsad',
- } as any
- }
- }
-});
diff --git a/packages/merchant-backoffice/src/components/product/InventoryProductForm.tsx b/packages/merchant-backoffice/src/components/product/InventoryProductForm.tsx
deleted file mode 100644
index 8f05c97..0000000
--- a/packages/merchant-backoffice/src/components/product/InventoryProductForm.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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/>
- */
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { FormProvider, FormErrors } from "../form/FormProvider";
-import { InputNumber } from "../form/InputNumber";
-import { InputSearchProduct } from "../form/InputSearchProduct";
-import { MerchantBackend, WithId } from "../../declaration";
-import { Translate, useTranslator } from "../../i18n";
-import { ProductMap } from "../../paths/instance/orders/create/CreatePage";
-
-type Form = {
- product: MerchantBackend.Products.ProductDetail & WithId,
- quantity: number;
-}
-
-interface Props {
- currentProducts: ProductMap,
- onAddProduct: (product: MerchantBackend.Products.ProductDetail & WithId, quantity: number) => void,
- inventory: (MerchantBackend.Products.ProductDetail & WithId)[],
-}
-
-export function InventoryProductForm({ currentProducts, onAddProduct, inventory }: Props): VNode {
- const initialState = { quantity: 1 }
- const [state, setState] = useState<Partial<Form>>(initialState)
- const [errors, setErrors] = useState<FormErrors<Form>>({})
-
- const i18n = useTranslator()
-
- 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.` });
- return;
- }
- if (productWithInfiniteStock) {
- onAddProduct(state.product, 1)
- } else {
- if (!state.quantity || state.quantity <= 0) {
- setErrors({ quantity: i18n`Quantity must be greater than 0!` });
- return;
- }
- const currentStock = state.product.total_stock - state.product.total_lost - state.product.total_sold
- const p = currentProducts[state.product.id]
- if (p) {
- 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.` });
- return;
- }
- onAddProduct(state.product, state.quantity + p.quantity)
- } else {
- if (state.quantity > currentStock) {
- const left = currentStock;
- setErrors({ quantity: i18n`This quantity exceeds remaining stock. Currently, only ${left} units remain unreserved in stock.` });
- return;
- }
- onAddProduct(state.product, state.quantity)
- }
- }
-
- setState(initialState)
- }
-
- return <FormProvider<Form> errors={errors} object={state} valueHandler={setState}>
- <InputSearchProduct selected={state.product} onChange={(p) => setState(v => ({ ...v, product: p }))} products={inventory} />
- { state.product && <div class="columns mt-5">
- <div class="column is-two-thirds">
- {!productWithInfiniteStock &&
- <InputNumber<Form> name="quantity" label={i18n`Quantity`} tooltip={i18n`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></button>
- </div>
- </div>
- </div> }
-
- </FormProvider>
-}
diff --git a/packages/merchant-backoffice/src/components/product/NonInventoryProductForm.tsx b/packages/merchant-backoffice/src/components/product/NonInventoryProductForm.tsx
deleted file mode 100644
index 397efe6..0000000
--- a/packages/merchant-backoffice/src/components/product/NonInventoryProductForm.tsx
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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/>
- */
-import { Fragment, h, VNode } from "preact";
-import { useCallback, useEffect, useState } from "preact/hooks";
-import * as yup from 'yup';
-import { FormErrors, FormProvider } from "../form/FormProvider";
-import { Input } from "../form/Input";
-import { InputCurrency } from "../form/InputCurrency";
-import { InputImage } from "../form/InputImage";
-import { InputNumber } from "../form/InputNumber";
-import { InputTaxes } from "../form/InputTaxes";
-import { MerchantBackend } from "../../declaration";
-import { useListener } from "../../hooks/listener";
-import { Translate, useTranslator } from "../../i18n";
-import {
- NonInventoryProductSchema as schema
-} from '../../schemas';
-
-
-type Entity = MerchantBackend.Product
-
-interface Props {
- onAddProduct: (p: Entity) => Promise<void>;
- productToEdit?: Entity;
-}
-export function NonInventoryProductFrom({ productToEdit, onAddProduct }: Props): VNode {
- const [showCreateProduct, setShowCreateProduct] = useState(false)
-
- const isEditing = !!productToEdit
-
- useEffect(() => {
- setShowCreateProduct(isEditing)
- }, [isEditing])
-
- const [submitForm, addFormSubmitter] = useListener<Partial<MerchantBackend.Product> | undefined>((result) => {
- if (result) {
- setShowCreateProduct(false)
- return onAddProduct({
- quantity: result.quantity || 0,
- taxes: result.taxes || [],
- description: result.description || '',
- image: result.image || '',
- price: result.price || '',
- unit: result.unit || ''
- })
- }
- return Promise.resolve()
- })
-
- const i18n = useTranslator()
-
- 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`} onClick={() => setShowCreateProduct(true)} ><Translate>Add custom product</Translate></button>
- </div>
- {showCreateProduct && <div class="modal is-active">
- <div class="modal-background " onClick={() => setShowCreateProduct(false)} />
- <div class="modal-card">
- <header class="modal-card-head">
- <p class="modal-card-title">{i18n`Complete information of the product`}</p>
- <button class="delete " aria-label="close" onClick={() => setShowCreateProduct(false)} />
- </header>
- <section class="modal-card-body">
- <ProductForm initial={productToEdit} onSubscribe={addFormSubmitter} />
- </section>
- <footer class="modal-card-foot">
- <div class="buttons is-right" style={{ width: '100%' }}>
- <button class="button " onClick={() => setShowCreateProduct(false)} ><Translate>Cancel</Translate></button>
- <button class="button is-info " disabled={!submitForm} onClick={submitForm} ><Translate>Confirm</Translate></button>
- </div>
- </footer>
- </div>
- <button class="modal-close is-large " aria-label="close" onClick={() => setShowCreateProduct(false)} />
- </div>}
- </Fragment>
-}
-
-interface ProductProps {
- onSubscribe: (c?: () => Entity | undefined) => void;
- initial?: Partial<Entity>;
-}
-
-interface NonInventoryProduct {
- quantity: number;
- description: string;
- unit: string;
- price: string;
- image: string;
- taxes: MerchantBackend.Tax[];
-}
-
-export function ProductForm({ onSubscribe, initial }: ProductProps): VNode {
- const [value, valueHandler] = useState<Partial<NonInventoryProduct>>({
- taxes: [],
- ...initial,
- })
- let errors: FormErrors<Entity> = {}
- try {
- schema.validateSync(value, { abortEarly: false })
- } catch (err) {
- if (err instanceof yup.ValidationError) {
- const yupErrors = err.inner as yup.ValidationError[]
- errors = yupErrors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, [cur.path]: cur.message }), {})
- }
- }
-
- const submit = useCallback((): Entity | undefined => {
- return value as MerchantBackend.Product
- }, [value])
-
- const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined)
-
- useEffect(() => {
- onSubscribe(hasErrors ? undefined : submit)
- }, [submit, hasErrors])
-
- const i18n = useTranslator()
-
- return <div>
- <FormProvider<NonInventoryProduct> name="product" errors={errors} object={value} valueHandler={valueHandler} >
-
- <InputImage<NonInventoryProduct> name="image" label={i18n`Image`} tooltip={i18n`photo of the product`} />
- <Input<NonInventoryProduct> name="description" inputType="multiline" label={i18n`Description`} tooltip={i18n`full product description`} />
- <Input<NonInventoryProduct> name="unit" label={i18n`Unit`} tooltip={i18n`name of the product unit`} />
- <InputCurrency<NonInventoryProduct> name="price" label={i18n`Price`} tooltip={i18n`amount in the current currency`} />
-
- <InputNumber<NonInventoryProduct> name="quantity" label={i18n`Quantity`} tooltip={i18n`how many products will be added`} />
-
- <InputTaxes<NonInventoryProduct> name="taxes" label={i18n`Taxes`} />
-
- </FormProvider>
- </div>
-}
diff --git a/packages/merchant-backoffice/src/components/product/ProductForm.tsx b/packages/merchant-backoffice/src/components/product/ProductForm.tsx
deleted file mode 100644
index 9434d3d..0000000
--- a/packages/merchant-backoffice/src/components/product/ProductForm.tsx
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import { h } from "preact";
-import { useCallback, useEffect, useState } from "preact/hooks";
-import * as yup from "yup";
-import { useBackendContext } from "../../context/backend";
-import { MerchantBackend } from "../../declaration";
-import { useTranslator } from "../../i18n";
-import {
- ProductCreateSchema as createSchema,
- ProductUpdateSchema as updateSchema,
-} from "../../schemas";
-import { FormProvider, FormErrors } from "../form/FormProvider";
-import { Input } from "../form/Input";
-import { InputCurrency } from "../form/InputCurrency";
-import { InputImage } from "../form/InputImage";
-import { InputNumber } from "../form/InputNumber";
-import { InputStock, Stock } from "../form/InputStock";
-import { InputTaxes } from "../form/InputTaxes";
-import { InputWithAddon } from "../form/InputWithAddon";
-
-type Entity = MerchantBackend.Products.ProductDetail & { product_id: string };
-
-interface Props {
- onSubscribe: (c?: () => Entity | undefined) => void;
- initial?: Partial<Entity>;
- alreadyExist?: boolean;
-}
-
-export function ProductForm({ onSubscribe, initial, alreadyExist }: Props) {
- const [value, valueHandler] = useState<Partial<Entity & { stock: Stock }>>({
- address: {},
- description_i18n: {},
- taxes: [],
- next_restock: { t_s: "never" },
- price: ":0",
- ...initial,
- stock:
- !initial || initial.total_stock === -1
- ? undefined
- : {
- current: initial.total_stock || 0,
- lost: initial.total_lost || 0,
- sold: initial.total_sold || 0,
- address: initial.address,
- nextRestock: initial.next_restock,
- },
- });
- let errors: FormErrors<Entity> = {};
-
- try {
- (alreadyExist ? updateSchema : createSchema).validateSync(value, {
- abortEarly: false,
- });
- } catch (err) {
- if (err instanceof yup.ValidationError) {
- const yupErrors = err.inner as yup.ValidationError[];
- errors = yupErrors.reduce(
- (prev, cur) =>
- !cur.path ? prev : { ...prev, [cur.path]: cur.message },
- {}
- );
- }
- }
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined
- );
-
- const submit = useCallback((): Entity | undefined => {
- const stock: Stock = (value as any).stock;
-
- if (!stock) {
- value.total_stock = -1;
- } else {
- value.total_stock = stock.current;
- value.total_lost = stock.lost;
- value.next_restock =
- stock.nextRestock instanceof Date
- ? { t_s: stock.nextRestock.getTime() / 1000 }
- : stock.nextRestock;
- value.address = stock.address;
- }
- delete (value as any).stock;
-
- if (typeof value.minimum_age !== "undefined" && value.minimum_age < 1) {
- delete value.minimum_age;
- }
-
- return value as MerchantBackend.Products.ProductDetail & {
- product_id: string;
- };
- }, [value]);
-
- useEffect(() => {
- onSubscribe(hasErrors ? undefined : submit);
- }, [submit, hasErrors]);
-
- const backend = useBackendContext();
- const i18n = useTranslator();
-
- return (
- <div>
- <FormProvider<Entity>
- name="product"
- errors={errors}
- object={value}
- valueHandler={valueHandler}
- >
- {alreadyExist ? undefined : (
- <InputWithAddon<Entity>
- name="product_id"
- addonBefore={`${backend.url}/product/`}
- label={i18n`ID`}
- tooltip={i18n`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`}
- />
- <Input<Entity>
- name="description"
- inputType="multiline"
- label={i18n`Description`}
- tooltip={i18n`product description for customers`}
- />
- <InputNumber<Entity>
- name="minimum_age"
- label={i18n`Age restricted`}
- tooltip={i18n`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`}
- />
- <InputCurrency<Entity>
- name="price"
- label={i18n`Price`}
- tooltip={i18n`sale price for customers, including taxes, for above units of the product`}
- />
- <InputStock
- name="stock"
- label={i18n`Stock`}
- alreadyExist={alreadyExist}
- tooltip={i18n`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`}
- />
- </FormProvider>
- </div>
- );
-}
diff --git a/packages/merchant-backoffice/src/components/product/ProductList.tsx b/packages/merchant-backoffice/src/components/product/ProductList.tsx
deleted file mode 100644
index ff141bb..0000000
--- a/packages/merchant-backoffice/src/components/product/ProductList.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 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/>
- */
-import { Amounts } from "@gnu-taler/taler-util";
-import { h, VNode } from "preact";
-import emptyImage from "../../assets/empty.png";
-import { MerchantBackend } from "../../declaration";
-import { Translate } from "../../i18n";
-
-interface Props {
- list: MerchantBackend.Product[];
- actions?: {
- name: string;
- tooltip: string;
- handler: (d: MerchantBackend.Product, index: number) => void;
- }[];
-}
-export function ProductList({ list, actions = [] }: Props): VNode {
- return (
- <div class="table-container">
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <Translate>image</Translate>
- </th>
- <th>
- <Translate>description</Translate>
- </th>
- <th>
- <Translate>quantity</Translate>
- </th>
- <th>
- <Translate>unit price</Translate>
- </th>
- <th>
- <Translate>total price</Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {list.map((entry, index) => {
- const unitPrice = !entry.price ? "0" : entry.price;
- const totalPrice = !entry.price
- ? "0"
- : Amounts.stringify(
- Amounts.mult(
- Amounts.parseOrThrow(entry.price),
- entry.quantity
- ).amount
- );
-
- return (
- <tr key={index}>
- <td>
- <img
- style={{ height: 32, width: 32 }}
- src={entry.image ? entry.image : emptyImage}
- />
- </td>
- <td>{entry.description}</td>
- <td>
- {entry.quantity === 0
- ? "--"
- : `${entry.quantity} ${entry.unit}`}
- </td>
- <td>{unitPrice}</td>
- <td>{totalPrice}</td>
- <td class="is-actions-cell right-sticky">
- {actions.map((a, i) => {
- return (
- <div key={i} class="buttons is-right">
- <button
- class="button is-small is-danger has-tooltip-left"
- data-tooltip={a.tooltip}
- type="button"
- onClick={() => a.handler(entry, index)}
- >
- {a.name}
- </button>
- </div>
- );
- })}
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- </div>
- );
-}