From 93809d5428917a0c943e79ed9308acd45ad5eb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Leszczyk?= Date: Thu, 24 Mar 2022 10:44:48 +0100 Subject: [PATCH] feat(dashboard-v2): implement data mutations for user accounts --- .../components/forms/AccountRemovalForm.js | 78 +++++++++++++ .../components/forms/AccountSettingsForm.js | 106 ++++++++++++++++++ .../dashboard-v2/src/pages/settings/index.js | 67 ++++++----- 3 files changed, 221 insertions(+), 30 deletions(-) create mode 100644 packages/dashboard-v2/src/components/forms/AccountRemovalForm.js create mode 100644 packages/dashboard-v2/src/components/forms/AccountSettingsForm.js diff --git a/packages/dashboard-v2/src/components/forms/AccountRemovalForm.js b/packages/dashboard-v2/src/components/forms/AccountRemovalForm.js new file mode 100644 index 00000000..bdd7196e --- /dev/null +++ b/packages/dashboard-v2/src/components/forms/AccountRemovalForm.js @@ -0,0 +1,78 @@ +import * as Yup from "yup"; +import { useState } from "react"; +import PropTypes from "prop-types"; +import { Formik, Form } from "formik"; + +import { Button } from "../Button"; +import { TextField } from "../Form/TextField"; +import accountsService from "../../services/accountsService"; + +const accountRemovalSchema = Yup.object().shape({ + confirm: Yup.string().oneOf(["delete"], `Type "delete" to confirm`), +}); + +export const AccountRemovalForm = ({ abort, onSuccess }) => { + const [error, setError] = useState(false); + + return ( + { + try { + setError(false); + await accountsService.delete("user"); + onSuccess(); + } catch { + setError(true); + } + }} + > + {({ errors, touched, isValid, dirty }) => ( +
+
+

Delete account

+

+ This will completely delete your account. This process can't be undone. +

+
+ +
+ +

Type "delete" in the field below to remove your account.

+ + + +
+ + +
+ {error && ( +
+ There was an error processing your request. Please try again later. +
+ )} + + )} +
+ ); +}; + +AccountRemovalForm.propTypes = { + abort: PropTypes.func.isRequired, + onSuccess: PropTypes.func.isRequired, +}; diff --git a/packages/dashboard-v2/src/components/forms/AccountSettingsForm.js b/packages/dashboard-v2/src/components/forms/AccountSettingsForm.js new file mode 100644 index 00000000..f979dd31 --- /dev/null +++ b/packages/dashboard-v2/src/components/forms/AccountSettingsForm.js @@ -0,0 +1,106 @@ +import * as Yup from "yup"; +import PropTypes from "prop-types"; +import { Formik, Form } from "formik"; + +import { Button } from "../Button"; +import { TextField } from "../Form/TextField"; +import accountsService from "../../services/accountsService"; + +const isPopulated = (value) => value?.length > 0; + +const emailUpdateSchema = Yup.object().shape({ + email: Yup.string().email("Please provide a valid email address"), + confirmEmail: Yup.string() + .oneOf([Yup.ref("email"), null], "Emails must match") + .when("email", { + is: isPopulated, + then: (schema) => schema.required("Please confirm new email address"), + }), + password: Yup.string().min(6, "Password has to be at least 6 characters long"), + confirmPassword: Yup.string() + .oneOf([Yup.ref("password"), null], "Passwords must match") + .when("password", { + is: isPopulated, + then: (schema) => schema.required("Please confirm new password"), + }), +}); + +export const AccountSettingsForm = ({ user, onSuccess, onFailure }) => { + return ( + { + try { + await accountsService.put("user", { + json: { email, password }, + }); + + resetForm(); + onSuccess(); + } catch { + onFailure(); + } + }} + > + {({ errors, touched, isValid, dirty }) => ( +
+
+
+ + +
+
+ + +
+
+
+ +
+
+ )} +
+ ); +}; + +AccountSettingsForm.propTypes = { + onSuccess: PropTypes.func.isRequired, + onFailure: PropTypes.func.isRequired, +}; diff --git a/packages/dashboard-v2/src/pages/settings/index.js b/packages/dashboard-v2/src/pages/settings/index.js index 459cf8c0..41ccf6b4 100644 --- a/packages/dashboard-v2/src/pages/settings/index.js +++ b/packages/dashboard-v2/src/pages/settings/index.js @@ -1,19 +1,31 @@ -import * as React from "react"; +import { useState } from "react"; import { useMedia } from "react-use"; -import styled from "styled-components"; +import { navigate } from "gatsby"; +import { useUser } from "../../contexts/user"; import theme from "../../lib/theme"; import UserSettingsLayout from "../../layouts/UserSettingsLayout"; -import { TextInputBasic } from "../../components/TextInputBasic/TextInputBasic"; -import { Button } from "../../components/Button"; import { AvatarUploader } from "../../components/AvatarUploader"; +import { AccountSettingsForm } from "../../components/forms/AccountSettingsForm"; +import { Modal } from "../../components/Modal/Modal"; +import { AccountRemovalForm } from "../../components/forms/AccountRemovalForm"; +import { Alert } from "../../components/Alert"; -const FormGroup = styled.div.attrs({ - className: "grid sm:grid-cols-[1fr_min-content] w-full gap-y-2 gap-x-4 items-end", -})``; +const State = { + Pure: "PURE", + Success: "SUCCESS", + Failure: "FAILURE", +}; const AccountPage = () => { const isLargeScreen = useMedia(`(min-width: ${theme.screens.xl})`); + const { user, mutate: reloadUser } = useUser(); + const [state, setState] = useState(State.Pure); + const [removalInitiated, setRemovalInitiated] = useState(false); + + const prompt = () => setRemovalInitiated(true); + const abort = () => setRemovalInitiated(false); + return ( <>
@@ -32,28 +44,18 @@ const AccountPage = () => { )}
- - -
- -
-
- - -
- -
-
- - -
- -
- - The password must be at least 6 characters long. Significantly different from the email and old - password. - -
+ {state === State.Failure && ( + There was an error processing your request. Please try again later. + )} + {state === State.Success && Changes saved successfully.} + { + reloadUser(); + setState(State.Success); + }} + onFailure={() => setState(State.Failure)} + />

@@ -61,7 +63,7 @@ const AccountPage = () => {

This will completely delete your account. This process can't be undone.

);