aboutsummaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/components/form
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2022-12-19 12:23:39 -0300
committerSebastian <sebasjm@gmail.com>2022-12-19 12:23:39 -0300
commit72b429321553841ac1ff48cf974bfc65da01bb06 (patch)
tree7db9a4462f02de6cb86de695a1e64772b00ead5f /packages/merchant-backoffice-ui/src/components/form
parent770ab6f01dc81a16f384f314982bd761540f8e65 (diff)
downloadwallet-core-72b429321553841ac1ff48cf974bfc65da01bb06.tar.xz
pretty
Diffstat (limited to 'packages/merchant-backoffice-ui/src/components/form')
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/FormProvider.tsx76
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/Input.tsx118
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputArray.tsx156
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputBoolean.tsx89
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx51
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputGroup.tsx82
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputImage.tsx144
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputLocation.tsx51
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputNumber.tsx44
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputPayto.tsx41
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx199
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputSecured.stories.tsx56
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputSecured.tsx222
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputStock.tsx232
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx119
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx118
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/TextField.tsx64
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/useField.tsx65
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/useGroupField.tsx17
20 files changed, 1230 insertions, 716 deletions
diff --git a/packages/merchant-backoffice-ui/src/components/form/FormProvider.tsx b/packages/merchant-backoffice-ui/src/components/form/FormProvider.tsx
index ab32b6bed..7bcebd706 100644
--- a/packages/merchant-backoffice-ui/src/components/form/FormProvider.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/FormProvider.tsx
@@ -15,37 +15,59 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @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;
+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
+ children: ComponentChildren;
}
-const noUpdater: Updater<Partial<unknown>> = () => (s: unknown) => s
+const noUpdater: Updater<Partial<unknown>> = () => (s: unknown) => s;
-export function FormProvider<T>({ object = {}, errors = {}, name = '', valueHandler, children }: Props<T>): VNode {
+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>;
+ 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> {
@@ -58,24 +80,24 @@ export interface FormType<T> {
valueHandler: Updater<Partial<T>>;
}
-const FormContext = createContext<FormType<unknown>>(null!)
+const FormContext = createContext<FormType<unknown>>(null!);
export function useFormContext<T>() {
- return useContext<FormType<T>>(FormContext)
+ return useContext<FormType<T>>(FormContext);
}
export type FormErrors<T> = {
- [P in keyof T]?: string | FormErrors<T[P]>
-}
+ [P in keyof T]?: string | FormErrors<T[P]>;
+};
export type FormtoStr<T> = {
- [P in keyof T]?: ((f?: T[P]) => string)
-}
+ [P in keyof T]?: (f?: T[P]) => string;
+};
export type FormfromStr<T> = {
- [P in keyof T]?: ((f: string) => T[P])
-}
+ [P in keyof T]?: (f: string) => T[P];
+};
export type FormUpdater<T> = {
- [P in keyof T]?: (f: keyof T) => (v: T[P]) => void
-}
+ [P in keyof T]?: (f: keyof T) => (v: T[P]) => void;
+};
diff --git a/packages/merchant-backoffice-ui/src/components/form/Input.tsx b/packages/merchant-backoffice-ui/src/components/form/Input.tsx
index 793477f3d..54140ba4d 100644
--- a/packages/merchant-backoffice-ui/src/components/form/Input.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/Input.tsx
@@ -15,57 +15,101 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { ComponentChildren, h, VNode } from "preact";
import { useField, InputProps } from "./useField.js";
interface Props<T> extends InputProps<T> {
- inputType?: 'text' | 'number' | 'multiline' | 'password';
+ inputType?: "text" | "number" | "multiline" | "password";
expand?: boolean;
toStr?: (v?: any) => string;
fromStr?: (s: string) => any;
- inputExtra?: any,
+ inputExtra?: any;
side?: ComponentChildren;
children?: ComponentChildren;
}
-const defaultToString = (f?: any): string => f || ''
-const defaultFromString = (v: string): any => v as any
+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} />;
+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 {
+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>}
+ 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>
- {side}
</div>
- </div>;
+ );
}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx b/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx
index f8bf6437d..b5da1117a 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Translate, useTranslator } from "../../i18n/index.js";
@@ -30,68 +30,110 @@ export interface Props<T> extends InputProps<T> {
fromStr?: (s: string) => any;
}
-const defaultToString = (f?: any): string => f || ''
-const defaultFromString = (v: string): any => v as 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 {
+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 [localError, setLocalError] = useState<string | null>(null);
- const error = localError || formError
+ const error = localError || formError;
const array: any[] = (value ? value! : []) as any;
- const [currentValue, setCurrentValue] = useState('');
+ 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));
- }} />
+ 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>
- </div>;
+ );
}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputBoolean.tsx b/packages/merchant-backoffice-ui/src/components/form/InputBoolean.tsx
index 4c40cacf6..f79e16c07 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputBoolean.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputBoolean.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { h, VNode } from "preact";
import { InputProps, useField } from "./useField.js";
@@ -30,43 +30,62 @@ interface Props<T> extends InputProps<T> {
fromBoolean?: (s: boolean | undefined) => any;
}
-const defaultToBoolean = (f?: any): boolean | undefined => f || ''
-const defaultFromBoolean = (v: boolean | undefined): any => v as 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 {
+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))
- }
+ 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>}
+ 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>
- </div>;
+ );
}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx b/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx
index 6191d7ba5..57a5163b7 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { ComponentChildren, h } from "preact";
import { useConfigContext } from "../../context/config.js";
import { Amount } from "../../declaration.js";
@@ -31,17 +31,36 @@ export interface Props<T> extends InputProps<T> {
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}
- />
+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-ui/src/components/form/InputDuration.tsx b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx
index dd21a4708..658cc4db7 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx
@@ -81,7 +81,7 @@ export function InputDuration<T>({
era: () => "e",
},
},
- }
+ },
);
}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputGroup.tsx b/packages/merchant-backoffice-ui/src/components/form/InputGroup.tsx
index 26d0292d6..b5e0bd52b 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputGroup.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputGroup.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { ComponentChildren, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { useGroupField } from "./useGroupField.js";
@@ -32,35 +32,55 @@ export interface Props<T> {
initialActive?: boolean;
}
-export function InputGroup<T>({ name, label, children, tooltip, alternative, fixed, initialActive }: Props<keyof T>): VNode {
+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>;
+ 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-ui/src/components/form/InputImage.tsx b/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx
index 51ac23ca1..d5b2aadb6 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { ComponentChildren, h, VNode } from "preact";
import { useRef, useState } from "preact/hooks";
import { Translate } from "../../i18n/index.js";
@@ -30,65 +30,93 @@ export interface Props<T> extends InputProps<T> {
children?: ComponentChildren;
}
-export function InputImage<T>({ name, readonly, placeholder, tooltip, label, help, children, expand }: Props<keyof T>): VNode {
+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 image = useRef<HTMLInputElement>(null);
- const [sizeError, setSizeError] = useState(false)
+ 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>
- }
+ 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>
- </div>
+ );
}
-
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputLocation.tsx b/packages/merchant-backoffice-ui/src/components/form/InputLocation.tsx
index c97fe928b..613b2f1e6 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputLocation.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputLocation.tsx
@@ -15,29 +15,36 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { Fragment, h } from "preact";
import { useTranslator } from "../../i18n/index.js";
import { Input } from "./Input.js";
-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
+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`}
+ />
+ </>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputNumber.tsx b/packages/merchant-backoffice-ui/src/components/form/InputNumber.tsx
index 9f0b28ff2..3b5df1474 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputNumber.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputNumber.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { ComponentChildren, h } from "preact";
import { InputWithAddon } from "./InputWithAddon.js";
import { InputProps } from "./useField.js";
@@ -29,14 +29,32 @@ export interface Props<T> extends InputProps<T> {
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}
- />
+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-ui/src/components/form/InputPayto.tsx b/packages/merchant-backoffice-ui/src/components/form/InputPayto.tsx
index 021616e3f..6e88e8f2c 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputPayto.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputPayto.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { h, VNode } from "preact";
import { InputArray } from "./InputArray.js";
import { PAYTO_REGEX } from "../../utils/constants.js";
@@ -25,15 +25,28 @@ import { InputProps } from "./useField.js";
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}` }
- />
+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-ui/src/components/form/InputSearchProduct.tsx b/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx
index 0c91cc5a1..fceee9d56 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import emptyImage from "../../assets/empty.png";
@@ -26,74 +26,97 @@ import { Translate, useTranslator } from "../../i18n/index.js";
import { FormErrors, FormProvider } from "./FormProvider.js";
import { InputWithAddon } from "./InputWithAddon.js";
-type Entity = MerchantBackend.Products.ProductDetail & WithId
+type Entity = MerchantBackend.Products.ProductDetail & WithId;
export interface Props {
selected?: Entity;
onChange: (p?: Entity) => void;
- products: (MerchantBackend.Products.ProductDetail & WithId)[],
+ products: (MerchantBackend.Products.ProductDetail & WithId)[];
}
interface ProductSearch {
name: string;
}
-export function InputSearchProduct({ selected, onChange, products }: Props): VNode {
- const [prodForm, setProdName] = useState<Partial<ProductSearch>>({ name: '' })
+export function InputSearchProduct({
+ selected,
+ onChange,
+ products,
+}: Props): VNode {
+ const [prodForm, setProdName] = useState<Partial<ProductSearch>>({
+ name: "",
+ });
const errors: FormErrors<ProductSearch> = {
- name: undefined
- }
- const i18n = useTranslator()
-
+ 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>
+ 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>
- </div>
- </article>
+ </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>}
+ return (
+ <FormProvider<ProductSearch>
+ errors={errors}
+ object={prodForm}
+ valueHandler={setProdName}
>
- <div>
- <ProductList
- name={prodForm.name}
- list={products}
- onSelect={(p) => {
- setProdName({ name: '' })
- onChange(p)
- }}
- />
- </div>
- </InputWithAddon>
-
- </FormProvider>
-
+ <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)[]
+ list: (MerchantBackend.Products.ProductDetail & WithId)[];
}
function ProductList({ name, onSelect, list }: ProductListProps) {
@@ -102,37 +125,61 @@ function ProductList({ name, onSelect, list }: ProductListProps) {
this BR is added to occupy the space that will be added when the
dropdown appears
*/
- return <div ><br /></div>
+ 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>
+ 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>
- </div>
+ );
}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputSecured.stories.tsx b/packages/merchant-backoffice-ui/src/components/form/InputSecured.stories.tsx
index 061525d9e..12ce6c6aa 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputSecured.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputSecured.stories.tsx
@@ -15,41 +15,47 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
-import { h, VNode } from 'preact';
-import { useState } from 'preact/hooks';
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
import { FormProvider } from "./FormProvider.js";
-import { InputSecured } from './InputSecured.js';
+import { InputSecured } from "./InputSecured.js";
export default {
- title: 'Components/Form/InputSecured',
+ title: "Components/Form/InputSecured",
component: InputSecured,
};
-type T = { auth_token: string | null }
+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>
-}
+ 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>
-}
+ 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>
-}
+ 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-ui/src/components/form/InputSecured.tsx b/packages/merchant-backoffice-ui/src/components/form/InputSecured.tsx
index b0168e505..799978683 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputSecured.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputSecured.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Translate, useTranslator } from "../../i18n/index.js";
@@ -26,94 +26,160 @@ import { InputProps, useField } from "./useField.js";
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>
+ 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 {
+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 [newValue, setNuewValue] = useState("");
- const i18n = useTranslator()
+ 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 &&
+ 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="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>
+ {!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>
- <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>
+ </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>
- </div>
- }
- </Fragment >;
+ )}
+ </Fragment>
+ );
}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx b/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx
index 74806734c..57aa5968d 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { Fragment, h } from "preact";
import { MerchantBackend, Timestamp } from "../../declaration.js";
import { InputProps, useField } from "./useField.js";
@@ -34,8 +34,7 @@ export interface Props<T> extends InputProps<T> {
alreadyExist?: boolean;
}
-
-type Entity = Stock
+type Entity = Stock;
export interface Stock {
current: number;
@@ -50,66 +49,96 @@ interface StockDelta {
lost: number;
}
-
-export function InputStock<T>({ name, tooltip, label, alreadyExist }: Props<keyof T>) {
+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()
+ 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)
+ onChange(undefined as any);
} else {
onChange({
...formValue,
current: (formValue?.current || 0) + addedStock.incoming,
- lost: (formValue?.lost || 0) + addedStock.lost
- } as any)
+ lost: (formValue?.lost || 0) + addedStock.lost,
+ } as any);
}
- }, [formValue, addedStock])
+ }, [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>
- }
+ 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>
- </div>
- </Fragment >
+ </Fragment>
+ );
}
- const currentStock = (formValue.current || 0) - (formValue.lost || 0) - (formValue.sold || 0)
+ 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
- }
+ 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 ?
@@ -117,26 +146,39 @@ export function InputStock<T>({ name, tooltip, label, alreadyExist }: Props<keyo
// 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">
+ 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">
@@ -144,28 +186,40 @@ export function InputStock<T>({ name, tooltip, label, alreadyExist }: Props<keyo
</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>
+ </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>
- </div>
- </Fragment>
+ </Fragment>
+ );
}
- // (
-
-
+// (
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx b/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx
index 84f9234e9..d95463790 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx
@@ -15,12 +15,12 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { h, VNode } from "preact";
import { useCallback, useState } from "preact/hooks";
-import * as yup from 'yup';
+import * as yup from "yup";
import { MerchantBackend } from "../../declaration.js";
import { Translate, useTranslator } from "../../i18n/index.js";
import { TaxSchema as schema } from "../../schemas/index.js";
@@ -33,65 +33,114 @@ 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);
+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 [value, valueHandler] = useState<Partial<Entity>>({});
// const [errors, setErrors] = useState<FormErrors<Entity>>({})
- let errors: FormErrors<Entity> = {}
+ let errors: FormErrors<Entity> = {};
try {
- schema.validateSync(value, { abortEarly: false })
+ 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 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 hasErrors = Object.keys(errors).some(
+ (k) => (errors as any)[k] !== undefined,
+ );
const submit = useCallback((): void => {
- onChange([value as any, ...taxes] as any)
- valueHandler({})
- }, [value])
+ onChange([value as any, ...taxes] as any);
+ valueHandler({});
+ }, [value]);
- const i18n = useTranslator()
+ 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} >
-
+ <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>
- )}
+ <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<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.`} />
+ <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"
+ <button
+ class="button is-info"
data-tooltip={i18n`add tax to the tax list`}
disabled={hasErrors}
- onClick={submit}><Translate>Add</Translate></button>
+ onClick={submit}
+ >
+ <Translate>Add</Translate>
+ </button>
</div>
</FormProvider>
</InputGroup>
- )
+ );
}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
index bdc9eaa0c..620922584 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
@@ -15,63 +15,99 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { ComponentChildren, h, VNode } from "preact";
import { InputProps, useField } from "./useField.js";
export interface Props<T> extends InputProps<T> {
expand?: boolean;
- inputType?: 'text' | 'number';
+ inputType?: "text" | "number";
addonBefore?: ComponentChildren;
addonAfter?: ComponentChildren;
toStr?: (v?: any) => string;
fromStr?: (s: string) => any;
- inputExtra?: any,
- children?: ComponentChildren,
+ inputExtra?: any;
+ children?: ComponentChildren;
side?: ComponentChildren;
}
-const defaultToString = (f?: any): string => f || ''
-const defaultFromString = (v: string): any => v as any
+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 {
+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>}
+ 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>
- {error && <p class="help is-danger">{error}</p>}
+ {side}
</div>
- {side}
</div>
- </div>;
+ );
}
diff --git a/packages/merchant-backoffice-ui/src/components/form/TextField.tsx b/packages/merchant-backoffice-ui/src/components/form/TextField.tsx
index 2cda71599..03f36dcbb 100644
--- a/packages/merchant-backoffice-ui/src/components/form/TextField.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/TextField.tsx
@@ -15,39 +15,57 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { ComponentChildren, h, VNode } from "preact";
import { useField, InputProps } from "./useField.js";
interface Props<T> extends InputProps<T> {
- inputType?: 'text' | 'number' | 'multiline' | 'password';
+ 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 {
+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>}
+ 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>
- {side}
</div>
- </div>;
+ );
}
diff --git a/packages/merchant-backoffice-ui/src/components/form/useField.tsx b/packages/merchant-backoffice-ui/src/components/form/useField.tsx
index 6b685d722..dffb0cc66 100644
--- a/packages/merchant-backoffice-ui/src/components/form/useField.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/useField.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { ComponentChildren, VNode } from "preact";
import { useFormContext } from "./FormProvider.js";
@@ -29,26 +29,29 @@ interface Use<V> {
initial: any;
onChange: (v: V) => void;
toStr: (f: V | undefined) => string;
- fromStr: (v: string) => V
+ 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 { 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 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))
+ 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,
@@ -57,24 +60,26 @@ export function useField<T>(name: keyof T): Use<T[typeof name]> {
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
+ *
+ * @param object
+ * @param name
+ * @returns
*/
const readField = (object: any, name: string) => {
- return name.split('.').reduce((prev, current) => prev && prev[current], object)
-}
+ 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) }
-}
+ if (names.length === 0) return value;
+ const [head, ...rest] = names;
+ return { ...object, [head]: setValueDeeper(object[head] || {}, rest, value) };
+};
export interface InputProps<T> {
name: T;
@@ -83,4 +88,4 @@ export interface InputProps<T> {
tooltip?: ComponentChildren;
readonly?: boolean;
help?: ComponentChildren;
-} \ No newline at end of file
+}
diff --git a/packages/merchant-backoffice-ui/src/components/form/useGroupField.tsx b/packages/merchant-backoffice-ui/src/components/form/useGroupField.tsx
index e6365e3ad..9a445eb32 100644
--- a/packages/merchant-backoffice-ui/src/components/form/useGroupField.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/useGroupField.tsx
@@ -15,9 +15,9 @@
*/
/**
-*
-* @author Sebastian Javier Marchano (sebasjm)
-*/
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
import { useFormContext } from "./FormProvider.js";
@@ -27,14 +27,15 @@ interface Use {
export function useGroupField<T>(name: keyof T): Use {
const f = useFormContext<T>();
- if (!f)
- return {};
+ if (!f) return {};
return {
- hasError: readField(f.errors, String(name))
+ hasError: readField(f.errors, String(name)),
};
}
const readField = (object: any, name: string) => {
- return name.split('.').reduce((prev, current) => prev && prev[current], object)
-}
+ return name
+ .split(".")
+ .reduce((prev, current) => prev && prev[current], object);
+};