diff --git a/apps/wallet/package.json b/apps/wallet/package.json
index 6a78d5b9a3b86..dea2c6ac3d3be 100644
--- a/apps/wallet/package.json
+++ b/apps/wallet/package.json
@@ -121,6 +121,7 @@
"@mysten/sui.js": "workspace:*",
"@mysten/wallet-standard": "workspace:*",
"@noble/hashes": "^1.3.1",
+ "@radix-ui/react-checkbox": "^1.0.4",
"@reduxjs/toolkit": "^1.9.5",
"@scure/bip32": "^1.3.1",
"@scure/bip39": "^1.2.1",
diff --git a/apps/wallet/src/ui/app/components/accounts/ImportPrivateKeyForm.tsx b/apps/wallet/src/ui/app/components/accounts/ImportPrivateKeyForm.tsx
index 73d0c07afd599..2da561b16a4c3 100644
--- a/apps/wallet/src/ui/app/components/accounts/ImportPrivateKeyForm.tsx
+++ b/apps/wallet/src/ui/app/components/accounts/ImportPrivateKeyForm.tsx
@@ -6,9 +6,9 @@ import { useForm, type SubmitHandler } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import * as Yup from 'yup';
import { privateKeyValidation } from '../../helpers/validation/privateKeyValidation';
+import { Form } from '../../shared/forms/Form';
+import { TextAreaField } from '../../shared/forms/TextAreaField';
import { Button } from '_app/shared/ButtonUI';
-import Alert from '_src/ui/app/components/alert';
-import { Text } from '_src/ui/app/shared/text';
const formSchema = Yup.object({
privateKey: privateKeyValidation,
@@ -21,33 +21,19 @@ type ImportPrivateKeyFormProps = {
};
export function ImportPrivateKeyForm({ onSubmit }: ImportPrivateKeyFormProps) {
- const {
- register,
- handleSubmit,
- formState: { isSubmitting, isValid, touchedFields, errors },
- } = useForm({
+ const form = useForm({
mode: 'onTouched',
resolver: yupResolver(formSchema),
});
+ const {
+ register,
+ formState: { isSubmitting, isValid },
+ } = form;
const navigate = useNavigate();
return (
-
+
);
}
diff --git a/apps/wallet/src/ui/app/components/accounts/ProtectAccountForm.tsx b/apps/wallet/src/ui/app/components/accounts/ProtectAccountForm.tsx
new file mode 100644
index 0000000000000..76bb05027b0aa
--- /dev/null
+++ b/apps/wallet/src/ui/app/components/accounts/ProtectAccountForm.tsx
@@ -0,0 +1,94 @@
+// Copyright (c) Mysten Labs, Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+import { yupResolver } from '@hookform/resolvers/yup';
+import { useForm, type SubmitHandler } from 'react-hook-form';
+import { useNavigate } from 'react-router-dom';
+import * as Yup from 'yup';
+import { CheckboxField } from '../../shared/forms/CheckboxField';
+import { Form } from '../../shared/forms/Form';
+import { TextField } from '../../shared/forms/TextField';
+import ExternalLink from '../external-link';
+import { Button } from '_app/shared/ButtonUI';
+import { ToS_LINK } from '_src/shared/constants';
+
+const formSchema = Yup.object({
+ password: Yup.string().required('Required'),
+ confirmedPassword: Yup.string().required('Required'),
+ acceptedTos: Yup.boolean().required().oneOf([true]),
+ enabledAutolock: Yup.boolean(),
+});
+
+type FormValues = Yup.InferType;
+
+type ProtectAccountFormProps = {
+ submitButtonText: string;
+ cancelButtonText: string;
+ onSubmit: SubmitHandler;
+};
+
+export function ProtectAccountForm({
+ submitButtonText,
+ cancelButtonText,
+ onSubmit,
+}: ProtectAccountFormProps) {
+ const form = useForm({
+ mode: 'all',
+ defaultValues: {
+ password: '',
+ confirmedPassword: '',
+ acceptedTos: false,
+ enabledAutolock: true,
+ },
+ resolver: yupResolver(formSchema),
+ });
+ const {
+ register,
+ formState: { isSubmitting, isValid },
+ } = form;
+ const navigate = useNavigate();
+
+ return (
+
+ );
+}
diff --git a/apps/wallet/src/ui/app/index.tsx b/apps/wallet/src/ui/app/index.tsx
index 0c683dd7d76d7..c09f2f4673d76 100644
--- a/apps/wallet/src/ui/app/index.tsx
+++ b/apps/wallet/src/ui/app/index.tsx
@@ -6,11 +6,12 @@ import { useEffect } from 'react';
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
import { useInitialPageView } from './hooks/useInitialPageView';
-import { CreateNewAccountPage } from './pages/accounts/CreateNewAccountPage';
+import { ProtectAccountPage } from './pages/accounts/ProtectAccountPage';
import AssetsPage from './pages/home/assets';
import { QredoConnectInfoPage } from './pages/qredo-connect/QredoConnectInfoPage';
import { SelectQredoAccountsPage } from './pages/qredo-connect/SelectQredoAccountsPage';
import { RestrictedPage } from './pages/restricted';
+import WelcomePage from './pages/welcome';
import { AppType } from './redux/slices/app/AppType';
import { Staking } from './staking/home';
import ForgotPasswordPage from '_app/wallet/forgot-password-page';
@@ -41,7 +42,6 @@ import CreatePage from '_pages/initialize/create';
import { ImportPage } from '_pages/initialize/import';
import SelectPage from '_pages/initialize/select';
import SiteConnectPage from '_pages/site-connect';
-import WelcomePage from '_pages/welcome';
import { setNavVisibility } from '_redux/slices/app';
const HIDDEN_MENU_PATHS = [
@@ -97,7 +97,7 @@ const App = () => {
{useNewOnboardingFlow && (
}>
} />
- } />
+ } />
} />
} />
} />
diff --git a/apps/wallet/src/ui/app/pages/accounts/AddAccountPage.tsx b/apps/wallet/src/ui/app/pages/accounts/AddAccountPage.tsx
index fbffe23c7ad08..d5118e81dc139 100644
--- a/apps/wallet/src/ui/app/pages/accounts/AddAccountPage.tsx
+++ b/apps/wallet/src/ui/app/pages/accounts/AddAccountPage.tsx
@@ -96,7 +96,7 @@ export function AddAccountPage({ showSocialSignInOptions = false }: AddAccountPa
variant="outline"
size="tall"
text="Create a new Passphrase Account"
- to="/accounts/create-new-account"
+ to="/accounts/protect-account"
onClick={() => {
ampli.clickedCreateNewAccount({ sourceFlow });
}}
diff --git a/apps/wallet/src/ui/app/pages/accounts/CreateNewAccountPage.tsx b/apps/wallet/src/ui/app/pages/accounts/CreateNewAccountPage.tsx
deleted file mode 100644
index 46a81cc93971f..0000000000000
--- a/apps/wallet/src/ui/app/pages/accounts/CreateNewAccountPage.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright (c) Mysten Labs, Inc.
-// SPDX-License-Identifier: Apache-2.0
-
-export function CreateNewAccountPage() {
- return
Implement me!
;
-}
diff --git a/apps/wallet/src/ui/app/pages/accounts/ProtectAccountPage.tsx b/apps/wallet/src/ui/app/pages/accounts/ProtectAccountPage.tsx
new file mode 100644
index 0000000000000..b660add0a8691
--- /dev/null
+++ b/apps/wallet/src/ui/app/pages/accounts/ProtectAccountPage.tsx
@@ -0,0 +1,34 @@
+// Copyright (c) Mysten Labs, Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+import { ProtectAccountForm } from '../../components/accounts/ProtectAccountForm';
+import { Heading } from '../../shared/heading';
+import { Text } from '_app/shared/text';
+
+export function ProtectAccountPage() {
+ return (
+
+
+ Wallet Setup
+
+
+
+ Protect Account with a Password Lock
+
+
+
+ {
+ // eslint-disable-next-line no-console
+ console.log(
+ 'TODO: Do something when the user submits the form successfully',
+ formValues,
+ );
+ }}
+ />
+
+
+ );
+}
diff --git a/apps/wallet/src/ui/app/shared/forms/CheckboxField.tsx b/apps/wallet/src/ui/app/shared/forms/CheckboxField.tsx
new file mode 100644
index 0000000000000..d04bbfeeb7ce5
--- /dev/null
+++ b/apps/wallet/src/ui/app/shared/forms/CheckboxField.tsx
@@ -0,0 +1,33 @@
+// Copyright (c) Mysten Labs, Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+import { type ComponentProps, type ReactNode, forwardRef } from 'react';
+import { Controller, useFormContext } from 'react-hook-form';
+import { Checkbox } from './controls/Checkbox';
+
+type CheckboxFieldProps = {
+ name: string;
+ label: ReactNode;
+} & Omit, 'ref'>;
+
+export const CheckboxField = forwardRef(
+ ({ label, name, ...props }, forwardedRef) => {
+ const { control } = useFormContext();
+ return (
+ (
+
+ )}
+ />
+ );
+ },
+);
diff --git a/apps/wallet/src/ui/app/shared/forms/Form.tsx b/apps/wallet/src/ui/app/shared/forms/Form.tsx
new file mode 100644
index 0000000000000..ffde456f1b3b8
--- /dev/null
+++ b/apps/wallet/src/ui/app/shared/forms/Form.tsx
@@ -0,0 +1,25 @@
+// Copyright (c) Mysten Labs, Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+import { type ComponentProps } from 'react';
+import {
+ type FieldValues,
+ FormProvider,
+ type SubmitHandler,
+ type UseFormReturn,
+} from 'react-hook-form';
+
+type FormProps = Omit, 'onSubmit'> & {
+ form: UseFormReturn;
+ onSubmit: SubmitHandler;
+};
+
+export function Form({ form, onSubmit, children, ...props }: FormProps) {
+ return (
+
+
+
+ );
+}
diff --git a/apps/wallet/src/ui/app/shared/forms/FormField.tsx b/apps/wallet/src/ui/app/shared/forms/FormField.tsx
new file mode 100644
index 0000000000000..b4950c07870b1
--- /dev/null
+++ b/apps/wallet/src/ui/app/shared/forms/FormField.tsx
@@ -0,0 +1,26 @@
+// Copyright (c) Mysten Labs, Inc.
+// SPDX-License-Identifier: Apache-2.0
+import { type ReactNode } from 'react';
+import { useFormContext } from 'react-hook-form';
+import { FormLabel } from './FormLabel';
+import Alert from '../../components/alert';
+
+type FormFieldProps = {
+ name: string;
+ label: ReactNode;
+ children: ReactNode;
+};
+
+export function FormField({ children, name, label }: FormFieldProps) {
+ const { getFieldState, formState } = useFormContext();
+ const state = getFieldState(name, formState);
+
+ return (
+