fix #7059
This commit is contained in:
parent
377e78e854
commit
38b84bb805
@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ComponentChildren, h, VNode } from "preact";
|
import { ComponentChildren, h, VNode } from "preact";
|
||||||
|
import { useLayoutEffect, useRef } from "preact/hooks";
|
||||||
// import { LoadingModal } from "../modal";
|
// import { LoadingModal } from "../modal";
|
||||||
import { useAsync } from "../hooks/async";
|
import { useAsync } from "../hooks/async";
|
||||||
// import { Translate } from "../../i18n";
|
// import { Translate } from "../../i18n";
|
||||||
@ -28,17 +29,26 @@ type Props = {
|
|||||||
children: ComponentChildren;
|
children: ComponentChildren;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
onClick?: () => Promise<void>;
|
onClick?: () => Promise<void>;
|
||||||
|
grabFocus?: boolean;
|
||||||
[rest: string]: any;
|
[rest: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function AsyncButton({
|
export function AsyncButton({
|
||||||
onClick,
|
onClick,
|
||||||
|
grabFocus,
|
||||||
disabled,
|
disabled,
|
||||||
children,
|
children,
|
||||||
...rest
|
...rest
|
||||||
}: Props): VNode {
|
}: Props): VNode {
|
||||||
const { isLoading, request } = useAsync(onClick);
|
const { isLoading, request } = useAsync(onClick);
|
||||||
|
|
||||||
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (grabFocus) {
|
||||||
|
buttonRef.current?.focus();
|
||||||
|
}
|
||||||
|
}, [grabFocus]);
|
||||||
|
|
||||||
// if (isSlow) {
|
// if (isSlow) {
|
||||||
// return <LoadingModal onCancel={cancel} />;
|
// return <LoadingModal onCancel={cancel} />;
|
||||||
// }
|
// }
|
||||||
@ -48,7 +58,7 @@ export function AsyncButton({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<span data-tooltip={rest["data-tooltip"]} style={{ marginLeft: 5 }}>
|
<span data-tooltip={rest["data-tooltip"]} style={{ marginLeft: 5 }}>
|
||||||
<button {...rest} onClick={request} disabled={disabled}>
|
<button {...rest} ref={buttonRef} onClick={request} disabled={disabled}>
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { format, isAfter, parse, sub, subYears } from "date-fns";
|
import { format, subYears } from "date-fns";
|
||||||
import { h, VNode } from "preact";
|
import { h, VNode } from "preact";
|
||||||
import { useLayoutEffect, useRef, useState } from "preact/hooks";
|
import { useLayoutEffect, useRef, useState } from "preact/hooks";
|
||||||
import { DatePicker } from "../picker/DatePicker";
|
import { DatePicker } from "../picker/DatePicker";
|
||||||
@ -9,6 +9,7 @@ export interface DateInputProps {
|
|||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
error?: string;
|
error?: string;
|
||||||
years?: Array<number>;
|
years?: Array<number>;
|
||||||
|
onConfirm?: () => void;
|
||||||
bind: [string, (x: string) => void];
|
bind: [string, (x: string) => void];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +45,12 @@ export function DateInput(props: DateInputProps): VNode {
|
|||||||
type="text"
|
type="text"
|
||||||
class={showError ? "input is-danger" : "input"}
|
class={showError ? "input is-danger" : "input"}
|
||||||
value={value}
|
value={value}
|
||||||
onInput={(e) => {
|
onKeyPress={(e) => {
|
||||||
|
if (e.key === 'Enter' && props.onConfirm) {
|
||||||
|
props.onConfirm()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onInput={(e) => {
|
||||||
const text = e.currentTarget.value;
|
const text = e.currentTarget.value;
|
||||||
setDirty(true);
|
setDirty(true);
|
||||||
props.bind[1](text);
|
props.bind[1](text);
|
||||||
|
@ -7,6 +7,7 @@ export interface TextInputProps {
|
|||||||
error?: string;
|
error?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
|
onConfirm?: () => void;
|
||||||
bind: [string, (x: string) => void];
|
bind: [string, (x: string) => void];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +38,11 @@ export function EmailInput(props: TextInputProps): VNode {
|
|||||||
placeholder={props.placeholder}
|
placeholder={props.placeholder}
|
||||||
type="email"
|
type="email"
|
||||||
class={showError ? "input is-danger" : "input"}
|
class={showError ? "input is-danger" : "input"}
|
||||||
|
onKeyPress={(e) => {
|
||||||
|
if (e.key === 'Enter' && props.onConfirm) {
|
||||||
|
props.onConfirm()
|
||||||
|
}
|
||||||
|
}}
|
||||||
onInput={(e) => {
|
onInput={(e) => {
|
||||||
setDirty(true);
|
setDirty(true);
|
||||||
props.bind[1]((e.target as HTMLInputElement).value);
|
props.bind[1]((e.target as HTMLInputElement).value);
|
||||||
|
@ -7,6 +7,7 @@ export interface TextInputProps {
|
|||||||
error?: string;
|
error?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
|
onConfirm?: () => void;
|
||||||
bind: [string, (x: string) => void];
|
bind: [string, (x: string) => void];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +37,11 @@ export function PhoneNumberInput(props: TextInputProps): VNode {
|
|||||||
type="tel"
|
type="tel"
|
||||||
placeholder={props.placeholder}
|
placeholder={props.placeholder}
|
||||||
class={showError ? "input is-danger" : "input"}
|
class={showError ? "input is-danger" : "input"}
|
||||||
|
onKeyPress={(e) => {
|
||||||
|
if (e.key === 'Enter' && props.onConfirm) {
|
||||||
|
props.onConfirm()
|
||||||
|
}
|
||||||
|
}}
|
||||||
onInput={(e) => {
|
onInput={(e) => {
|
||||||
setDirty(true);
|
setDirty(true);
|
||||||
props.bind[1]((e.target as HTMLInputElement).value);
|
props.bind[1]((e.target as HTMLInputElement).value);
|
||||||
|
@ -8,6 +8,7 @@ export interface TextInputProps {
|
|||||||
error?: string;
|
error?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
|
onConfirm?: () => void;
|
||||||
bind: [string, (x: string) => void];
|
bind: [string, (x: string) => void];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +38,11 @@ export function TextInput(props: TextInputProps): VNode {
|
|||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
placeholder={props.placeholder}
|
placeholder={props.placeholder}
|
||||||
class={showError ? "input is-danger" : "input"}
|
class={showError ? "input is-danger" : "input"}
|
||||||
|
onKeyPress={(e) => {
|
||||||
|
if (e.key === 'Enter' && props.onConfirm) {
|
||||||
|
props.onConfirm()
|
||||||
|
}
|
||||||
|
}}
|
||||||
onInput={(e) => {
|
onInput={(e) => {
|
||||||
setDirty(true);
|
setDirty(true);
|
||||||
props.bind[1]((e.target as HTMLInputElement).value);
|
props.bind[1]((e.target as HTMLInputElement).value);
|
||||||
|
@ -44,6 +44,11 @@ export function AttributeEntryScreen(): VNode {
|
|||||||
setValue={(v: string) => setAttrs({ ...attrs, [spec.name]: v })}
|
setValue={(v: string) => setAttrs({ ...attrs, [spec.name]: v })}
|
||||||
spec={spec}
|
spec={spec}
|
||||||
errorMessage={error}
|
errorMessage={error}
|
||||||
|
onConfirm={() => {
|
||||||
|
if (!hasErrors) {
|
||||||
|
setAskUserIfSure(true)
|
||||||
|
}
|
||||||
|
}}
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -104,6 +109,7 @@ interface AttributeEntryFieldProps {
|
|||||||
setValue: (newValue: string) => void;
|
setValue: (newValue: string) => void;
|
||||||
spec: UserAttributeSpec;
|
spec: UserAttributeSpec;
|
||||||
errorMessage: string | undefined;
|
errorMessage: string | undefined;
|
||||||
|
onConfirm: () => void;
|
||||||
}
|
}
|
||||||
const possibleBirthdayYear: Array<number> = [];
|
const possibleBirthdayYear: Array<number> = [];
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
@ -117,6 +123,7 @@ function AttributeEntryField(props: AttributeEntryFieldProps): VNode {
|
|||||||
grabFocus={props.isFirst}
|
grabFocus={props.isFirst}
|
||||||
label={props.spec.label}
|
label={props.spec.label}
|
||||||
years={possibleBirthdayYear}
|
years={possibleBirthdayYear}
|
||||||
|
onConfirm={props.onConfirm}
|
||||||
error={props.errorMessage}
|
error={props.errorMessage}
|
||||||
bind={[props.value, props.setValue]}
|
bind={[props.value, props.setValue]}
|
||||||
/>
|
/>
|
||||||
@ -125,6 +132,7 @@ function AttributeEntryField(props: AttributeEntryFieldProps): VNode {
|
|||||||
<PhoneNumberInput
|
<PhoneNumberInput
|
||||||
grabFocus={props.isFirst}
|
grabFocus={props.isFirst}
|
||||||
label={props.spec.label}
|
label={props.spec.label}
|
||||||
|
onConfirm={props.onConfirm}
|
||||||
error={props.errorMessage}
|
error={props.errorMessage}
|
||||||
bind={[props.value, props.setValue]}
|
bind={[props.value, props.setValue]}
|
||||||
/>
|
/>
|
||||||
@ -133,6 +141,7 @@ function AttributeEntryField(props: AttributeEntryFieldProps): VNode {
|
|||||||
<TextInput
|
<TextInput
|
||||||
grabFocus={props.isFirst}
|
grabFocus={props.isFirst}
|
||||||
label={props.spec.label}
|
label={props.spec.label}
|
||||||
|
onConfirm={props.onConfirm}
|
||||||
error={props.errorMessage}
|
error={props.errorMessage}
|
||||||
bind={[props.value, props.setValue]}
|
bind={[props.value, props.setValue]}
|
||||||
/>
|
/>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import { differenceInBusinessDays } from "date-fns";
|
||||||
import { ComponentChildren, h, VNode } from "preact";
|
import { ComponentChildren, h, VNode } from "preact";
|
||||||
|
import { useLayoutEffect, useRef } from "preact/hooks";
|
||||||
import { AsyncButton } from "../../components/AsyncButton";
|
import { AsyncButton } from "../../components/AsyncButton";
|
||||||
|
|
||||||
export interface ConfirmModelProps {
|
export interface ConfirmModelProps {
|
||||||
@ -17,7 +19,7 @@ export function ConfirmModal({
|
|||||||
active, description, onCancel, onConfirm, children, danger, disabled, label = "Confirm", cancelLabel = "Dismiss"
|
active, description, onCancel, onConfirm, children, danger, disabled, label = "Confirm", cancelLabel = "Dismiss"
|
||||||
}: ConfirmModelProps): VNode {
|
}: ConfirmModelProps): VNode {
|
||||||
return (
|
return (
|
||||||
<div class={active ? "modal is-active" : "modal"}>
|
<div class={active ? "modal is-active" : "modal"} >
|
||||||
<div class="modal-background " onClick={onCancel} />
|
<div class="modal-background " onClick={onCancel} />
|
||||||
<div class="modal-card" style={{ maxWidth: 700 }}>
|
<div class="modal-card" style={{ maxWidth: 700 }}>
|
||||||
<header class="modal-card-head">
|
<header class="modal-card-head">
|
||||||
@ -33,8 +35,11 @@ export function ConfirmModal({
|
|||||||
<button class="button" onClick={onCancel}>
|
<button class="button" onClick={onCancel}>
|
||||||
{cancelLabel}
|
{cancelLabel}
|
||||||
</button>
|
</button>
|
||||||
<div class="buttons is-right" style={{ width: "100%" }}>
|
<div class="buttons is-right" style={{ width: "100%" }} onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Escape' && onCancel) onCancel()
|
||||||
|
}}>
|
||||||
<AsyncButton
|
<AsyncButton
|
||||||
|
grabFocus
|
||||||
class={danger ? "button is-danger " : "button is-info "}
|
class={danger ? "button is-danger " : "button is-info "}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={onConfirm}
|
onClick={onConfirm}
|
||||||
|
@ -10,10 +10,12 @@ import { FileInput, FileTypeContent } from "../../components/fields/FileInput";
|
|||||||
export function SecretEditorScreen(): VNode {
|
export function SecretEditorScreen(): VNode {
|
||||||
const reducer = useAnastasisContext();
|
const reducer = useAnastasisContext();
|
||||||
const [secretValue, setSecretValue] = useState("");
|
const [secretValue, setSecretValue] = useState("");
|
||||||
const [secretFile, _setSecretFile] = useState<FileTypeContent | undefined>(undefined);
|
const [secretFile, _setSecretFile] = useState<FileTypeContent | undefined>(
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
function setSecretFile(v) {
|
function setSecretFile(v) {
|
||||||
setSecretValue("") // reset secret value when uploading a file
|
setSecretValue(""); // reset secret value when uploading a file
|
||||||
_setSecretFile(v)
|
_setSecretFile(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentSecretName =
|
const currentSecretName =
|
||||||
@ -34,14 +36,16 @@ export function SecretEditorScreen(): VNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const secretNext = async (): Promise<void> => {
|
const secretNext = async (): Promise<void> => {
|
||||||
const secret = secretFile ? {
|
const secret = secretFile
|
||||||
value: encodeCrock(stringToBytes(secretValue)),
|
? {
|
||||||
filename: secretFile.name,
|
value: encodeCrock(stringToBytes(secretValue)),
|
||||||
mime: secretFile.type,
|
filename: secretFile.name,
|
||||||
} : {
|
mime: secretFile.type,
|
||||||
value: encodeCrock(stringToBytes(secretValue)),
|
}
|
||||||
mime: "text/plain",
|
: {
|
||||||
}
|
value: encodeCrock(stringToBytes(secretValue)),
|
||||||
|
mime: "text/plain",
|
||||||
|
};
|
||||||
return reducer.runTransaction(async (tx) => {
|
return reducer.runTransaction(async (tx) => {
|
||||||
await tx.transition("enter_secret_name", {
|
await tx.transition("enter_secret_name", {
|
||||||
name: secretName,
|
name: secretName,
|
||||||
@ -55,9 +59,14 @@ export function SecretEditorScreen(): VNode {
|
|||||||
await tx.transition("next", {});
|
await tx.transition("next", {});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const errors = !secretName ? 'Add a secret name' : (
|
const errors = !secretName
|
||||||
(!secretValue && !secretFile) ? 'Add a secret value or a choose a file to upload' : undefined
|
? "Add a secret name"
|
||||||
)
|
: !secretValue && !secretFile
|
||||||
|
? "Add a secret value or a choose a file to upload"
|
||||||
|
: undefined;
|
||||||
|
function goNextIfNoErrors(): void {
|
||||||
|
if (!errors) secretNext();
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<AnastasisClientFrame
|
<AnastasisClientFrame
|
||||||
hideNext={errors}
|
hideNext={errors}
|
||||||
@ -69,12 +78,14 @@ export function SecretEditorScreen(): VNode {
|
|||||||
label="Secret name:"
|
label="Secret name:"
|
||||||
tooltip="The secret name allows you to identify your secret when restoring it. It is a label that you can choose freely."
|
tooltip="The secret name allows you to identify your secret when restoring it. It is a label that you can choose freely."
|
||||||
grabFocus
|
grabFocus
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
bind={[secretName, setSecretName]}
|
bind={[secretName, setSecretName]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<TextInput
|
<TextInput
|
||||||
disabled={!!secretFile}
|
disabled={!!secretFile}
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
label="Enter the secret as text:"
|
label="Enter the secret as text:"
|
||||||
bind={[secretValue, setSecretValue]}
|
bind={[secretValue, setSecretValue]}
|
||||||
/>
|
/>
|
||||||
@ -82,10 +93,12 @@ export function SecretEditorScreen(): VNode {
|
|||||||
<div class="block">
|
<div class="block">
|
||||||
Or upload a secret file
|
Or upload a secret file
|
||||||
<FileInput label="Choose file" onChange={setSecretFile} />
|
<FileInput label="Choose file" onChange={setSecretFile} />
|
||||||
{secretFile && <div>
|
{secretFile && (
|
||||||
Uploading secret file <b>{secretFile.name}</b> <a onClick={() => setSecretFile(undefined)}>cancel</a>
|
<div>
|
||||||
</div>
|
Uploading secret file <b>{secretFile.name}</b>{" "}
|
||||||
}
|
<a onClick={() => setSecretFile(undefined)}>cancel</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</AnastasisClientFrame>
|
</AnastasisClientFrame>
|
||||||
);
|
);
|
||||||
|
@ -26,6 +26,9 @@ export function AuthMethodEmailSetup({
|
|||||||
: undefined;
|
: undefined;
|
||||||
const errors = !email ? "Add your email" : emailError;
|
const errors = !email ? "Add your email" : emailError;
|
||||||
|
|
||||||
|
function goNextIfNoErrors(): void {
|
||||||
|
if (!errors) addEmailAuth();
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<AnastasisClientFrame hideNav title="Add email authentication">
|
<AnastasisClientFrame hideNav title="Add email authentication">
|
||||||
<p>
|
<p>
|
||||||
@ -37,6 +40,7 @@ export function AuthMethodEmailSetup({
|
|||||||
<EmailInput
|
<EmailInput
|
||||||
label="Email address"
|
label="Email address"
|
||||||
error={emailError}
|
error={emailError}
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
placeholder="email@domain.com"
|
placeholder="email@domain.com"
|
||||||
bind={[email, setEmail]}
|
bind={[email, setEmail]}
|
||||||
/>
|
/>
|
||||||
|
@ -10,7 +10,7 @@ import { AuthMethodSolveProps } from "./index";
|
|||||||
|
|
||||||
export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode {
|
export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode {
|
||||||
const [answer, setAnswer] = useState("");
|
const [answer, setAnswer] = useState("");
|
||||||
const [expanded, setExpanded] = useState(false)
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
|
||||||
const reducer = useAnastasisContext();
|
const reducer = useAnastasisContext();
|
||||||
if (!reducer) {
|
if (!reducer) {
|
||||||
@ -91,23 +91,38 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode {
|
|||||||
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
||||||
<p>
|
<p>
|
||||||
An email has been sent to "<b>{selectedChallenge.instructions}</b>". The
|
An email has been sent to "<b>{selectedChallenge.instructions}</b>". The
|
||||||
message has and identification code and recovery code that starts with "<b>A-</b>".
|
message has and identification code and recovery code that starts with "
|
||||||
Wait the message to arrive and the enter the recovery code below.
|
<b>A-</b>". Wait the message to arrive and the enter the recovery code
|
||||||
|
below.
|
||||||
</p>
|
</p>
|
||||||
{!expanded ? <p>
|
{!expanded ? (
|
||||||
The identification code in the email should start with "{selectedUuid.substring(0, 10)}"
|
<p>
|
||||||
<span class="icon has-tooltip-top" data-tooltip="click to expand" onClick={() => setExpanded(e => !e)}>
|
The identification code in the email should start with "
|
||||||
<i class="mdi mdi-information" />
|
{selectedUuid.substring(0, 10)}"
|
||||||
</span>
|
<span
|
||||||
</p>
|
class="icon has-tooltip-top"
|
||||||
: <p>
|
data-tooltip="click to expand"
|
||||||
The identification code in the email is "{selectedUuid}"
|
onClick={() => setExpanded((e) => !e)}
|
||||||
<span class="icon has-tooltip-top" data-tooltip="click to show less code" onClick={() => setExpanded(e => !e)}>
|
>
|
||||||
<i class="mdi mdi-information" />
|
<i class="mdi mdi-information" />
|
||||||
</span>
|
</span>
|
||||||
</p>}
|
</p>
|
||||||
<TextInput label="Answer"
|
) : (
|
||||||
|
<p>
|
||||||
|
The identification code in the email is "{selectedUuid}"
|
||||||
|
<span
|
||||||
|
class="icon has-tooltip-top"
|
||||||
|
data-tooltip="click to show less code"
|
||||||
|
onClick={() => setExpanded((e) => !e)}
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-information" />
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<TextInput
|
||||||
|
label="Answer"
|
||||||
grabFocus
|
grabFocus
|
||||||
|
onConfirm={onNext}
|
||||||
bind={[answer, setAnswer]}
|
bind={[answer, setAnswer]}
|
||||||
placeholder="A-1234567812345678"
|
placeholder="A-1234567812345678"
|
||||||
/>
|
/>
|
||||||
|
@ -36,6 +36,9 @@ export function AuthMethodIbanSetup({
|
|||||||
: !account
|
: !account
|
||||||
? "Add an account IBAN number"
|
? "Add an account IBAN number"
|
||||||
: undefined;
|
: undefined;
|
||||||
|
function goNextIfNoErrors(): void {
|
||||||
|
if (!errors) addIbanAuth();
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<AnastasisClientFrame hideNav title="Add bank transfer authentication">
|
<AnastasisClientFrame hideNav title="Add bank transfer authentication">
|
||||||
<p>
|
<p>
|
||||||
@ -49,11 +52,13 @@ export function AuthMethodIbanSetup({
|
|||||||
label="Bank account holder name"
|
label="Bank account holder name"
|
||||||
grabFocus
|
grabFocus
|
||||||
placeholder="John Smith"
|
placeholder="John Smith"
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
bind={[name, setName]}
|
bind={[name, setName]}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="IBAN"
|
label="IBAN"
|
||||||
placeholder="DE91100000000123456789"
|
placeholder="DE91100000000123456789"
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
bind={[account, setAccount]}
|
bind={[account, setAccount]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,6 +48,10 @@ export function AuthMethodPostSetup({
|
|||||||
: !country
|
: !country
|
||||||
? "The country is missing"
|
? "The country is missing"
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
function goNextIfNoErrors(): void {
|
||||||
|
if (!errors) addPostAuth();
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<AnastasisClientFrame hideNav title="Add postal authentication">
|
<AnastasisClientFrame hideNav title="Add postal authentication">
|
||||||
<p>
|
<p>
|
||||||
@ -56,19 +60,40 @@ export function AuthMethodPostSetup({
|
|||||||
will receive in a letter to that address.
|
will receive in a letter to that address.
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<TextInput grabFocus label="Full Name" bind={[fullName, setFullName]} />
|
<TextInput
|
||||||
|
grabFocus
|
||||||
|
label="Full Name"
|
||||||
|
bind={[fullName, setFullName]}
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TextInput label="Street" bind={[street, setStreet]} />
|
<TextInput
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
|
label="Street"
|
||||||
|
bind={[street, setStreet]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TextInput label="City" bind={[city, setCity]} />
|
<TextInput
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
|
label="City"
|
||||||
|
bind={[city, setCity]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TextInput label="Postal Code" bind={[postcode, setPostcode]} />
|
<TextInput
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
|
label="Postal Code"
|
||||||
|
bind={[postcode, setPostcode]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TextInput label="Country" bind={[country, setCountry]} />
|
<TextInput
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
|
label="Country"
|
||||||
|
bind={[country, setCountry]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{configured.length > 0 && (
|
{configured.length > 0 && (
|
||||||
|
@ -89,7 +89,12 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode {
|
|||||||
<AnastasisClientFrame hideNav title="Postal Challenge">
|
<AnastasisClientFrame hideNav title="Postal Challenge">
|
||||||
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
||||||
<p>Wait for the answer</p>
|
<p>Wait for the answer</p>
|
||||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
<TextInput
|
||||||
|
onConfirm={onNext}
|
||||||
|
label="Answer"
|
||||||
|
grabFocus
|
||||||
|
bind={[answer, setAnswer]}
|
||||||
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
@ -26,6 +26,9 @@ export function AuthMethodQuestionSetup({
|
|||||||
: !answerText
|
: !answerText
|
||||||
? "Add the answer to your question"
|
? "Add the answer to your question"
|
||||||
: undefined;
|
: undefined;
|
||||||
|
function goNextIfNoErrors(): void {
|
||||||
|
if (!errors) addQuestionAuth();
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<AnastasisClientFrame hideNav title="Add Security Question">
|
<AnastasisClientFrame hideNav title="Add Security Question">
|
||||||
<div>
|
<div>
|
||||||
@ -39,6 +42,7 @@ export function AuthMethodQuestionSetup({
|
|||||||
<TextInput
|
<TextInput
|
||||||
label="Security question"
|
label="Security question"
|
||||||
grabFocus
|
grabFocus
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
placeholder="Your question"
|
placeholder="Your question"
|
||||||
bind={[questionText, setQuestionText]}
|
bind={[questionText, setQuestionText]}
|
||||||
/>
|
/>
|
||||||
@ -46,6 +50,7 @@ export function AuthMethodQuestionSetup({
|
|||||||
<div>
|
<div>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Answer"
|
label="Answer"
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
placeholder="Your answer"
|
placeholder="Your answer"
|
||||||
bind={[answerText, setAnswerText]}
|
bind={[answerText, setAnswerText]}
|
||||||
/>
|
/>
|
||||||
|
@ -91,11 +91,14 @@ export function AuthMethodQuestionSolve({ id }: AuthMethodSolveProps): VNode {
|
|||||||
<p>
|
<p>
|
||||||
In this challenge you need to provide the answer for the next question:
|
In this challenge you need to provide the answer for the next question:
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<pre>{selectedChallenge.instructions}</pre>
|
||||||
{selectedChallenge.instructions}
|
|
||||||
</pre>
|
|
||||||
<p>Type the answer below</p>
|
<p>Type the answer below</p>
|
||||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
<TextInput
|
||||||
|
label="Answer"
|
||||||
|
onConfirm={onNext}
|
||||||
|
grabFocus
|
||||||
|
bind={[answer, setAnswer]}
|
||||||
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
@ -25,6 +25,9 @@ export function AuthMethodSmsSetup({
|
|||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
}, []);
|
}, []);
|
||||||
const errors = !mobileNumber ? "Add a mobile number" : undefined;
|
const errors = !mobileNumber ? "Add a mobile number" : undefined;
|
||||||
|
function goNextIfNoErrors(): void {
|
||||||
|
if (!errors) addSmsAuth();
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<AnastasisClientFrame hideNav title="Add SMS authentication">
|
<AnastasisClientFrame hideNav title="Add SMS authentication">
|
||||||
<div>
|
<div>
|
||||||
@ -37,6 +40,7 @@ export function AuthMethodSmsSetup({
|
|||||||
<PhoneNumberInput
|
<PhoneNumberInput
|
||||||
label="Mobile number"
|
label="Mobile number"
|
||||||
placeholder="Your mobile number"
|
placeholder="Your mobile number"
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
grabFocus
|
grabFocus
|
||||||
bind={[mobileNumber, setMobileNumber]}
|
bind={[mobileNumber, setMobileNumber]}
|
||||||
/>
|
/>
|
||||||
|
@ -11,7 +11,7 @@ import { AuthMethodSolveProps } from "./index";
|
|||||||
export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode {
|
export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode {
|
||||||
const [answer, setAnswer] = useState("");
|
const [answer, setAnswer] = useState("");
|
||||||
|
|
||||||
const [expanded, setExpanded] = useState(false)
|
const [expanded, setExpanded] = useState(false);
|
||||||
const reducer = useAnastasisContext();
|
const reducer = useAnastasisContext();
|
||||||
if (!reducer) {
|
if (!reducer) {
|
||||||
return (
|
return (
|
||||||
@ -91,23 +91,38 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode {
|
|||||||
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
||||||
<p>
|
<p>
|
||||||
An sms has been sent to "<b>{selectedChallenge.instructions}</b>". The
|
An sms has been sent to "<b>{selectedChallenge.instructions}</b>". The
|
||||||
message has and identification code and recovery code that starts with "<b>A-</b>".
|
message has and identification code and recovery code that starts with "
|
||||||
Wait the message to arrive and the enter the recovery code below.
|
<b>A-</b>". Wait the message to arrive and the enter the recovery code
|
||||||
|
below.
|
||||||
</p>
|
</p>
|
||||||
{!expanded ? <p>
|
{!expanded ? (
|
||||||
The identification code in the SMS should start with "{selectedUuid.substring(0, 10)}"
|
<p>
|
||||||
<span class="icon has-tooltip-top" data-tooltip="click to expand" onClick={() => setExpanded(e => !e)}>
|
The identification code in the SMS should start with "
|
||||||
<i class="mdi mdi-information" />
|
{selectedUuid.substring(0, 10)}"
|
||||||
</span>
|
<span
|
||||||
</p>
|
class="icon has-tooltip-top"
|
||||||
: <p>
|
data-tooltip="click to expand"
|
||||||
The identification code in the SMS is "{selectedUuid}"
|
onClick={() => setExpanded((e) => !e)}
|
||||||
<span class="icon has-tooltip-top" data-tooltip="click to show less code" onClick={() => setExpanded(e => !e)}>
|
>
|
||||||
<i class="mdi mdi-information" />
|
<i class="mdi mdi-information" />
|
||||||
</span>
|
</span>
|
||||||
</p>}
|
</p>
|
||||||
<TextInput label="Answer"
|
) : (
|
||||||
|
<p>
|
||||||
|
The identification code in the SMS is "{selectedUuid}"
|
||||||
|
<span
|
||||||
|
class="icon has-tooltip-top"
|
||||||
|
data-tooltip="click to show less code"
|
||||||
|
onClick={() => setExpanded((e) => !e)}
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-information" />
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<TextInput
|
||||||
|
label="Answer"
|
||||||
grabFocus
|
grabFocus
|
||||||
|
onConfirm={onNext}
|
||||||
bind={[answer, setAnswer]}
|
bind={[answer, setAnswer]}
|
||||||
placeholder="A-1234567812345678"
|
placeholder="A-1234567812345678"
|
||||||
/>
|
/>
|
||||||
|
@ -38,6 +38,9 @@ export function AuthMethodTotpSetup({
|
|||||||
: !testCodeMatches
|
: !testCodeMatches
|
||||||
? "The test code doesnt match"
|
? "The test code doesnt match"
|
||||||
: undefined;
|
: undefined;
|
||||||
|
function goNextIfNoErrors(): void {
|
||||||
|
if (!errors) addTotpAuth();
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<AnastasisClientFrame hideNav title="Add TOTP authentication">
|
<AnastasisClientFrame hideNav title="Add TOTP authentication">
|
||||||
<p>
|
<p>
|
||||||
@ -54,7 +57,11 @@ export function AuthMethodTotpSetup({
|
|||||||
<p>
|
<p>
|
||||||
After scanning the code with your TOTP App, test it in the input below.
|
After scanning the code with your TOTP App, test it in the input below.
|
||||||
</p>
|
</p>
|
||||||
<TextInput label="Test code" bind={[test, setTest]} />
|
<TextInput
|
||||||
|
label="Test code"
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
|
bind={[test, setTest]}
|
||||||
|
/>
|
||||||
{configured.length > 0 && (
|
{configured.length > 0 && (
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="block">Your TOTP numbers:</div>
|
<div class="block">Your TOTP numbers:</div>
|
||||||
|
@ -89,7 +89,12 @@ export function AuthMethodTotpSolve({ id }: AuthMethodSolveProps): VNode {
|
|||||||
<AnastasisClientFrame hideNav title="TOTP Challenge">
|
<AnastasisClientFrame hideNav title="TOTP Challenge">
|
||||||
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
<SolveOverviewFeedbackDisplay feedback={feedback} />
|
||||||
<p>enter the totp solution</p>
|
<p>enter the totp solution</p>
|
||||||
<TextInput label="Answer" grabFocus bind={[answer, setAnswer]} />
|
<TextInput
|
||||||
|
label="Answer"
|
||||||
|
onConfirm={onNext}
|
||||||
|
grabFocus
|
||||||
|
bind={[answer, setAnswer]}
|
||||||
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
@ -20,6 +20,9 @@ export function AuthMethodVideoSetup({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
function goNextIfNoErrors(): void {
|
||||||
|
addVideoAuth();
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<AnastasisClientFrame hideNav title="Add video authentication">
|
<AnastasisClientFrame hideNav title="Add video authentication">
|
||||||
<p>
|
<p>
|
||||||
@ -32,6 +35,7 @@ export function AuthMethodVideoSetup({
|
|||||||
<ImageInput
|
<ImageInput
|
||||||
label="Choose photograph"
|
label="Choose photograph"
|
||||||
grabFocus
|
grabFocus
|
||||||
|
onConfirm={goNextIfNoErrors}
|
||||||
bind={[image, setImage]}
|
bind={[image, setImage]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user