Variant file names |
- {context.object.alt_names.map((alt_name) => (
+ {object.alt_names.map((alt_name) => (
- {humanFileSize(context.object.file_size)}
+ {humanFileSize(object.file_size)}
@@ -116,15 +101,15 @@ function SampleDetails() {
- {context.object.file_type}
+ {object.file_type}
@@ -133,10 +118,10 @@ function SampleDetails() {
md5 |
-
+
@@ -145,10 +130,10 @@ function SampleDetails() {
|
sha1 |
-
+
@@ -157,10 +142,10 @@ function SampleDetails() {
|
sha256 |
-
+
@@ -169,10 +154,10 @@ function SampleDetails() {
|
sha512 |
-
+
@@ -181,10 +166,10 @@ function SampleDetails() {
|
crc32 |
- {context.object.crc32}
+ {object.crc32}
@@ -196,15 +181,15 @@ function SampleDetails() {
- {context.object.ssdeep}
+ {object.ssdeep}
@@ -214,15 +199,15 @@ function SampleDetails() {
| Upload time |
{" "}
- {context.object.upload_time ? (
+ {object.upload_time ? (
-
+
) : (
[]
@@ -233,123 +218,3 @@ function SampleDetails() {
);
}
-
-// negate the buffer contents (xor with key equal 0xff)
-function negateBuffer(buffer) {
- const uint8View = new Uint8Array(buffer);
- const xored = uint8View.map((item) => item ^ 0xff);
- return xored.buffer;
-}
-
-function SamplePreview() {
- const [content, setContent] = useState("");
- const api = useContext(APIContext);
- const objectContext = useContext(ObjectContext);
- const tabContext = useTabContext();
-
- async function updateSample() {
- try {
- const fileId = objectContext.object.id;
- const obfuscate = 1;
- const fileContentResponse = await api.downloadFile(
- fileId,
- obfuscate
- );
- const fileContentResponseData = negateBuffer(
- fileContentResponse.data
- );
- setContent(fileContentResponseData);
- } catch (e) {
- objectContext.setObjectError(e);
- }
- }
-
- useEffect(() => {
- updateSample();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [objectContext.object.id]);
-
- return (
-
- );
-}
-
-function PreviewSwitchAction(props) {
- const tabContext = useTabContext();
- const mode = tabContext.subTab || "raw";
-
- if (mode === "raw")
- return (
-
- );
- else
- return (
-
- );
-}
-
-export default function ShowSample(props) {
- const api = useContext(APIContext);
- const params = useParams();
- const remotePath = useRemotePath();
-
- async function downloadSample(object) {
- window.location.href = await api.requestFileDownloadLink(object.id);
- }
-
- async function zipSample(object) {
- window.location.href = await api.requestZipFileDownloadLink(object.id);
- }
-
- return (
-
- ,
- ,
- ,
- ,
- ,
- ,
- ,
- ]}
- />
-
- ,
- ,
- ,
- ,
- ,
- ,
- ,
- ]}
- />
-
-
- );
-}
diff --git a/mwdb/web/src/components/SamplePreview.tsx b/mwdb/web/src/components/SamplePreview.tsx
new file mode 100644
index 000000000..9d3f2ded9
--- /dev/null
+++ b/mwdb/web/src/components/SamplePreview.tsx
@@ -0,0 +1,42 @@
+import { APIContext } from "@mwdb-web/commons/api";
+import { useContext, useEffect, useState } from "react";
+import { ObjectContext, useTabContext } from "./ShowObject";
+import { negateBuffer } from "@mwdb-web/commons/helpers";
+import { HexView } from "@mwdb-web/commons/ui";
+
+export function SamplePreview() {
+ const [content, setContent] = useState(new ArrayBuffer(0));
+ const api = useContext(APIContext);
+ const objectContext = useContext(ObjectContext);
+ const tabContext = useTabContext();
+
+ async function updateSample() {
+ try {
+ const fileId = objectContext!.object!.id!;
+ const obfuscate = 1;
+ const fileContentResponse = await api.downloadFile(
+ fileId,
+ obfuscate
+ );
+ const fileContentResponseData = negateBuffer(
+ fileContentResponse.data
+ );
+ setContent(fileContentResponseData);
+ } catch (e) {
+ objectContext.setObjectError(e);
+ }
+ }
+
+ useEffect(() => {
+ updateSample();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [objectContext?.object?.id]);
+
+ return (
+
+ );
+}
diff --git a/mwdb/web/src/components/Search.jsx b/mwdb/web/src/components/Search.jsx
deleted file mode 100644
index ed24a8ecb..000000000
--- a/mwdb/web/src/components/Search.jsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import React from "react";
-
-import RecentObjects from "./RecentObjects";
-
-export default function Search(props) {
- return ;
-}
diff --git a/mwdb/web/src/components/ShowObject/Actions/DownloadAction.tsx b/mwdb/web/src/components/ShowObject/Actions/DownloadAction.tsx
index 0f9eeacad..9c85d6cd2 100644
--- a/mwdb/web/src/components/ShowObject/Actions/DownloadAction.tsx
+++ b/mwdb/web/src/components/ShowObject/Actions/DownloadAction.tsx
@@ -7,7 +7,7 @@ import { ObjectAction } from "@mwdb-web/commons/ui";
import { ObjectOrConfigOrBlobData } from "@mwdb-web/types/types";
type Props = {
- download: (object?: Partial) => void;
+ download: (object?: Partial) => Promise;
};
export function DownloadAction(props: Props) {
diff --git a/mwdb/web/src/components/ShowObject/Actions/PushAction.tsx b/mwdb/web/src/components/ShowObject/Actions/PushAction.tsx
index 33bc35843..a208f6c71 100644
--- a/mwdb/web/src/components/ShowObject/Actions/PushAction.tsx
+++ b/mwdb/web/src/components/ShowObject/Actions/PushAction.tsx
@@ -22,7 +22,7 @@ export function PushAction() {
const remotes = config.config.remotes;
- if (!remotes || !remotes.length || api.remote) return [];
+ if (!remotes || !remotes.length || api.remote) return <>>;
async function pushRemote() {
try {
diff --git a/mwdb/web/src/components/ShowObject/common/AttributesBox.tsx b/mwdb/web/src/components/ShowObject/common/AttributesBox.tsx
index f1ec21ed9..a167e5a09 100644
--- a/mwdb/web/src/components/ShowObject/common/AttributesBox.tsx
+++ b/mwdb/web/src/components/ShowObject/common/AttributesBox.tsx
@@ -11,7 +11,7 @@ import { ConfirmationModal } from "@mwdb-web/commons/ui";
import { Capability } from "@mwdb-web/types/types";
import { Attributes } from "./Attributes";
-import AttributesAddModal from "../../AttributesAddModal";
+import { AttributesAddModal } from "../../AttributesAddModal";
import { Attribute, AttributeDefinition } from "@mwdb-web/types/types";
export function AttributesBox() {
diff --git a/mwdb/web/src/components/ShowObject/common/LatestConfigTab.tsx b/mwdb/web/src/components/ShowObject/common/LatestConfigTab.tsx
index 02bc7c304..982e8d7f5 100644
--- a/mwdb/web/src/components/ShowObject/common/LatestConfigTab.tsx
+++ b/mwdb/web/src/components/ShowObject/common/LatestConfigTab.tsx
@@ -1,6 +1,6 @@
import { useContext } from "react";
-import ConfigTable from "../../ConfigTable";
+import { ConfigTable } from "../../Config/common/ConfigTable";
import { ObjectContext } from "@mwdb-web/commons/context";
import { ObjectAction, ObjectTab } from "@mwdb-web/commons/ui";
diff --git a/mwdb/web/src/components/ShowObject/common/ObjectBox.tsx b/mwdb/web/src/components/ShowObject/common/ObjectBox.tsx
index 20d557996..10c86cc7c 100644
--- a/mwdb/web/src/components/ShowObject/common/ObjectBox.tsx
+++ b/mwdb/web/src/components/ShowObject/common/ObjectBox.tsx
@@ -11,7 +11,7 @@ type Props = {
children: React.ReactNode;
};
-export default function ObjectBox({ defaultTab, children }: Props) {
+export function ObjectBox({ defaultTab, children }: Props) {
const remotePath = useRemotePath();
const location = useLocation();
const { Component, setComponent } = useComponentState();
diff --git a/mwdb/web/src/components/ShowObject/common/RecentObjectHeader.tsx b/mwdb/web/src/components/ShowObject/common/RecentObjectHeader.tsx
new file mode 100644
index 000000000..fff4a252e
--- /dev/null
+++ b/mwdb/web/src/components/ShowObject/common/RecentObjectHeader.tsx
@@ -0,0 +1,14 @@
+export function RecentObjectHeader() {
+ return (
+
+ {/* Shrinked mode */}
+ Object ID |
+ Type/First seen/Tags |
+ {/* Wide mode */}
+ Object ID |
+ Object type |
+ Tags |
+ First seen |
+
+ );
+}
diff --git a/mwdb/web/src/components/ShowObject/common/RecentObjectRow.tsx b/mwdb/web/src/components/ShowObject/common/RecentObjectRow.tsx
new file mode 100644
index 000000000..cdbdabf1c
--- /dev/null
+++ b/mwdb/web/src/components/ShowObject/common/RecentObjectRow.tsx
@@ -0,0 +1,67 @@
+import { TagList, DateString, ObjectLink } from "@mwdb-web/commons/ui";
+import { RecentInnerRow, RecentRow } from "@mwdb-web/components/RecentView";
+import { RecentRowProps } from "@mwdb-web/types/props";
+import { ObjectData } from "@mwdb-web/types/types";
+
+export function RecentObjectRow(props: RecentRowProps) {
+ const objectId = ;
+ const uploadTime = ;
+ const tags = (
+ {
+ ev.preventDefault();
+ props.addToQuery("tag", tag);
+ }}
+ tagRemove={(ev, tag) => props.addToQuery("NOT tag", tag)}
+ filterable
+ />
+ );
+
+ return (
+
+ <>
+
+ {/* All modes */}
+
+ {objectId}
+
+ |
+
+ {/* Shrinked mode */}
+
+ <>{props.type}>
+
+
+ {uploadTime}
+
+ {tags}
+ {/* Wide mode */}
+
+ <>{props.type}>
+
+ |
+
+ {tags}
+ |
+
+
+ {uploadTime}
+
+ |
+ >
+
+ );
+}
diff --git a/mwdb/web/src/components/ShowObject/common/RecentObjects.tsx b/mwdb/web/src/components/ShowObject/common/RecentObjects.tsx
new file mode 100644
index 000000000..4d5ca411d
--- /dev/null
+++ b/mwdb/web/src/components/ShowObject/common/RecentObjects.tsx
@@ -0,0 +1,13 @@
+import { RecentView } from "../../RecentView";
+import { RecentObjectHeader } from "./RecentObjectHeader";
+import { RecentObjectRow } from "./RecentObjectRow";
+
+export default function RecentObjects() {
+ return (
+
+ );
+}
diff --git a/mwdb/web/src/components/ShowObject/common/RelationsTab.tsx b/mwdb/web/src/components/ShowObject/common/RelationsTab.tsx
index 10801310b..dc83e336c 100644
--- a/mwdb/web/src/components/ShowObject/common/RelationsTab.tsx
+++ b/mwdb/web/src/components/ShowObject/common/RelationsTab.tsx
@@ -4,7 +4,7 @@ import { useSearchParams } from "react-router-dom";
import { faProjectDiagram, faSearch } from "@fortawesome/free-solid-svg-icons";
-import RelationsPlot from "../../RelationsPlot";
+import { RelationsPlotView } from "../../Views/RelationsPlotView";
import { ObjectContext } from "@mwdb-web/commons/context";
import { ObjectAction, ObjectTab } from "@mwdb-web/commons/ui";
@@ -24,7 +24,10 @@ export function RelationsTab() {
,
]}
component={() => (
-
+
)}
/>
);
diff --git a/mwdb/web/src/components/ShowObject/common/ShowObject.tsx b/mwdb/web/src/components/ShowObject/common/ShowObject.tsx
index 161390c17..0e4f72961 100644
--- a/mwdb/web/src/components/ShowObject/common/ShowObject.tsx
+++ b/mwdb/web/src/components/ShowObject/common/ShowObject.tsx
@@ -10,7 +10,7 @@ import { toast } from "react-toastify";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import ObjectBox from "./ObjectBox";
+import { ObjectBox } from "./ObjectBox";
import { MultiRelationsBox } from "./MultiRelationsBox";
import { CommentBox } from "./CommentBox";
import { SharesBox } from "./SharesBox";
diff --git a/mwdb/web/src/components/ShowObject/common/TagForm.tsx b/mwdb/web/src/components/ShowObject/common/TagForm.tsx
index c4b801e8c..b8d5d1367 100644
--- a/mwdb/web/src/components/ShowObject/common/TagForm.tsx
+++ b/mwdb/web/src/components/ShowObject/common/TagForm.tsx
@@ -1,4 +1,4 @@
-import { useState, useContext } from "react";
+import { useState, useContext, FormEventHandler } from "react";
import { APIContext } from "@mwdb-web/commons/api";
import { ObjectContext } from "@mwdb-web/commons/context";
diff --git a/mwdb/web/src/components/ShowTextBlob.jsx b/mwdb/web/src/components/ShowTextBlob.jsx
deleted file mode 100644
index fa21569e4..000000000
--- a/mwdb/web/src/components/ShowTextBlob.jsx
+++ /dev/null
@@ -1,192 +0,0 @@
-import React, { useContext } from "react";
-import { Link, useParams } from "react-router-dom";
-
-import {
- ShowObject,
- ObjectTab,
- ObjectContext,
- LatestConfigTab,
- RelationsTab,
- DownloadAction,
- FavoriteAction,
- RemoveAction,
- ObjectAction,
- PushAction,
- PullAction,
-} from "./ShowObject";
-
-import {
- faScroll,
- faFingerprint,
- faRandom,
- faSearch,
-} from "@fortawesome/free-solid-svg-icons";
-
-import {
- makeSearchLink,
- makeSearchDateLink,
- downloadData,
- humanFileSize,
-} from "@mwdb-web/commons/helpers";
-import { DataTable, DateString, HexView } from "@mwdb-web/commons/ui";
-import { Extendable } from "@mwdb-web/commons/plugins";
-import { useRemotePath } from "@mwdb-web/commons/remotes";
-
-function TextBlobDetails() {
- const context = useContext(ObjectContext);
- const remotePath = useRemotePath();
- return (
-
-
-
- Blob name |
-
-
- {context.object.blob_name}
-
- |
-
-
- Blob size |
-
-
- {humanFileSize(context.object.blob_size)}
-
- |
-
-
- Blob type |
-
-
- {context.object.blob_type}
-
- |
-
-
- First seen |
-
- {" "}
- {context.object.upload_time ? (
-
-
-
- ) : (
- []
- )}
- |
-
-
- Last seen |
-
- {" "}
- {context.object.last_seen ? (
-
-
-
- ) : (
- []
- )}
- |
-
-
-
- );
-}
-
-function TextBlobPreview() {
- const context = useContext(ObjectContext);
- return (
-
- );
-}
-
-function BlobDiffAction() {
- const context = useContext(ObjectContext);
- const remotePath = useRemotePath();
- return (
-
- );
-}
-
-export default function ShowTextBlob(props) {
- const params = useParams();
- const remotePath = useRemotePath();
- async function downloadTextBlob(object) {
- downloadData(object.content, object.id, "text/plain");
- }
-
- return (
-
- ,
- ,
- ,
- ,
- ,
- ,
- ]}
- />
-
- ,
- ,
- ,
- ,
- ,
- ,
- ]}
- />
-
-
- );
-}
diff --git a/mwdb/web/src/components/UploadButton.tsx b/mwdb/web/src/components/UploadButton.tsx
new file mode 100644
index 000000000..7e45cfaf0
--- /dev/null
+++ b/mwdb/web/src/components/UploadButton.tsx
@@ -0,0 +1,27 @@
+import { faUpload } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { AuthContext } from "@mwdb-web/commons/auth";
+import { Capability } from "@mwdb-web/types/types";
+import { useContext } from "react";
+import { Link } from "react-router-dom";
+
+export function UploadButton() {
+ const auth = useContext(AuthContext);
+ const buttonLink = auth.hasCapability(Capability.addingFiles) ? (
+
+
+ Upload
+
+ ) : (
+
+
+
+ Upload
+
+
+ );
+ return buttonLink;
+}
diff --git a/mwdb/web/src/components/UploadDropzone.tsx b/mwdb/web/src/components/UploadDropzone.tsx
new file mode 100644
index 000000000..c75dbdbd2
--- /dev/null
+++ b/mwdb/web/src/components/UploadDropzone.tsx
@@ -0,0 +1,53 @@
+import { useCallback } from "react";
+import { useDropzone } from "react-dropzone";
+
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faUpload } from "@fortawesome/free-solid-svg-icons";
+
+type Props = {
+ onDrop: (data: File) => void;
+ file: File | null;
+};
+
+export function UploadDropzone(props: Props) {
+ const onDrop = props.onDrop;
+ const { getRootProps, getInputProps, isDragActive, isDragReject } =
+ useDropzone({
+ multiple: false,
+ onDrop: useCallback(
+ (acceptedFiles: any) => onDrop(acceptedFiles[0]),
+ [onDrop]
+ ),
+ });
+
+ const dropzoneClassName = isDragActive
+ ? "dropzone-active"
+ : isDragReject
+ ? "dropzone-reject"
+ : "";
+
+ return (
+
+
+
+
+
+
+
+ {props.file ? (
+
+ {props.file.name} - {props.file.size} bytes
+
+ ) : (
+ Click here to upload
+ )}
+
+
+
+
+ );
+}
diff --git a/mwdb/web/src/components/UserSetPassword.jsx b/mwdb/web/src/components/UserSetPasswordView.tsx
similarity index 82%
rename from mwdb/web/src/components/UserSetPassword.jsx
rename to mwdb/web/src/components/UserSetPasswordView.tsx
index 3ea2369dc..e4eaca823 100644
--- a/mwdb/web/src/components/UserSetPassword.jsx
+++ b/mwdb/web/src/components/UserSetPasswordView.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from "react";
+import { useState } from "react";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as Yup from "yup";
@@ -8,12 +8,17 @@ import { View } from "@mwdb-web/commons/ui";
import { getErrorMessage } from "@mwdb-web/commons/helpers";
import { api } from "@mwdb-web/commons/api";
-export default function UserSetPassword() {
- const [success, setSuccess] = useState(false);
+type FormValues = {
+ password: string;
+ confirmPassword: string;
+};
+
+export function UserSetPasswordView() {
+ const [success, setSuccess] = useState(false);
const token =
new URLSearchParams(window.location.search).get("token") || "";
- const validationSchema = Yup.object().shape({
+ const validationSchema: Yup.SchemaOf = Yup.object().shape({
password: Yup.string()
.required("Password is required")
.min(8, "Password must be at least 8 characters"),
@@ -26,10 +31,13 @@ export default function UserSetPassword() {
});
const formOptions = { resolver: yupResolver(validationSchema) };
- const { register, handleSubmit, formState } = useForm(formOptions);
- const { errors } = formState;
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ } = useForm(formOptions);
- async function onSubmit(form) {
+ async function onSubmit(form: FormValues) {
try {
let response = await api.authSetPassword(token, form.password);
setSuccess(true);
@@ -45,17 +53,15 @@ export default function UserSetPassword() {
return (
-
diff --git a/mwdb/web/src/components/UserPasswordRecover.jsx b/mwdb/web/src/components/Views/UserPasswordRecoverView.tsx
similarity index 77%
rename from mwdb/web/src/components/UserPasswordRecover.jsx
rename to mwdb/web/src/components/Views/UserPasswordRecoverView.tsx
index 23fddc92b..aadf91afd 100644
--- a/mwdb/web/src/components/UserPasswordRecover.jsx
+++ b/mwdb/web/src/components/Views/UserPasswordRecoverView.tsx
@@ -1,7 +1,7 @@
-import React, { useState, useContext, useRef } from "react";
+import { useState, useContext, useRef } from "react";
import ReCAPTCHA from "react-google-recaptcha";
import { toast } from "react-toastify";
-import { useForm } from "react-hook-form";
+import { UseFormProps, useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as Yup from "yup";
@@ -11,30 +11,28 @@ import { View, Label, FormError, LoadingSpinner } from "@mwdb-web/commons/ui";
import { getErrorMessage } from "@mwdb-web/commons/helpers";
import { useNavRedirect } from "@mwdb-web/commons/hooks";
-const formFields = {
- login: "login",
- email: "email",
- recaptcha: "recaptcha",
+type FormValues = {
+ login: string;
+ email: string;
+ recaptcha: string | null;
};
-const validationSchema = Yup.object().shape({
- [formFields.login]: Yup.string().required("Login is required"),
- [formFields.email]: Yup.string()
+const validationSchema: Yup.SchemaOf = Yup.object().shape({
+ login: Yup.string().required("Login is required"),
+ email: Yup.string()
.required("Email is required")
.matches(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g, "Value is not email"),
- [formFields.recaptcha]: Yup.string()
- .nullable()
- .required("Recaptcha is required"),
+ recaptcha: Yup.string().nullable().required("Recaptcha is required"),
});
-const formOptions = {
+const formOptions: UseFormProps = {
resolver: yupResolver(validationSchema),
mode: "onSubmit",
reValidateMode: "onSubmit",
shouldFocusError: true,
};
-export default function UserPasswordRecover() {
+export function UserPasswordRecoverView() {
const config = useContext(ConfigContext);
const { redirectTo } = useNavRedirect();
const {
@@ -43,18 +41,18 @@ export default function UserPasswordRecover() {
formState: { errors },
reset,
setValue,
- } = useForm(formOptions);
+ } = useForm(formOptions);
- const [loading, setLoading] = useState(false);
- const captchaRef = useRef(null);
+ const [loading, setLoading] = useState(false);
+ const captchaRef = useRef(null);
- async function recoverPassword(values) {
+ async function recoverPassword(values: FormValues) {
try {
setLoading(true);
await api.authRecoverPassword(
values.login,
values.email,
- values.recaptcha
+ values.recaptcha!
);
toast("Password reset link has been sent to the e-mail address", {
@@ -70,7 +68,7 @@ export default function UserPasswordRecover() {
reset();
} finally {
captchaRef.current?.reset();
- setValue(formFields.recaptcha, null);
+ setValue("recaptcha", null);
}
}
@@ -85,11 +83,11 @@ export default function UserPasswordRecover() {
- {config.config["recaptcha_site_key"] && (
+ {config.config.recaptcha_site_key && (
<>
- setValue(formFields.recaptcha, val)
+ setValue("recaptcha", val)
}
/>
diff --git a/mwdb/web/src/components/UserRegister.jsx b/mwdb/web/src/components/Views/UserRegisterView.tsx
similarity index 76%
rename from mwdb/web/src/components/UserRegister.jsx
rename to mwdb/web/src/components/Views/UserRegisterView.tsx
index 49394ed1d..55bca7c7e 100644
--- a/mwdb/web/src/components/UserRegister.jsx
+++ b/mwdb/web/src/components/Views/UserRegisterView.tsx
@@ -1,5 +1,5 @@
-import React, { useContext, useRef, useState } from "react";
-import { useForm } from "react-hook-form";
+import { useContext, useRef, useState } from "react";
+import { UseFormProps, useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as Yup from "yup";
import ReCAPTCHA from "react-google-recaptcha";
@@ -11,48 +11,44 @@ import { View, Label, FormError, LoadingSpinner } from "@mwdb-web/commons/ui";
import { getErrorMessage } from "@mwdb-web/commons/helpers";
import { useNavRedirect } from "@mwdb-web/commons/hooks";
-const formFields = {
- login: "login",
- email: "email",
- affiliation: "affiliation",
- job_title: "job_title",
- job_responsibilities: "job_responsibilities",
- other_info: "other_info",
- recaptcha: "recaptcha",
+type FormValues = {
+ login: string;
+ email: string;
+ affiliation: string;
+ job_title: string;
+ job_responsibilities: string;
+ other_info: string;
+ recaptcha: string | null;
};
-const validationSchema = Yup.object().shape({
- [formFields.login]: Yup.string()
+const validationSchema: Yup.SchemaOf = Yup.object().shape({
+ login: Yup.string()
.required("Login is required")
.matches(
/[A-Za-z0-9_-]{1,32}/,
"Login must contain only letters, digits, '_'and '-' characters, max 32 characters allowed."
),
- [formFields.email]: Yup.string()
+ email: Yup.string()
.required("Email is required")
.matches(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g, "Value is not email"),
- [formFields.affiliation]: Yup.string().required("Affiliation is required"),
- [formFields.job_title]: Yup.string().required("Job title is required"),
+ affiliation: Yup.string().required("Affiliation is required"),
+ job_title: Yup.string().required("Job title is required"),
- [formFields.job_responsibilities]: Yup.string().required(
+ job_responsibilities: Yup.string().required(
"Job responsibilities is required"
),
- [formFields.other_info]: Yup.string().required(
- "Other information is required"
- ),
- [formFields.recaptcha]: Yup.string()
- .nullable()
- .required("Recaptcha is required"),
+ other_info: Yup.string().required("Other information is required"),
+ recaptcha: Yup.string().nullable().required("Recaptcha is required"),
});
-const formOptions = {
+const formOptions: UseFormProps = {
resolver: yupResolver(validationSchema),
mode: "onSubmit",
reValidateMode: "onSubmit",
shouldFocusError: true,
};
-export default function UserRegister() {
+export function UserRegisterView() {
const config = useContext(ConfigContext);
const { redirectTo } = useNavRedirect();
const {
@@ -60,12 +56,12 @@ export default function UserRegister() {
handleSubmit,
setValue,
formState: { errors },
- } = useForm(formOptions);
+ } = useForm(formOptions);
- const [loading, setLoading] = useState(false);
- const captchaRef = useRef(null);
+ const [loading, setLoading] = useState(false);
+ const captchaRef = useRef(null);
- async function registerUser(values) {
+ async function registerUser(values: FormValues) {
try {
setLoading(true);
const additional_info = `Affiliation: ${values.affiliation}, Job title: ${values.job_title}, ${values.job_responsibilities} ${values.other_info}`;
@@ -73,7 +69,7 @@ export default function UserRegister() {
values.login,
values.email,
additional_info,
- values.recaptcha
+ values.recaptcha!
);
toast(
`User ${response.data.login} registration requested. Account is
@@ -89,7 +85,7 @@ export default function UserRegister() {
setLoading(false);
} finally {
captchaRef.current?.reset();
- setValue(formFields.recaptcha, null);
+ setValue("recaptcha", null);
}
}
@@ -110,11 +106,11 @@ export default function UserRegister() {
| |