Browse Source

+ Merge Auth and LogimForm

ilya_shyian 1 year ago
parent
commit
252d636099
30 changed files with 479 additions and 352 deletions
  1. 0 19
      build/asset-manifest.json
  2. 0 1
      build/index.html
  3. 0 2
      build/static/css/main.c25a228f.css
  4. 0 1
      build/static/css/main.c25a228f.css.map
  5. 0 2
      build/static/js/787.884e199e.chunk.js
  6. 0 1
      build/static/js/787.884e199e.chunk.js.map
  7. 0 3
      build/static/js/main.61386bd1.js
  8. 0 156
      build/static/js/main.61386bd1.js.LICENSE.txt
  9. 0 1
      build/static/js/main.61386bd1.js.map
  10. 0 1
      build/static/media/404.7e060e4273fbd7fc09f0a17ca7f3dd68.svg
  11. BIN
      build/static/media/default-avatar-image.fb653606e5a348e7bc76.png
  12. BIN
      build/static/media/main-page-image.bc655052ce386c031a15.png
  13. 0 7
      build/static/media/shopping-logo.423022a24e67b6b6140c294be23af1b0.svg
  14. 1 1
      package.json
  15. 1 1
      src/actions/actionAboutMe.js
  16. 25 0
      src/actions/actionUpdateAvatar.js
  17. 24 0
      src/actions/actionUserUpdate.js
  18. 20 0
      src/actions/actionUserUpsert.js
  19. 0 134
      src/components/AuthPage/AuthForm.js
  20. 16 2
      src/components/AuthPage/index.js
  21. 10 0
      src/components/DashboardPage/ProfileForm/ProfileImage.js
  22. 236 0
      src/components/DashboardPage/ProfileForm/index.js
  23. 17 2
      src/components/DashboardPage/index.js
  24. 1 1
      src/components/LayoutPage/index.js
  25. 8 0
      src/components/admin/AdminCategoryPage/CategoryForm.js
  26. 13 0
      src/components/admin/AdminGoodPage/GoodForm.js
  27. 22 9
      src/components/admin/AdminOrderPage/OrderForm.js
  28. 31 7
      src/components/common/AuthModal/LoginForm.js
  29. 14 0
      src/components/common/AuthModal/RegisterForm.js
  30. 40 1
      src/index.scss

+ 0 - 19
build/asset-manifest.json

@@ -1,19 +0,0 @@
-{
-  "files": {
-    "main.css": "/static/css/main.c25a228f.css",
-    "main.js": "/static/js/main.61386bd1.js",
-    "static/js/787.884e199e.chunk.js": "/static/js/787.884e199e.chunk.js",
-    "static/media/main-page-image.png": "/static/media/main-page-image.bc655052ce386c031a15.png",
-    "static/media/default-avatar-image.png": "/static/media/default-avatar-image.fb653606e5a348e7bc76.png",
-    "static/media/404.svg": "/static/media/404.7e060e4273fbd7fc09f0a17ca7f3dd68.svg",
-    "static/media/shopping-logo.svg": "/static/media/shopping-logo.423022a24e67b6b6140c294be23af1b0.svg",
-    "index.html": "/index.html",
-    "main.c25a228f.css.map": "/static/css/main.c25a228f.css.map",
-    "main.61386bd1.js.map": "/static/js/main.61386bd1.js.map",
-    "787.884e199e.chunk.js.map": "/static/js/787.884e199e.chunk.js.map"
-  },
-  "entrypoints": [
-    "static/css/main.c25a228f.css",
-    "static/js/main.61386bd1.js"
-  ]
-}

File diff suppressed because it is too large
+ 0 - 1
build/index.html


File diff suppressed because it is too large
+ 0 - 2
build/static/css/main.c25a228f.css


File diff suppressed because it is too large
+ 0 - 1
build/static/css/main.c25a228f.css.map


File diff suppressed because it is too large
+ 0 - 2
build/static/js/787.884e199e.chunk.js


File diff suppressed because it is too large
+ 0 - 1
build/static/js/787.884e199e.chunk.js.map


File diff suppressed because it is too large
+ 0 - 3
build/static/js/main.61386bd1.js


+ 0 - 156
build/static/js/main.61386bd1.js.LICENSE.txt

@@ -1,156 +0,0 @@
-/*
-object-assign
-(c) Sindre Sorhus
-@license MIT
-*/
-
-/*!
-  Copyright (c) 2018 Jed Watson.
-  Licensed under the MIT License (MIT), see
-  http://jedwatson.github.io/classnames
-*/
-
-/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
-
-/**
- * @license React
- * react-dom.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * @license React
- * react-is.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * @license React
- * react-jsx-runtime.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * @license React
- * react.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * @license React
- * scheduler.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * @license React
- * use-sync-external-store-shim.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * @license React
- * use-sync-external-store-shim/with-selector.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * React Router DOM v6.3.0
- *
- * Copyright (c) Remix Software Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.md file in the root directory of this source tree.
- *
- * @license MIT
- */
-
-/**
- * React Router v6.3.0
- *
- * Copyright (c) Remix Software Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.md file in the root directory of this source tree.
- *
- * @license MIT
- */
-
-/** @license MUI v5.8.0
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/** @license React v0.19.1
- * scheduler.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/** @license React v16.13.1
- * react-is.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/** @license React v16.14.0
- * react-dom.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/** @license React v16.14.0
- * react.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/** @license React v17.0.2
- * react-is.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */

File diff suppressed because it is too large
+ 0 - 1
build/static/js/main.61386bd1.js.map


File diff suppressed because it is too large
+ 0 - 1
build/static/media/404.7e060e4273fbd7fc09f0a17ca7f3dd68.svg


BIN
build/static/media/default-avatar-image.fb653606e5a348e7bc76.png


BIN
build/static/media/main-page-image.bc655052ce386c031a15.png


File diff suppressed because it is too large
+ 0 - 7
build/static/media/shopping-logo.423022a24e67b6b6140c294be23af1b0.svg


+ 1 - 1
package.json

@@ -1,7 +1,7 @@
 {
   "name": "store",
   "version": "0.1.0",
-  "proxy": "http://127.0.0.1:8000",
+  "proxy": "http://127.0.0.1:8000/api",
   "private": true,
   "dependencies": {
     "@dnd-kit/core": "^5.0.3",

+ 1 - 1
src/actions/actionAboutMe.js

@@ -15,7 +15,7 @@ export const actionAboutMe = () => async (dispatch, getState) => {
             gql(
                 `query AboutMe($q:String){
                         UserFindOne(query:$q){
-                            _id username avatar{
+                            _id username name nick avatar{
                                 _id url
                             }
                         }

+ 25 - 0
src/actions/actionUpdateAvatar.js

@@ -0,0 +1,25 @@
+import { actionPromiseClear } from "../reducers";
+import { actionAboutMe } from "./actionAboutMe";
+import { actionUploadFile } from "./actionUploadFile";
+import { actionUserUpsert } from "./actionUserUpsert";
+
+export const actionUpdateAvatar = (file) => async (dispatch, getState) => {
+    await dispatch(actionUploadFile(file));
+
+    const {
+        promise: {
+            uploadFile: {
+                payload: { _id },
+                status,
+            },
+        },
+    } = getState();
+
+    await dispatch(actionUserUpsert({ avatar: { _id } }));
+
+    if (status === "FULFILLED") {
+        await dispatch(actionAboutMe());
+    }
+
+    await dispatch(actionPromiseClear("uploadFile"));
+};

+ 24 - 0
src/actions/actionUserUpdate.js

@@ -0,0 +1,24 @@
+import { actionPromiseClear } from "../reducers";
+import { actionAboutMe } from "./actionAboutMe";
+import { actionUploadFile } from "./actionUploadFile";
+import { actionUserUpsert } from "./actionUserUpsert";
+
+export const actionUserUpdate = (user) => async (dispatch, getState) => {
+    await dispatch(actionUserUpsert(user));
+
+    if (!user) {
+        return;
+    }
+
+    const {
+        promise: {
+            userUpsert: { status },
+        },
+    } = getState();
+
+    if (status === "FULFILLED") {
+        await dispatch(actionAboutMe());
+    }
+
+    await dispatch(actionPromiseClear("userUpsert"));
+};

+ 20 - 0
src/actions/actionUserUpsert.js

@@ -0,0 +1,20 @@
+import { gql } from "../helpers";
+import { actionPromise } from "../reducers";
+
+export const actionUserUpsert = (user) => async (dispatch, getState) => {
+    await dispatch(
+        actionPromise(
+            "userUpsert",
+            gql(
+                `mutation userUpsert($user:UserInput!){
+              UserUpsert(user:$user){
+                  _id username 
+              }
+          }`,
+                {
+                    user,
+                }
+            )
+        )
+    );
+};

+ 0 - 134
src/components/AuthPage/AuthForm.js

@@ -1,134 +0,0 @@
-import { actionLogin } from "../../actions/actionLogin";
-
-import { useState, useEffect, useContext } from "react";
-import { connect, useSelector } from "react-redux";
-import { MdVisibility, MdVisibilityOff } from "react-icons/md";
-import { Box, Button, IconButton, TextField } from "@mui/material";
-import { useFormik } from "formik";
-import * as Yup from "yup";
-import { UIContext } from "../UIContext";
-import { useNavigate } from "react-router-dom";
-
-const signInSchema = Yup.object().shape({
-    username: Yup.string().required("Обов'язкове"),
-    password: Yup.string().required("Обов'язкове"),
-});
-
-export const AuthForm = ({ onSubmit = null, promiseStatus, promisePayload, serverErrors = [] } = {}) => {
-    const [showPassword, setShowPassword] = useState(false);
-    const { setAlert } = useContext(UIContext);
-    const navigate = useNavigate();
-    const isAdmin = useSelector((state) => state.auth?.payload?.sub?.acl || []).includes("admin");
-
-    if (isAdmin) {
-        navigate("/admin");
-    }
-
-    const formik = useFormik({
-        initialValues: {
-            username: "",
-            password: "",
-        },
-        validationSchema: signInSchema,
-        validateOnChange: true,
-        onSubmit: () => {
-            onSubmit(formik.values.username, formik.values.password);
-        },
-    });
-
-    useEffect(() => {
-        if (promiseStatus === "FULFILLED") {
-            formik.setSubmitting(false);
-            if (promisePayload) {
-                setAlert({
-                    show: true,
-                    severity: "success",
-                    message: "Готово",
-                });
-            } else {
-                setAlert({
-                    show: true,
-                    severity: "error",
-                    message: "Не вірні дані",
-                });
-            }
-        }
-        if (promiseStatus === "REJECTED") {
-            const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
-            formik.setSubmitting(false);
-            setAlert({
-                show: true,
-                severity: "error",
-                message: errorMessage,
-            });
-        }
-    }, [promiseStatus]);
-
-    return (
-        <Box
-            className="AuthForm"
-            display="flex"
-            flexDirection="column"
-            onSubmit={formik.handleSubmit}
-            justifyContent="center"
-            component="form"
-        >
-            <TextField
-                id="username"
-                name="username"
-                variant="outlined"
-                label="Username"
-                error={formik.touched.username && Boolean(formik.errors.username)}
-                value={formik.values.username}
-                onBlur={formik.handleBlur}
-                onChange={formik.handleChange}
-                helperText={formik.touched.username && formik.errors.username}
-                fullWidth
-                sx={{ mt: 2 }}
-            />
-
-            <TextField
-                id="password"
-                name="password"
-                variant="outlined"
-                label="Password"
-                type={showPassword ? "text" : "password"}
-                error={formik.touched.password && Boolean(formik.errors.password)}
-                value={formik.values.password}
-                onBlur={formik.handleBlur}
-                onChange={formik.handleChange}
-                helperText={formik.touched.password && formik.errors.password}
-                InputProps={{
-                    endAdornment: (
-                        <IconButton onClick={() => setShowPassword((prev) => !prev)} edge="end">
-                            {showPassword ? <MdVisibilityOff /> : <MdVisibility />}
-                        </IconButton>
-                    ),
-                }}
-                fullWidth
-                sx={{ mt: 2 }}
-            />
-            <Button
-                variant="contained"
-                color="primary"
-                type="submit"
-                disabled={formik.isSubmitting || !formik.isValid}
-                sx={{ mt: 2, mr: 1 }}
-                fullWidth
-            >
-                Увійти
-            </Button>
-        </Box>
-    );
-};
-
-export const CAuthForm = connect(
-    (state) => ({
-        promiseStatus: state.promise?.login?.status || null,
-        promisePayload: state.promise?.login?.payload || null,
-        serverErrors: state.promise?.login?.error || [],
-    }),
-    {
-        onSubmit: (login, password) => actionLogin(login, password),
-    }
-)(AuthForm);

+ 16 - 2
src/components/AuthPage/index.js

@@ -1,10 +1,24 @@
 import { Box } from "@mui/material";
-import { CAuthForm } from "./AuthForm";
+import { useEffect } from "react";
+import { useSelector } from "react-redux";
+import { useNavigate } from "react-router-dom";
+import { CLoginForm } from "../common/AuthModal/LoginForm";
 
 export const AuthPage = () => {
+    const navigate = useNavigate();
+    const permissions = useSelector((state) => state.auth?.payload?.sub?.acl || []);
+
+    useEffect(() => {
+        if (permissions.includes("admin")) {
+            navigate("/admin");
+        } else if (permissions.includes("user")) {
+            navigate("/");
+        }
+    }, [permissions]);
+
     return (
         <Box className="AuthPage">
-            <CAuthForm />
+            <CLoginForm inputVariant="outlined" buttonVariant="contained" />
         </Box>
     );
 };

+ 10 - 0
src/components/DashboardPage/ProfileForm/ProfileImage.js

@@ -0,0 +1,10 @@
+import { connect } from "react-redux";
+
+import { backendURL, mediaURL } from "../../../helpers";
+import defaultAvatarImage from "../../../images/default-avatar-image.png";
+
+export const ProfileImage = ({ avatar }) => {
+    return <img src={avatar?.url ? `${backendURL}${mediaURL}${avatar?.url}` : defaultAvatarImage} className="ProfileImage" />;
+};
+
+export const CProfileImage = connect((state) => ({ avatar: state.promise?.aboutMe?.payload?.avatar || {} }))(ProfileImage);

+ 236 - 0
src/components/DashboardPage/ProfileForm/index.js

@@ -0,0 +1,236 @@
+import { Box, Button, Grid, IconButton, Paper, Table, TableBody, TableCell, TableRow, TextField } from "@mui/material";
+import { useContext, useEffect, useState } from "react";
+
+import { connect } from "react-redux";
+import { actionUpdateAvatar } from "../../../actions/actionUpdateAvatar";
+import { actionUserUpdate } from "../../../actions/actionUserUpdate";
+import { Ava } from "../../common/Ava";
+import { DropZone } from "../../common/DropZone";
+import { CProfileImage } from "./ProfileImage";
+import { useFormik } from "formik";
+import * as Yup from "yup";
+import { UIContext } from "../../UIContext";
+import { MdVisibility, MdVisibilityOff } from "react-icons/md";
+
+const CDropZone = connect(null, { onFileDrop: (acceptedFiles) => actionUpdateAvatar(acceptedFiles[0]) })(DropZone);
+
+const profileSchema = Yup.object().shape({
+    name: Yup.string(),
+    username: Yup.string().min(3, "Too Short!").max(15, "Too Long!").required("Required"),
+    password: Yup.string().min(3, "Too Short!").max(15, "Too Long!"),
+    nick: Yup.string(),
+});
+
+export const ProfileForm = ({ profile = {}, promiseStatus, onProfileSave, serverErrors = [] } = {}) => {
+    const [isLetterShown, setIsLetterShown] = useState(false);
+    const [editMod, setEditMod] = useState(false);
+    const [showPassword, setShowPassword] = useState(false);
+    const { setAlert } = useContext(UIContext);
+    const [promiseTimeOut, setPromiseTimeOut] = useState(null);
+
+    const formik = useFormik({
+        initialValues: {
+            username: "",
+            password: "",
+            repeatPassword: "",
+        },
+        validationSchema: profileSchema,
+        validateOnChange: true,
+        onSubmit: () => {
+            onProfileSave(formik.values);
+            setPromiseTimeOut(setTimeout(() => formik.setSubmitting(false), 3000));
+        },
+    });
+
+    useEffect(() => {
+        return () => {
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
+        };
+    }, []);
+
+    useEffect(() => {
+        formik.setValues(profile);
+
+        return () => {
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
+        };
+    }, [profile]);
+
+    useEffect(() => {
+        if (promiseStatus === "FULFILLED") {
+            formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
+            setAlert({
+                show: true,
+                severity: "success",
+                message: "Готово",
+            });
+        }
+        if (promiseStatus === "REJECTED") {
+            const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
+            formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
+            setAlert({
+                show: true,
+                severity: "error",
+                message: errorMessage,
+            });
+        }
+    }, [promiseStatus]);
+
+    return (
+        <Box component="form" className="ProfileForm" onSubmit={formik.handleSubmit}>
+            <Grid container spacing={3}>
+                <Grid xs={4} item>
+                    <CDropZone>
+                        <Box
+                            className="profileImageWrapper"
+                            onMouseEnter={() => setIsLetterShown(true)}
+                            onMouseLeave={() => setIsLetterShown(false)}
+                        >
+                            <CProfileImage />
+                            <Box className={`letter ${isLetterShown && "show"}`}>Drop file or click to update</Box>
+                        </Box>
+                    </CDropZone>
+                </Grid>
+                <Grid xs={8} item>
+                    <Table justify>
+                        <TableBody>
+                            <TableRow>
+                                <TableCell>Username</TableCell>
+                                <TableCell>
+                                    {editMod ? (
+                                        <TextField
+                                            size="small"
+                                            id="username"
+                                            name="username"
+                                            variant="standard"
+                                            label="Username"
+                                            error={formik.touched.username && Boolean(formik.errors.username)}
+                                            value={formik.values.username}
+                                            onBlur={formik.handleBlur}
+                                            onChange={formik.handleChange}
+                                            helperText={formik.touched.username && formik.errors.username}
+                                        />
+                                    ) : (
+                                        formik.values.username
+                                    )}
+                                </TableCell>
+                            </TableRow>
+                            <TableRow>
+                                <TableCell>Nick</TableCell>
+                                <TableCell>
+                                    {editMod ? (
+                                        <TextField
+                                            size="small"
+                                            id="nick"
+                                            name="nick"
+                                            variant="standard"
+                                            label="Nick"
+                                            error={formik.touched.nick && Boolean(formik.errors.nick)}
+                                            value={formik.values.nick}
+                                            onBlur={formik.handleBlur}
+                                            onChange={formik.handleChange}
+                                            helperText={formik.touched.nick && formik.errors.nick}
+                                        />
+                                    ) : (
+                                        formik.values.nick
+                                    )}
+                                </TableCell>
+                            </TableRow>
+                            <TableRow>
+                                <TableCell>Name</TableCell>
+                                <TableCell>
+                                    {editMod ? (
+                                        <TextField
+                                            size="small"
+                                            id="name"
+                                            name="name"
+                                            variant="standard"
+                                            label="Name"
+                                            error={formik.touched.name && Boolean(formik.errors.name)}
+                                            value={formik.values.name}
+                                            onBlur={formik.handleBlur}
+                                            onChange={formik.handleChange}
+                                            helperText={formik.touched.name && formik.errors.name}
+                                        />
+                                    ) : (
+                                        formik.values.name
+                                    )}
+                                </TableCell>
+                            </TableRow>
+                            <TableRow>
+                                <TableCell>Password</TableCell>
+                                <TableCell>
+                                    {editMod ? (
+                                        <TextField
+                                            size="small"
+                                            id="password"
+                                            name="password"
+                                            variant="standard"
+                                            label="Password"
+                                            type={showPassword ? "text" : "password"}
+                                            error={formik.touched.password && Boolean(formik.errors.password)}
+                                            value={formik.values.password}
+                                            onBlur={formik.handleBlur}
+                                            onChange={formik.handleChange}
+                                            helperText={formik.touched.password && formik.errors.password}
+                                            InputProps={{
+                                                endAdornment: (
+                                                    <IconButton onClick={() => setShowPassword((prev) => !prev)} edge="end">
+                                                        {showPassword ? <MdVisibilityOff /> : <MdVisibility />}
+                                                    </IconButton>
+                                                ),
+                                            }}
+                                        />
+                                    ) : (
+                                        "****************"
+                                    )}
+                                </TableCell>
+                            </TableRow>
+                            <TableRow>
+                                <TableCell></TableCell>
+                                <TableCell>
+                                    {editMod ? (
+                                        <Box display="flex" justifyContent="flex-end">
+                                            <Button variant="text" color="error" onClick={() => setEditMod(false)}>
+                                                Скасувати
+                                            </Button>
+                                            <Button
+                                                variant="text"
+                                                color="primary"
+                                                type="submit"
+                                                disabled={formik.isSubmitting || !formik.isValid}
+                                            >
+                                                Зберегти
+                                            </Button>
+                                        </Box>
+                                    ) : (
+                                        <Box display="flex" justifyContent="flex-end">
+                                            <Button onClick={() => setEditMod(true)}>Редагувати</Button>
+                                        </Box>
+                                    )}
+                                </TableCell>
+                            </TableRow>
+                        </TableBody>
+                    </Table>
+                </Grid>
+            </Grid>
+        </Box>
+    );
+};
+
+export const CProfileForm = connect(
+    (state) => ({
+        profile: state.promise?.aboutMe?.payload,
+        promiseStatus: state.promise.userUpsert?.status || null,
+        serverErrors: state.promise?.userUpsert?.error || [],
+    }),
+    {
+        onProfileSave: (profile = {}) => actionUserUpdate(profile),
+    }
+)(ProfileForm);

+ 17 - 2
src/components/DashboardPage/index.js

@@ -1,12 +1,27 @@
-import { Stack } from "@mui/material";
+import { Box, Paper, Stack, Typography } from "@mui/material";
 import { connect } from "react-redux";
 import { Error } from "../common/Error";
 import { DashboardOrder } from "./DashboardOrder";
+import { CProfileForm } from "./ProfileForm";
 
 export const DashboardPage = ({ orders = [] }) => {
     return (
         <Stack className="DashboardPage" spacing={4}>
-            {!!orders.length ? orders.map((order) => <DashboardOrder order={order} key={order._id} />) : <Error>Пока пусто </Error>}
+            <Paper className="Paper">
+                <CProfileForm />
+            </Paper>
+            {!!orders.length ? (
+                <Box mt={2}>
+                    <Typography pb={2} variant="h5">
+                        Замовлення
+                    </Typography>
+                    {orders.map((order) => (
+                        <DashboardOrder order={order} key={order._id} />
+                    ))}
+                </Box>
+            ) : (
+                ""
+            )}
         </Stack>
     );
 };

+ 1 - 1
src/components/LayoutPage/index.js

@@ -188,7 +188,7 @@ export const LayoutPage = () => {
                                 path="/dashboard/"
                                 exact
                                 element={
-                                    <CProtectedRoute roles={["user"]} fallback="/auth">
+                                    <CProtectedRoute roles={["user"]} fallback="/">
                                         <DashboardPageContainer />
                                     </CProtectedRoute>
                                 }

+ 8 - 0
src/components/admin/AdminCategoryPage/CategoryForm.js

@@ -37,6 +37,7 @@ const CategoryForm = ({
     const [promiseTimeOut, setPromiseTimeOut] = useState(null);
     const navigate = useNavigate();
     const dispatch = useDispatch();
+
     const formik = useFormik({
         initialValues: {
             name: category?.name || "",
@@ -59,6 +60,7 @@ const CategoryForm = ({
 
     useEffect(() => {
         return () => {
+            promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
         };
     }, []);
@@ -68,12 +70,15 @@ const CategoryForm = ({
         setInputParent(category?.parent || null);
         setInputGoods(category?.goods || []);
         setInputSubcategories(category?.subcategories || []);
+        formik.validateForm();
     }, [category]);
 
     useEffect(() => {
         if (promiseStatus === "FULFILLED") {
             formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
+
             setAlert({
                 show: true,
                 severity: "success",
@@ -83,6 +88,7 @@ const CategoryForm = ({
         if (promiseStatus === "REJECTED") {
             const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
             setAlert({
                 show: true,
@@ -94,10 +100,12 @@ const CategoryForm = ({
 
     useEffect(() => {
         if (deletePromiseStatus === "FULFILLED") {
+            promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
             navigate("/admin/categories/");
         }
         if (deletePromiseStatus === "REJECTED") {
+            promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
             setAlert({
                 show: true,

+ 13 - 0
src/components/admin/AdminGoodPage/GoodForm.js

@@ -73,9 +73,17 @@ export const GoodForm = ({
         },
     });
 
+    useEffect(() => {
+        return () => {
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
+        };
+    }, []);
+
     useEffect(() => {
         if (promiseStatus === "FULFILLED") {
             formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
             setAlert({
                 show: true,
@@ -86,6 +94,7 @@ export const GoodForm = ({
         if (promiseStatus === "REJECTED") {
             const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
             setAlert({
                 show: true,
@@ -97,10 +106,12 @@ export const GoodForm = ({
 
     useEffect(() => {
         if (deletePromiseStatus === "FULFILLED") {
+            promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
             navigate("/admin/goods/");
         }
         if (deletePromiseStatus === "REJECTED") {
+            promiseTimeOut && clearTimeout(promiseTimeOut);
             setPromiseTimeOut(null);
             setAlert({
                 show: true,
@@ -120,6 +131,7 @@ export const GoodForm = ({
         formik.setFieldValue("description", good.description || "");
         formik.setFieldValue("amount", good.amount || 0);
         formik.setFieldValue("price", good.price || 0);
+        formik.validateForm();
     }, [good.categories, good.name, good.description, good.amount, good.price]);
 
     useEffect(() => {
@@ -127,6 +139,7 @@ export const GoodForm = ({
             onClose && onClose();
         };
     }, []);
+
     return (
         <Box className="GoodForm" component="form" onSubmit={formik.handleSubmit}>
             <TextField

+ 22 - 9
src/components/admin/AdminOrderPage/OrderForm.js

@@ -12,11 +12,6 @@ import { useNavigate } from "react-router-dom";
 import { actionOrderDelete } from "../../../actions/actionOrderDelete";
 import { ConfirmModal } from "../../common/ConfirmModal";
 
-const deliveryOptions = [
-    { label: "Нова пошта", value: "nova-poshta" },
-    { label: "Justin", value: "justin" },
-];
-
 export const OrderForm = ({
     serverErrors = [],
     onSaveClick,
@@ -33,6 +28,7 @@ export const OrderForm = ({
     const { setAlert } = useContext(UIContext);
     const goodList = useSelector((state) => state.promise?.goodsAll?.payload || []);
     const [inputOrderGoods, setInputOrderGoods] = useState([]);
+    const [promiseTimeOut, setPromiseTimeOut] = useState(null);
     const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
     const navigate = useNavigate();
     const dispatch = useDispatch();
@@ -47,22 +43,29 @@ export const OrderForm = ({
             orderToSave.orderGoods = inputOrderGoods;
             onSaveClick && onSaveClick();
             onSave(orderToSave);
+            setPromiseTimeOut(setTimeout(() => formik.setSubmitting(false), 3000));
         },
     });
 
+    useEffect(() => {
+        return () => {
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
+        };
+    }, []);
+
     useEffect(() => {
         setInputStatus(order?.status || null);
         setInputUser(order?.owner || null);
         setInputOrderGoods(order.orderGoods || []);
-    }, [order]);
-
-    useEffect(() => {
         formik.validateForm();
-    }, [formik.values]);
+    }, [order]);
 
     useEffect(() => {
         if (promiseStatus === "FULFILLED") {
             formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
             setAlert({
                 show: true,
                 severity: "success",
@@ -72,6 +75,8 @@ export const OrderForm = ({
         if (promiseStatus === "REJECTED") {
             const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
             setAlert({
                 show: true,
                 severity: "error",
@@ -82,9 +87,17 @@ export const OrderForm = ({
 
     useEffect(() => {
         if (deletePromiseStatus === "FULFILLED") {
+            formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
+
             navigate("/admin/orders/");
         }
         if (deletePromiseStatus === "REJECTED") {
+            formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
+
             setAlert({
                 show: true,
                 severity: "error",

+ 31 - 7
src/components/common/AuthModal/LoginForm.js

@@ -13,9 +13,17 @@ const signInSchema = Yup.object().shape({
     password: Yup.string().min(3, "Too Short!").max(15, "Too Long!").required("Required"),
 });
 
-export const LoginForm = ({ onLogin, onRegisterButtonClick, promiseStatus, serverErrors }) => {
+export const LoginForm = ({
+    onLogin,
+    onRegisterButtonClick,
+    promiseStatus,
+    serverErrors,
+    inputVariant = "standard",
+    buttonVariant = "text",
+} = {}) => {
     const [showPassword, setShowPassword] = useState(false);
     const { setAlert } = useContext(UIContext);
+    const [promiseTimeOut, setPromiseTimeOut] = useState(null);
 
     const formik = useFormik({
         initialValues: {
@@ -27,12 +35,22 @@ export const LoginForm = ({ onLogin, onRegisterButtonClick, promiseStatus, serve
         validateOnChange: true,
         onSubmit: () => {
             onLogin(formik.values.username, formik.values.password);
+            setPromiseTimeOut(setTimeout(() => formik.setSubmitting(false), 3000));
         },
     });
 
+    useEffect(() => {
+        return () => {
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
+        };
+    }, []);
+
     useEffect(() => {
         if (promiseStatus === "FULFILLED") {
             formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
             setAlert({
                 show: true,
                 severity: "success",
@@ -42,6 +60,8 @@ export const LoginForm = ({ onLogin, onRegisterButtonClick, promiseStatus, serve
         if (promiseStatus === "REJECTED") {
             const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
             setAlert({
                 show: true,
                 severity: "error",
@@ -62,7 +82,7 @@ export const LoginForm = ({ onLogin, onRegisterButtonClick, promiseStatus, serve
             <TextField
                 id="username"
                 name="username"
-                variant="standard"
+                variant={inputVariant}
                 label="Username"
                 error={formik.touched.username && Boolean(formik.errors.username)}
                 value={formik.values.username}
@@ -76,7 +96,7 @@ export const LoginForm = ({ onLogin, onRegisterButtonClick, promiseStatus, serve
             <TextField
                 id="password"
                 name="password"
-                variant="standard"
+                variant={inputVariant}
                 label="Password"
                 type={showPassword ? "text" : "password"}
                 error={formik.touched.password && Boolean(formik.errors.password)}
@@ -97,7 +117,7 @@ export const LoginForm = ({ onLogin, onRegisterButtonClick, promiseStatus, serve
 
             <Stack direction="row" justifyContent="flex-end" sx={{ width: "100%" }}>
                 <Button
-                    variant="text"
+                    variant={buttonVariant}
                     color="primary"
                     type="submit"
                     disabled={formik.isSubmitting || !formik.isValid}
@@ -106,9 +126,13 @@ export const LoginForm = ({ onLogin, onRegisterButtonClick, promiseStatus, serve
                     Войти
                 </Button>
 
-                <Button variant="text" onClick={onRegisterButtonClick} sx={{ mt: 2 }}>
-                    Регистрация
-                </Button>
+                {onRegisterButtonClick ? (
+                    <Button variant={buttonVariant} onClick={onRegisterButtonClick} sx={{ mt: 2 }}>
+                        Регистрация
+                    </Button>
+                ) : (
+                    ""
+                )}
             </Stack>
         </Box>
     );

+ 14 - 0
src/components/common/AuthModal/RegisterForm.js

@@ -21,6 +21,8 @@ const signUpSchema = Yup.object().shape({
 export const RegisterForm = ({ serverErrors, promiseStatus, onRegister, onLoginButtonClick }) => {
     const [showPassword, setShowPassword] = useState(false);
     const { setAlert } = useContext(UIContext);
+    const [promiseTimeOut, setPromiseTimeOut] = useState(null);
+
     const formik = useFormik({
         initialValues: {
             username: "",
@@ -31,12 +33,22 @@ export const RegisterForm = ({ serverErrors, promiseStatus, onRegister, onLoginB
         validateOnChange: true,
         onSubmit: () => {
             onRegister(formik.values.username, formik.values.password);
+            setPromiseTimeOut(setTimeout(() => formik.setSubmitting(false), 3000));
         },
     });
 
+    useEffect(() => {
+        return () => {
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
+        };
+    }, []);
+
     useEffect(() => {
         if (promiseStatus === "FULFILLED") {
             formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
             setAlert({
                 show: true,
                 severity: "success",
@@ -46,6 +58,8 @@ export const RegisterForm = ({ serverErrors, promiseStatus, onRegister, onLoginB
         if (promiseStatus === "REJECTED") {
             const errorMessage = serverErrors.reduce((prev, curr) => prev + "\n" + curr.message, "");
             formik.setSubmitting(false);
+            promiseTimeOut && clearTimeout(promiseTimeOut);
+            setPromiseTimeOut(null);
             setAlert({
                 show: true,
                 severity: "error",

+ 40 - 1
src/index.scss

@@ -46,7 +46,7 @@
   min-width: 100%;
   height: 100vh;
   align-items: center;
-  & .AuthForm{
+  & .LoginForm{
     min-width: 400px;
   }
 }
@@ -220,6 +220,45 @@
 
     & .DashboardPage{
       padding: 20px;
+
+      & .Paper{
+        padding: 20px;
+      }
+
+      & .ProfileForm{
+        & .profileImageWrapper{
+          position: relative;
+          overflow: hidden;
+          & .ProfileImage{
+            max-width: 100%;
+
+          }
+          & .letter{
+            z-index: 2;
+            display: block;
+            position: absolute;
+
+            width: 100%;
+            transition:.2s;
+            text-align: center;
+            background: #CECECE;
+            padding: 10px;
+            opacity: .9;
+
+            &.show{
+              margin-top:-43px;
+            }
+          }
+
+        }
+
+
+
+        MuiTableCell-root{border-right: 1px solid grey}
+
+
+      }
+
       & .DashboardOrder{
         padding:20px;