react-hook-form#SubmitHandler TypeScript Examples
The following examples show how to use
react-hook-form#SubmitHandler.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: pricing-form-context.tsx From admin with MIT License | 6 votes |
PriceListFormContext = React.createContext<{
configFields: Record<ConfigurationField, unknown>
handleConfigurationSwitch: (values: string[]) => void
prices: CreatePriceListPricesFormValues | null
setPrices: React.Dispatch<
React.SetStateAction<CreatePriceListPricesFormValues | null>
>
handleSubmit: <T>(
submitHandler: SubmitHandler<T>
) => (e?: React.BaseSyntheticEvent) => Promise<void>
} | null>(null)
Example #2
Source File: index.tsx From github-explorer with MIT License | 5 votes |
export function Dashboard() {
const {
errors,
setError,
register,
formState,
handleSubmit,
} = useForm({
mode: "onChange",
resolver: yupResolver(addRepositorySchema),
});
const { repositories, addRepository, isLoading } = useRepositories();
const [t] = useTranslation();
const onSubmit: SubmitHandler<RepositoryFormValues> = async (
data,
) => {
const { repositoryName } = data;
try {
await addRepository(repositoryName);
} catch (error) {
setError("repositoryName", {
type: "manual",
message: error.message,
});
}
};
const { isDirty, isValid } = formState;
const { repositoryName: repositoryNameError } = errors;
const isButtonDisabled = isLoading || !isValid || !isDirty;
const hasError = Boolean(repositoryNameError?.message) && !isValid && isDirty;
return (
<Layout>
<Title>{t("dashboard.title")}</Title>
<Form onSubmit={handleSubmit(onSubmit)}>
<Input
id="repositoryName"
ref={register}
type="text"
name="repositoryName"
hasError={hasError}
aria-label="Repository Name"
placeholder={t("dashboard.repository_name_placeholder")}
/>
<Button
type="submit"
isLoading={isLoading}
disabled={isButtonDisabled}
>
{t("buttons.search")}
</Button>
</Form>
{repositoryNameError && (
<AddRepositoryInputError>
{repositoryNameError.message}
</AddRepositoryInputError>
)}
<RepositoriesList>
{repositories.map(repository => (
<li key={repository.full_name}>
<Repository repository={repository} />
</li>
))}
</RepositoriesList>
</Layout>
);
}
Example #3
Source File: AddShortcut.tsx From backstage with Apache License 2.0 | 5 votes |
AddShortcut = ({ onClose, anchorEl, api }: Props) => {
const classes = useStyles();
const alertApi = useApi(alertApiRef);
const { pathname } = useLocation();
const [formValues, setFormValues] = useState<FormValues>();
const open = Boolean(anchorEl);
const handleSave: SubmitHandler<FormValues> = async ({ url, title }) => {
const shortcut: Omit<Shortcut, 'id'> = { url, title };
try {
await api.add(shortcut);
alertApi.post({
message: `Added shortcut '${title}' to your sidebar`,
severity: 'success',
});
} catch (error) {
alertApi.post({
message: `Could not add shortcut: ${error.message}`,
severity: 'error',
});
}
onClose();
};
const handlePaste = () => {
setFormValues({ url: pathname, title: document.title });
};
const handleClose = () => {
setFormValues(undefined);
onClose();
};
return (
<Popover
open={open}
anchorEl={anchorEl}
TransitionProps={{ onExit: handleClose }}
onClose={onClose}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
>
<Card className={classes.card}>
<CardHeader
className={classes.header}
title="Add Shortcut"
titleTypographyProps={{ variant: 'subtitle2' }}
action={
<Button
className={classes.button}
variant="text"
size="small"
color="primary"
onClick={handlePaste}
>
Use current page
</Button>
}
/>
<ShortcutForm
onClose={handleClose}
onSave={handleSave}
formValues={formValues}
/>
</Card>
</Popover>
);
}
Example #4
Source File: SendLN.tsx From raspiblitz-web with MIT License | 5 votes |
SendLn: FC<Props> = ({
loading,
balanceDecorated,
onConfirm,
onChangeInvoice,
error,
}) => {
const { unit } = useContext(AppContext);
const { t } = useTranslation();
const {
register,
handleSubmit,
formState: { errors, isValid, submitCount },
} = useForm<IFormInputs>({
mode: "onChange",
});
const onSubmit: SubmitHandler<IFormInputs> = (_) => onConfirm();
return (
<form onSubmit={handleSubmit(onSubmit)}>
<h3 className="text-xl font-bold">{t("wallet.send_lightning")}</h3>
<p className="my-5">
<span className="font-bold">{t("wallet.balance")}: </span>
{balanceDecorated} {unit}
</p>
<InputField
{...register("invoiceInput", {
required: t("forms.validation.lnInvoice.required"),
pattern: {
value: /^(lnbc|lntb)\w+/i,
message: t("forms.validation.lnInvoice.patternMismatch"),
},
onChange: onChangeInvoice,
})}
label={t("wallet.invoice")}
errorMessage={errors.invoiceInput}
placeholder="lnbc..."
disabled={loading}
/>
{error && <ErrorMessage errorMessage={error} />}
<ButtonWithSpinner
type="submit"
className="bd-button my-8 p-3"
loading={loading}
disabled={(submitCount > 0 && !isValid) || loading}
icon={<SendIcon className="mr-2 h-6 w-6" />}
>
{t("wallet.send")}
</ButtonWithSpinner>
</form>
);
}
Example #5
Source File: UnlockModal.tsx From raspiblitz-web with MIT License | 5 votes |
UnlockModal: FC<Props> = ({ onClose }) => {
const { t } = useTranslation();
const { setWalletLocked } = useContext(AppContext);
const [isLoading, setIsLoading] = useState(false);
const [passwordWrong, setPasswordWrong] = useState(false);
const {
register,
handleSubmit,
formState: { errors, isValid },
} = useForm<IFormInputs>({ mode: "onChange" });
const unlockHandler: SubmitHandler<IFormInputs> = (data: {
passwordInput: string;
}) => {
setIsLoading(true);
setPasswordWrong(false);
instance
.post("/lightning/unlock-wallet", { password: data.passwordInput })
.then((res) => {
if (res.data) {
setWalletLocked(false);
// disableScroll doesn't trigger on modal close
disableScroll.off();
onClose(true);
}
})
.catch((_) => {
setIsLoading(false);
setPasswordWrong(true);
});
};
return createPortal(
<ModalDialog closeable={false} close={() => onClose(false)}>
<h2 className="mt-5 text-lg font-bold">{t("wallet.unlock_title")}</h2>
<div>
<h3 className="p-2">{t("wallet.unlock_subtitle")}</h3>
<form onSubmit={handleSubmit(unlockHandler)}>
<InputField
{...register("passwordInput", {
required: t("forms.validation.unlock.required"),
})}
autoFocus
errorMessage={errors.passwordInput}
label={t("forms.validation.unlock.pass_c")}
placeholder={t("forms.validation.unlock.pass_c")}
type="password"
disabled={isLoading}
/>
<ButtonWithSpinner
type="submit"
className="bd-button my-5 p-3"
loading={isLoading}
disabled={!isValid}
icon={<LockOpen className="mx-1 h-6 w-6" />}
>
{isLoading ? t("wallet.unlocking") : t("wallet.unlock")}
</ButtonWithSpinner>
</form>
</div>
{passwordWrong && (
<p className="mb-5 text-red-500">{t("login.invalid_pass")}</p>
)}
</ModalDialog>,
MODAL_ROOT
);
}
Example #6
Source File: Onboarding.tsx From revite with GNU Affero General Public License v3.0 | 5 votes |
export function OnboardingModal({ onClose, callback }: Props) {
const { handleSubmit, register } = useForm<FormInputs>();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | undefined>(undefined);
const onSubmit: SubmitHandler<FormInputs> = ({ username }) => {
setLoading(true);
callback(username, true)
.then(() => onClose())
.catch((err: unknown) => {
setError(takeError(err));
setLoading(false);
});
};
return (
<div className={styles.onboarding}>
<div className={styles.header}>
<h1>
<Text id="app.special.modals.onboarding.welcome" />
<br />
<img src={wideSVG} loading="eager" />
</h1>
</div>
<div className={styles.form}>
{loading ? (
<Preloader type="spinner" />
) : (
<>
<p>
<Text id="app.special.modals.onboarding.pick" />
</p>
<form
onSubmit={
handleSubmit(
onSubmit,
) as unknown as JSX.GenericEventHandler<HTMLFormElement>
}>
<div>
<FormField
type="username"
register={register}
showOverline
error={error}
/>
</div>
<Button type="submit">
<Text id="app.special.modals.actions.continue" />
</Button>
</form>
</>
)}
</div>
<div />
</div>
);
}
Example #7
Source File: CreateBot.tsx From revite with GNU Affero General Public License v3.0 | 5 votes |
export function CreateBotModal({ onClose, onCreate }: Props) {
const client = useContext(AppContext);
const { handleSubmit, register, errors } = useForm<FormInputs>();
const [error, setError] = useState<string | undefined>(undefined);
const onSubmit: SubmitHandler<FormInputs> = async ({ name }) => {
try {
const { bot } = await client.bots.create({ name });
onCreate(bot);
onClose();
} catch (err) {
setError(takeError(err));
}
};
return (
<Modal
visible={true}
onClose={onClose}
title={<Text id="app.special.popovers.create_bot.title" />}
actions={[
{
confirmation: true,
palette: "accent",
onClick: handleSubmit(onSubmit),
children: <Text id="app.special.modals.actions.create" />,
},
{
palette: "plain",
onClick: onClose,
children: <Text id="app.special.modals.actions.cancel" />,
},
]}>
{/* Preact / React typing incompatabilities */}
<form
onSubmit={(e) => {
e.preventDefault();
handleSubmit(
onSubmit,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
)(e as any);
}}>
<FormField
type="username"
name="name"
register={register}
showOverline
error={errors.name?.message}
/>
{error && (
<Overline type="error" error={error}>
<Text id="app.special.popovers.create_bot.failed" />
</Overline>
)}
</form>
</Modal>
);
}
Example #8
Source File: FormContainer.tsx From UsTaxes with GNU Affero General Public License v3.0 | 5 votes |
OpenableFormContainer = <A,>(
props: PropsWithChildren<OpenableFormContainerProps<A>>
): ReactElement => {
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)')
const { isOpen = false, allowAdd = true, defaultValues } = props
const classes = useStyles()
// Note useFormContext here instead of useForm reuses the
// existing form context from the parent.
const { reset, handleSubmit } = useFormContext()
const closeForm = (): void => {
props.onOpenStateChange(false)
reset(defaultValues)
}
const onClose = (): void => {
if (props.onCancel !== undefined) props.onCancel()
closeForm()
}
const onSave: SubmitHandler<A> = (formData): void => {
props.onSave(formData)
closeForm()
}
const openAddForm = () => {
props.onOpenStateChange(true)
}
return (
<>
{(() => {
if (isOpen) {
return (
<FormContainer
onDone={intentionallyFloat(handleSubmit(onSave))}
onCancel={onClose}
>
{props.children}
</FormContainer>
)
} else if (allowAdd) {
return (
<div className={classes.buttonList}>
<Button
type="button"
onClick={openAddForm}
color={prefersDarkMode ? 'default' : 'secondary'}
variant="contained"
>
Add
</Button>
</div>
)
}
})()}
</>
)
}
Example #9
Source File: note-form.tsx From notebook with MIT License | 4 votes |
NoteForm: React.SFC<NoteFormProps> = ({
isOpen,
onClose,
selectedNote,
handleNoteCreate,
handleNoteUpdate
}) => {
const { register, handleSubmit, formState, errors } = useForm<FormInputs>({
mode: "onChange"
});
const onSubmit: SubmitHandler<FormInputs> = data => {
let newNote: note = {
id: "",
title: data.title,
body: data.body
};
if (handleNoteCreate) {
newNote.id = nanoid();
if (handleNoteCreate) handleNoteCreate(newNote);
} else {
newNote.id = selectedNote ? selectedNote.id : "";
if (handleNoteUpdate) handleNoteUpdate(newNote);
}
onClose();
};
const validateTitle = (value: string) => {
if (!value) {
return "Title is required";
} else return true;
};
const validateBody = (value: string) => {
if (!value) {
return "Body is required";
} else return true;
};
return (
<Modal
isOpen={isOpen}
onClose={onClose}
size="lg"
isCentered
motionPreset="slideInBottom"
>
<ModalOverlay />
<ModalContent>
<form onSubmit={handleSubmit(onSubmit)}>
<ModalHeader>{selectedNote ? "Edit" : "Create"} a Note</ModalHeader>
<ModalCloseButton />
<ModalBody pb={6}>
<FormControl isInvalid={!!errors?.title} isRequired>
<FormLabel>Title</FormLabel>
<Input
name="title"
placeholder="Title"
defaultValue={selectedNote?.title}
ref={register({ validate: validateTitle })}
/>
<FormErrorMessage>
{!!errors?.title && errors?.title?.message}
</FormErrorMessage>
</FormControl>
<FormControl size="lg" mt={4} isInvalid={!!errors?.body} isRequired>
<FormLabel>Body</FormLabel>
<Textarea
name="body"
placeholder="Body"
size="md"
borderRadius="5px"
defaultValue={selectedNote?.body}
ref={register({ validate: validateBody })}
/>
<FormErrorMessage>
{!!errors?.body && errors?.body?.message}
</FormErrorMessage>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
type="submit"
colorScheme="blue"
isLoading={formState.isSubmitting}
mr={3}
>
Save
</Button>
<Button onClick={onClose}>Cancel</Button>
</ModalFooter>
</form>
</ModalContent>
</Modal>
);
}
Example #10
Source File: EditShortcut.tsx From backstage with Apache License 2.0 | 4 votes |
EditShortcut = ({ shortcut, onClose, anchorEl, api }: Props) => {
const classes = useStyles();
const alertApi = useApi(alertApiRef);
const open = Boolean(anchorEl);
const handleSave: SubmitHandler<FormValues> = async ({ url, title }) => {
const newShortcut: Shortcut = {
...shortcut,
url,
title,
};
try {
await api.update(newShortcut);
alertApi.post({
message: `Updated shortcut '${title}'`,
severity: 'success',
});
} catch (error) {
alertApi.post({
message: `Could not update shortcut: ${error.message}`,
severity: 'error',
});
}
onClose();
};
const handleRemove = async () => {
try {
await api.remove(shortcut.id);
alertApi.post({
message: `Removed shortcut '${shortcut.title}' from your sidebar`,
severity: 'success',
});
} catch (error) {
alertApi.post({
message: `Could not delete shortcut: ${error.message}`,
severity: 'error',
});
}
};
const handleClose = () => {
onClose();
};
return (
<Popover
open={open}
anchorEl={anchorEl}
onClose={onClose}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
>
<Card className={classes.card}>
<CardHeader
className={classes.header}
title="Edit Shortcut"
titleTypographyProps={{ variant: 'subtitle2' }}
action={
<Button
className={classes.button}
variant="text"
size="small"
color="secondary"
startIcon={<DeleteIcon />}
onClick={handleRemove}
>
Remove
</Button>
}
/>
<ShortcutForm
formValues={{ url: shortcut.url, title: shortcut.title }}
onClose={handleClose}
onSave={handleSave}
/>
</Card>
</Popover>
);
}
Example #11
Source File: ReceiveModal.tsx From raspiblitz-web with MIT License | 4 votes |
ReceiveModal: FC<Props> = ({ onClose }) => {
const { unit } = useContext(AppContext);
const { t } = useTranslation();
const [invoiceType, setInvoiceType] = useState(TxType.LIGHTNING);
const [address, setAddress] = useState("");
const [amount, setAmount] = useState(0);
const [comment, setComment] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [copyAddress, addressCopied] = useClipboard(address);
const [error, setError] = useState("");
const lnInvoice = invoiceType === TxType.LIGHTNING;
const changeInvoiceHandler = async (txType: TxType) => {
setAddress("");
setAmount(0);
setComment("");
setError("");
setInvoiceType(txType);
if (txType === TxType.ONCHAIN) {
setIsLoading(true);
await instance
.post("lightning/new-address", {
type: "p2wkh",
})
.then((resp) => {
setAddress(resp.data);
})
.catch((err) => {
setError(`${t("login.error")}: ${err.response?.data?.detail}`);
})
.finally(() => {
setIsLoading(false);
});
}
};
const commentChangeHandler = (event: ChangeEvent<HTMLInputElement>) => {
setComment(event.target.value);
};
const amountChangeHandler = (event: ChangeEvent<HTMLInputElement>) => {
setAmount(+event.target.value);
};
const generateInvoiceHandler = async () => {
setIsLoading(true);
const mSatAmount =
unit === Unit.BTC ? convertBtcToSat(amount) * 1000 : amount * 1000;
await instance
.post(`lightning/add-invoice?value_msat=${mSatAmount}&memo=${comment}`)
.then((resp) => {
setAddress(resp.data.payment_request);
})
.catch((err) => {
setError(`${t("login.error")}: ${err.response?.data?.detail}`);
})
.finally(() => {
setIsLoading(false);
});
};
const showLnInvoice = lnInvoice && !isLoading;
const {
register,
handleSubmit,
formState: { errors, isValid, submitCount },
} = useForm<IFormInputs>({
mode: "onChange",
});
const onSubmit: SubmitHandler<IFormInputs> = (_data) =>
generateInvoiceHandler();
return createPortal(
<ModalDialog close={onClose}>
{showLnInvoice && (
<div className="text-xl font-bold">{t("wallet.create_invoice_ln")}</div>
)}
{!showLnInvoice && (
<div className="text-xl font-bold">{t("wallet.fund")}</div>
)}
<div className="my-3">
<SwitchTxType
invoiceType={invoiceType}
onTxTypeChange={changeInvoiceHandler}
/>
</div>
{address && (
<>
<div className="my-5 flex justify-center">
<QRCodeSVG value={address} size={256} />
</div>
<p className="my-5 text-sm text-gray-500 dark:text-gray-300">
{t("wallet.scan_qr")}
</p>
</>
)}
<form
className="flex w-full flex-col items-center"
onSubmit={handleSubmit(onSubmit)}
>
<fieldset className="mb-5 w-4/5">
{isLoading && (
<div className="p-5">
<LoadingSpinner />
</div>
)}
{showLnInvoice && !address && (
<div className="flex flex-col justify-center pb-5 text-center">
<AmountInput
amount={amount}
register={register("amountInput", {
required: t("forms.validation.chainAmount.required"),
validate: {
greaterThanZero: () =>
amount > 0 || t("forms.validation.chainAmount.required"),
},
onChange: amountChangeHandler,
})}
errorMessage={errors.amountInput}
/>
<div className="mt-2 flex flex-col justify-center">
<InputField
{...register("commentInput", {
onChange: commentChangeHandler,
})}
label={t("tx.comment")}
value={comment}
placeholder={t("tx.comment_placeholder")}
/>
</div>
</div>
)}
{error && <ErrorMessage errorMessage={error} />}
{!address && showLnInvoice && (
<button
type="submit"
className="bd-button my-3 p-3"
disabled={submitCount > 0 && !isValid}
>
{t("wallet.create_invoice")}
</button>
)}
</fieldset>
</form>
{address && (
<>
<article className="mb-5 flex flex-row items-center">
<Tooltip
overlay={
<div>
{addressCopied
? t("wallet.copied")
: t("wallet.copy_clipboard")}
</div>
}
placement="top"
>
<p
onClick={copyAddress}
className="m-2 w-full break-all text-gray-600 dark:text-white"
>
{address}
</p>
</Tooltip>
</article>
</>
)}
</ModalDialog>,
MODAL_ROOT
);
}
Example #12
Source File: SendOnChain.tsx From raspiblitz-web with MIT License | 4 votes |
SendOnChain: FC<Props> = ({
amount,
address,
balance,
comment,
fee,
onChangeAmount,
onChangeAddress,
onChangeComment,
onChangeFee,
onConfirm,
}) => {
const { t } = useTranslation();
const { unit } = useContext(AppContext);
const convertedBalance =
unit === Unit.BTC ? convertSatToBtc(balance) : balance;
const balanceDecorated =
unit === Unit.BTC
? convertToString(unit, convertedBalance)
: convertToString(unit, balance);
const {
register,
handleSubmit,
formState: { errors, isValid, submitCount },
} = useForm<IFormInputs>({
mode: "onChange",
});
const onSubmit: SubmitHandler<IFormInputs> = (_data) => onConfirm();
return (
<form className="px-5" onSubmit={handleSubmit(onSubmit)}>
<h3 className="text-xl font-bold">{t("wallet.send_onchain")}</h3>
<p className="my-5">
<span className="font-bold">{t("wallet.balance")}: </span>
{balanceDecorated} {unit}
</p>
<fieldset className="my-5 flex flex-col items-center justify-center text-center">
<div className="w-full py-1 md:w-10/12">
<InputField
{...register("addressInput", {
required: t("forms.validation.chainAddress.required"),
pattern: {
value: /^(1|3|bc1|tb1|tpub|bcrt)\w+/i,
message: t("forms.validation.chainAddress.patternMismatch"),
},
onChange: onChangeAddress,
})}
placeholder="bc1..."
label={t("wallet.address")}
errorMessage={errors.addressInput}
value={address}
/>
</div>
<div className="w-full py-1 md:w-10/12">
<AmountInput
amount={amount}
errorMessage={errors?.amountInput}
register={register("amountInput", {
required: t("forms.validation.chainAmount.required"),
max: {
value: convertedBalance || 0,
message: t("forms.validation.chainAmount.max"),
},
validate: {
greaterThanZero: () =>
amount > 0 || t("forms.validation.chainAmount.required"),
},
onChange: onChangeAmount,
})}
/>
</div>
<div className="w-full py-1 md:w-10/12">
<InputField
{...register("feeInput", {
required: t("forms.validation.chainFee.required"),
onChange: onChangeFee,
})}
label={t("tx.fee")}
errorMessage={errors.feeInput}
value={fee}
inputRightAddon="sat / vByte"
type="number"
/>
</div>
<div className="w-full py-1 md:w-10/12">
<InputField
{...register("commentInput", {
onChange: onChangeComment,
})}
label={t("tx.comment")}
value={comment}
placeholder={t("tx.comment_placeholder")}
/>
</div>
</fieldset>
<div className="mb-5 inline-block w-4/5 align-top lg:w-3/12">
<button
type="submit"
className="bd-button my-3 p-3"
disabled={submitCount > 0 && !isValid}
>
{t("wallet.confirm")}
</button>
</div>
</form>
);
}
Example #13
Source File: useMessageDialog.tsx From jobsgowhere with MIT License | 4 votes |
MessageDialogContainer: React.FC = () => {
const messageDialogRef = React.useRef<HTMLDivElement | null>(null);
const parameters = {} as MessageDialogParameters;
const [messageDialogParameters, setMessageDialogParameters] = React.useState(parameters);
const [shouldShowDialog, setShowDialog] = React.useState(false);
const { handleSubmit, register, errors, setValue } = useForm();
setMessageDialog = setMessageDialogParameters;
showMessageDialog = setShowDialog;
React.useEffect(() => {
const node = document.createElement("div");
document.body.appendChild(node);
messageDialogRef.current = node;
register("message", {
required: "Please enter a message",
minLength: {
value: 3,
message: "Please enter a message with a minimum of 3 characters",
},
});
}, [register]);
const onSubmit: SubmitHandler<FormValues> = (values) => {
const receiverId = messageDialogParameters.job_poster.id;
const subject = `${messageDialogParameters.current_user.first_name} ${messageDialogParameters.current_user.last_name} connected with you`;
const body = values.message;
const postTitle = messageDialogParameters.position.job_title;
ApiClient.post(`${process.env.REACT_APP_API}/sendmessage`, {
to: receiverId,
subject,
body,
postTitle,
})
.then(() => {
toast("? Good Job! Message Sent! Check your email for replies.");
showMessageDialog(false);
})
.catch((err) => {
toast(`❗️ Error: ${err.response.data.error_description}`);
});
};
const handleTextAreaChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
setValue("message", event.target.value);
};
function markup() {
if (!shouldShowDialog) return null;
return (
<StyledMessageDialogHolder>
<HeaderContainer>
<DialogTitle>{messageDialogParameters.title}</DialogTitle>
<CloseButton onClick={() => setShowDialog(false)}>
<svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M16.064 8.315a.188.188 0 0 0-.188-.187l-1.546.007L12 10.912 9.673 8.137l-1.55-.007a.187.187 0 0 0-.187.188c0 .044.016.086.044.122l3.05 3.633-3.05 3.63a.188.188 0 0 0 .143.31l1.55-.008L12 13.228l2.327 2.775 1.547.007a.187.187 0 0 0 .188-.188.194.194 0 0 0-.045-.121l-3.044-3.63 3.049-3.634a.188.188 0 0 0 .042-.122z"
fill="#3498DB"
/>
<path
d="M12 1.523c-5.798 0-10.5 4.702-10.5 10.5 0 5.799 4.702 10.5 10.5 10.5s10.5-4.701 10.5-10.5c0-5.798-4.702-10.5-10.5-10.5zm0 19.22a8.72 8.72 0 0 1-8.719-8.72A8.72 8.72 0 0 1 12 3.305a8.72 8.72 0 0 1 8.719 8.718A8.72 8.72 0 0 1 12 20.743z"
fill="#3498DB"
/>
</svg>
</CloseButton>
</HeaderContainer>
<Container>
<ContentContainerNested>
<Avatar>
<AvatarImage src={messageDialogParameters.job_poster.avatar_url} />
</Avatar>
<Info>
<InfoHeader>
<div>
<Name>
{messageDialogParameters.job_poster.first_name}{" "}
{messageDialogParameters.job_poster.last_name}
</Name>
<Headline>
{messageDialogParameters.job_poster.job_title} at{" "}
{messageDialogParameters.job_poster.company}
</Headline>
</div>
</InfoHeader>
<Title>{messageDialogParameters.position.job_title}</Title>
</Info>
</ContentContainerNested>
<form onSubmit={handleSubmit(onSubmit)}>
<TextArea
key={messageDialogParameters.id}
placeholder={messageDialogParameters.position.placeholder}
name="message"
onChange={handleTextAreaChange}
error={!!errors.message}
/>
{errors.message ? (
<InputErrorMessage>{errors.message.message}</InputErrorMessage>
) : null}
<Button fullWidth primary>
Send Message
</Button>
</form>
</Container>
</StyledMessageDialogHolder>
);
}
if (!messageDialogRef.current) return null;
return createPortal(markup(), messageDialogRef.current);
}
Example #14
Source File: Edit.tsx From jobsgowhere with MIT License | 4 votes |
Edit: React.FC<ProfileEditProps> = ({ profile, newUser, handleCancelEdit }) => {
const { firstName, lastName, email, picture } = profile;
const profileType = ("profileType" in profile && profile.profileType) || SEEKER;
const company = ("company" in profile && profile.company) || "";
const [headline, setHeadline] = React.useState(("headline" in profile && profile.headline) || "");
const [website, setWebsite] = React.useState(("website" in profile && profile.website) || "");
const [selectedProfileType, setSelectedProfileType] = React.useState(profileType);
const { register, handleSubmit, errors } = useForm<FormValues>();
const onSubmit: SubmitHandler<FormValues> = async (values) => {
try {
await ApiClient({
url: `${process.env.REACT_APP_API}/profile`,
method: newUser ? "post" : "put",
data: { ...values, email }, // add email value into request payload since email field is disabled
});
window.location.reload();
} catch (error) {
console.error(error.toJSON());
throw error;
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Helmet>
<title>Edit Profile</title>
</Helmet>
<Fieldset>
<ProfileImage src={picture} width="128" height="128" alt="profile image" />
</Fieldset>
<TwoCol>
<Col>
<Fieldset>
<Label htmlFor="first-name">First Name</Label>
<TextInput
id="first-name"
name="first_name"
defaultValue={firstName}
ref={register({ required: true })}
error={!!errors.first_name}
/>
</Fieldset>
</Col>
<Col>
<Fieldset>
<Label htmlFor="last-name">Last Name</Label>
<TextInput
id="last-name"
name="last_name"
defaultValue={lastName}
ref={register({ required: true })}
error={!!errors.last_name}
/>
</Fieldset>
</Col>
</TwoCol>
{newUser ? (
<Fieldset name="profile-type">
<Label htmlFor="profile-type">Profile Type</Label>
<RadiosHolder>
<div className="radio-item">
<Radio
value={SEEKER}
name="profile_type"
defaultChecked={profileType === SEEKER}
onChange={() => {
setSelectedProfileType(SEEKER);
}}
innerRef={register}
>
I'm Seeking
</Radio>
</div>
<div className="radio-item">
<Radio
value={RECRUITER}
name="profile_type"
defaultChecked={profileType === RECRUITER}
onChange={() => {
setSelectedProfileType(RECRUITER);
}}
innerRef={register}
>
I'm Hiring
</Radio>
</div>
</RadiosHolder>
</Fieldset>
) : (
"profileType" in profile && (
<input type="hidden" name="profile_type" value={profile.profileType} ref={register} />
)
)}
{selectedProfileType === RECRUITER && (
<>
<Fieldset>
<Label htmlFor="job-title">Your Title</Label>
<TextInput
id="job-title"
name="headline"
defaultValue={headline}
ref={register}
onChange={(e) => setHeadline(e.target.value)}
/>
</Fieldset>
<Fieldset>
<Label htmlFor="company">Your Company</Label>
<TextInput id="company" name="company" defaultValue={company} ref={register} />
</Fieldset>
<Fieldset>
<Label htmlFor="company-website">Company Website</Label>
<TextInput
id="company-website"
name="website"
defaultValue={website}
ref={register({
required: `Please enter a website link in this format (e.g. ${process.env.REACT_APP_WEBSITE_URL})`,
pattern: {
value: /(http(s)?):\/\/[(www.)?a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/,
message: `Please enter a valid website link (e.g. ${process.env.REACT_APP_WEBSITE_URL})`,
},
})}
onChange={(e) => setWebsite(e.target.value)}
error={!!errors.website}
/>
{errors.website && <InputErrorMessage>{errors.website.message}</InputErrorMessage>}
<Hint>
Include a company link for potential candiates to learn more about your company
</Hint>
</Fieldset>
</>
)}
{selectedProfileType === SEEKER && (
<>
<Fieldset>
<Label htmlFor="headline">Headline</Label>
<TextInput
id="headline"
name="headline"
defaultValue={headline}
ref={register}
onChange={(e) => setHeadline(e.target.value)}
/>
<Hint>Give a headline of what you want others to see you as.</Hint>
</Fieldset>
<Fieldset>
<Label htmlFor="seeker-website">Website / Portfolio / GitHub</Label>
<TextInput
id="seeker-website"
name="website"
defaultValue={website}
ref={register({
required: `Please enter a website link in this format (e.g. ${process.env.REACT_APP_WEBSITE_URL})`,
pattern: {
value: /(http(s)?):\/\/[(www.)?a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/,
message: `Please enter a valid website link (e.g. ${process.env.REACT_APP_WEBSITE_URL})`,
},
})}
onChange={(e) => setWebsite(e.target.value)}
error={!!errors.website}
/>
{errors.website && <InputErrorMessage>{errors.website.message}</InputErrorMessage>}
<Hint>Include a link for potential companies to learn more about you.</Hint>
</Fieldset>
</>
)}
<Fieldset>
<Label htmlFor="email">Email</Label>
<TextInput
id="email"
name="email"
defaultValue={email}
disabled
ref={register({
required: true,
pattern: /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
})}
error={!!errors.email}
/>
<Hint>This is for the emails you will receive when you connect with someone.</Hint>
</Fieldset>
{newUser ? (
<Button type="submit" fullWidth primary>
Continue
</Button>
) : (
<TwoCol>
<Col>
<Button type="button" onClick={handleCancelEdit} fullWidth>
Cancel
</Button>
</Col>
<Col>
<Button type="submit" fullWidth primary>
Save
</Button>
</Col>
</TwoCol>
)}
<input type="hidden" name="avatar_url" value={picture} ref={register} />
</form>
);
}
Example #15
Source File: ModifyAccount.tsx From revite with GNU Affero General Public License v3.0 | 4 votes |
export function ModifyAccountModal({ onClose, field }: Props) {
const client = useContext(AppContext);
const [processing, setProcessing] = useState(false);
const { handleSubmit, register, errors } = useForm<FormInputs>();
const [error, setError] = useState<string | undefined>(undefined);
const onSubmit: SubmitHandler<FormInputs> = async ({
password,
new_username,
new_email,
new_password,
}) => {
if (processing) return;
setProcessing(true);
try {
if (field === "email") {
await client.api.patch("/auth/account/change/email", {
current_password: password,
email: new_email,
});
onClose();
} else if (field === "password") {
await client.api.patch("/auth/account/change/password", {
current_password: password,
password: new_password,
});
onClose();
} else if (field === "username") {
await client.api.patch("/users/@me/username", {
username: new_username,
password,
});
onClose();
}
} catch (err) {
setError(takeError(err));
setProcessing(false);
}
};
return (
<Modal
visible={true}
onClose={onClose}
title={<Text id={`app.special.modals.account.change.${field}`} />}
disabled={processing}
actions={[
{
disabled: processing,
confirmation: true,
onClick: handleSubmit(onSubmit),
children:
field === "email" ? (
<Text id="app.special.modals.actions.send_email" />
) : (
<Text id="app.special.modals.actions.update" />
),
},
{
onClick: onClose,
children: <Text id="app.special.modals.actions.cancel" />,
palette: "plain",
},
]}>
{/* Preact / React typing incompatabilities */}
<form
onSubmit={(e) => {
e.preventDefault();
handleSubmit(
onSubmit,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
)(e as any);
}}>
{field === "email" && (
<FormField
type="email"
name="new_email"
register={register}
showOverline
error={errors.new_email?.message}
disabled={processing}
/>
)}
{field === "password" && (
<FormField
type="password"
name="new_password"
register={register}
showOverline
error={errors.new_password?.message}
autoComplete="new-password"
disabled={processing}
/>
)}
{field === "username" && (
<FormField
type="username"
name="new_username"
register={register}
showOverline
error={errors.new_username?.message}
disabled={processing}
/>
)}
<FormField
type="current_password"
register={register}
showOverline
error={errors.current_password?.message}
autoComplete="current-password"
disabled={processing}
/>
{error && (
<Overline type="error" error={error}>
<Text id="app.special.modals.account.failed" />
</Overline>
)}
</form>
</Modal>
);
}
Example #16
Source File: FormContainer.tsx From UsTaxes with GNU Affero General Public License v3.0 | 4 votes |
FormListContainer = <A,>(
props: PropsWithChildren<FormListContainerProps<A>>
): ReactElement => {
const {
children,
items,
icon,
max,
primary,
defaultValues,
secondary,
disableEditing = false,
removeItem,
onSubmitAdd,
onSubmitEdit,
onCancel = () => {
// default do nothing
},
grouping = () => 0,
groupHeaders = []
} = props
const [isOpen, setOpen] = useState(false)
const [editing, setEditing] = useState<number | undefined>(undefined)
const allowAdd = max === undefined || items.length < max
// Use the provided grouping function to split the input
// array into an array of groups. Each group has a title
// and a list of items, along with their original index.
const groups: [ReactNode, [A, number][]][] = _.chain(items)
.map<[A, number]>((x, n) => [x, n])
.groupBy(([x]) => grouping(x))
.toPairs()
.map<[ReactNode, [A, number][]]>(([k, xs]) => [
groupHeaders[parseInt(k)],
xs
])
.value()
// Note useFormContext here instead of useForm reuses the
// existing form context from the parent.
const { reset } = useFormContext()
const closeForm = (): void => {
setEditing(undefined)
setOpen(false)
reset(defaultValues)
}
const cancel = (): void => {
closeForm()
onCancel()
}
const onSave: SubmitHandler<A> = (formData): void => {
if (editing !== undefined) {
onSubmitEdit(editing)(formData)
} else {
onSubmitAdd(formData)
}
closeForm()
}
const openEditForm = (n: number): (() => void) | undefined => {
if (!disableEditing && editing === undefined) {
return () => {
setEditing(n)
setOpen(true)
reset(items[n])
}
}
}
const itemDisplay = (() => {
if (items.length > 0) {
return (
<List dense={true}>
{groups.map(([title, group], i) => (
<div key={`group-${i}`}>
{title}
{group.map(([item, originalIndex], k) => (
<MutableListItem
key={k}
primary={primary(item)}
secondary={
secondary !== undefined ? secondary(item) : undefined
}
onEdit={openEditForm(originalIndex)}
disableEdit={isOpen}
editing={editing === originalIndex}
remove={
removeItem !== undefined
? () => removeItem(originalIndex)
: undefined
}
icon={icon !== undefined ? icon(item) : undefined}
/>
))}
</div>
))}
</List>
)
}
})()
return (
<>
{itemDisplay}
<OpenableFormContainer
allowAdd={allowAdd}
defaultValues={defaultValues}
onSave={onSave}
isOpen={isOpen}
onOpenStateChange={setOpen}
onCancel={cancel}
>
{children}
</OpenableFormContainer>
</>
)
}