From 9c7e4b9feefb4c04ea3ad92ed7e74c66fd8207dc Mon Sep 17 00:00:00 2001
From: Swayam Rana <122097724+SwayamRana808@users.noreply.github.com>
Date: Wed, 22 May 2024 22:00:47 +0530
Subject: [PATCH] =?UTF-8?q?=F0=9F=8D=80=20update=20functionality=20for=20A?=
=?UTF-8?q?dmin=20profile=20(#931)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* update functionality for Admin profile
* update functionality for Admin profile
* update functionality admin page
* update functionality admin page
* #update functionality admin page
* #update functionality admin page
* #update functionality admin page
* update admin api fix
---
backend/app/models/Admin.js | 4 +
backend/app/routes/admin/getAdmins.js | 1 +
backend/app/routes/admin/index.js | 17 +-
backend/app/routes/admin/updateAdmin.js | 42 +++-
frontend/src/pages/Admin/Admin.jsx | 23 ++-
.../Admin/Components/Profile/Profile.jsx | 195 ++++++++++++++++--
.../Components/Profile/profile.module.scss | 39 +++-
frontend/src/pages/Admin/admin.module.scss | 2 +
8 files changed, 290 insertions(+), 33 deletions(-)
diff --git a/backend/app/models/Admin.js b/backend/app/models/Admin.js
index 18b68209..e3d0478f 100644
--- a/backend/app/models/Admin.js
+++ b/backend/app/models/Admin.js
@@ -33,6 +33,10 @@ const adminSchema = new Schema(
trim: true,
unique: true,
},
+ image: {
+ type: String,
+ trim: true,
+ },
},
{ timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' } }
);
diff --git a/backend/app/routes/admin/getAdmins.js b/backend/app/routes/admin/getAdmins.js
index 887c3a38..8aebb616 100644
--- a/backend/app/routes/admin/getAdmins.js
+++ b/backend/app/routes/admin/getAdmins.js
@@ -15,6 +15,7 @@ const getAdminsAggregate = (match, page) => {
email: 1,
contact: 1,
isSuperAdmin: 1,
+ image:1
},
},
{ $skip: constants.PAGINATION_LIMIT.GET_ADMINS * (Number(page) - 1) },
diff --git a/backend/app/routes/admin/index.js b/backend/app/routes/admin/index.js
index 35ff2d0f..c8d6929f 100644
--- a/backend/app/routes/admin/index.js
+++ b/backend/app/routes/admin/index.js
@@ -1,7 +1,9 @@
const router = require('express').Router({ mergeParams: true });
+const multer = require('multer');
+const { nanoid } = require('nanoid');
+const path = require('path');
const validationMiddleware = require('../../../helpers/middlewares/validation');
const { authMiddleware } = require('../../../helpers/middlewares/auth');
-
const {
postSuperAdminSchema,
getAdminsSchema,
@@ -23,6 +25,17 @@ const resetPassword = require('./resetPassword');
const updateAdmin = require('./updateAdmin');
const deleteAdmin = require('./deleteAdmin');
+
+
+const store = multer.diskStorage({
+ destination: 'uploads/Admin/',
+ filename: (req, file, cb) => {
+ const uniqueFilename = nanoid() + path.extname(file.originalname);
+ cb(null, uniqueFilename);
+ },
+});
+const upload = multer({ storage: store });
+
router.get('/', validationMiddleware(getAdminsSchema, 'query'), authMiddleware, getAdmins);
router.get('/createSuperAdmin', createSuperAdmin);
@@ -33,7 +46,7 @@ router.post('/forgotpassword', validationMiddleware(forgotPasswordSchema), forgo
router.post('/resetpassword/:token', validationMiddleware(resetPasswordSchema), resetPassword);
router.put('/password', validationMiddleware(passwordChangeSchema), authMiddleware, changePassword);
-router.put('/:id/:token', validationMiddleware(updateAdminSchema), updateAdmin);
+router.put('/:id/:token', validationMiddleware(updateAdminSchema),upload.single('image') ,updateAdmin);
router.delete('/deleteAdmin', validationMiddleware(deleteAdminSchema), authMiddleware, deleteAdmin);
diff --git a/backend/app/routes/admin/updateAdmin.js b/backend/app/routes/admin/updateAdmin.js
index 9984d1e8..453f7f4e 100644
--- a/backend/app/routes/admin/updateAdmin.js
+++ b/backend/app/routes/admin/updateAdmin.js
@@ -1,18 +1,42 @@
const Admin = require('../../models/Admin');
+const fs = require('fs');
+const path = require('path');
module.exports =async (req, res) => {
+ const admin = await Admin.findById(req.params.id);
+ if (!admin) {
+ return res.status(404).json({ error: 'Admin not found' });
+ }
+
+ // Delete previous image if it exists
+ if (admin.image && admin.image!=="undefined" && req.file?.path) {
+ if (fs.existsSync(path.join(__dirname,'..' ,'..','..', admin.image))) {
+ fs.unlinkSync(path.join(__dirname,'..' ,'..','..', admin.image));
+ }
+ }
+
try {
- const updatedAdmin = await Admin.findByIdAndUpdate(
- req.params.id,
- {
- $set: req.body,
- },
- { new: true }
- );
- res.status(200).json(updatedAdmin);
+ const updateFields = {
+ firstName:req.body.firstName,
+ lastName:req.body.lastName,
+ contact:req.body.contact,
+ username:req.body.username
+ };
+ if (req.file?.path) {
+ updateFields.image = req.file.path;
+ }else{
+ updateFields.image = admin.image;
+ }
+ const updatedAdmin = await Admin.findByIdAndUpdate(
+ req.params.id,
+ { $set: updateFields },
+ { new: true }
+ );
+ return res.status(200).json({"Req.Body":updateFields,"updatedDoc":updatedAdmin});
} catch (err) {
- res.status(500).json(err);
+ return res.status(500).json(err);
}
+
res.send('Done');
};
diff --git a/frontend/src/pages/Admin/Admin.jsx b/frontend/src/pages/Admin/Admin.jsx
index cf26fab2..8138b925 100644
--- a/frontend/src/pages/Admin/Admin.jsx
+++ b/frontend/src/pages/Admin/Admin.jsx
@@ -33,12 +33,13 @@ export const Admin = (props) => {
const toggleNav = () => setIsMenuOpen(!isMenuOpen);
const closeMobileMenu = () => setIsMenuOpen(false);
const dispatch = useDispatch();
- const firstName = localStorage.getItem("firstName");
+ const [update,setUpdate]=useState(true);
const [qId,setQId] = useState("")
const [adminData, setAdminData] = useState({});
+ const [image,setImage]=useState('./images/admin.png');
const FetchAdminData = async () => {
try {
- const baseUrl = `${END_POINT}/admin`;
+ const baseUrl = `${END_POINT}/admin/`;
const params = {
type: "self",
email: localStorage.getItem("email"),
@@ -47,8 +48,18 @@ export const Admin = (props) => {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("token")}`,
};
- // Make the GET request using Axios
+
const response = await axios.get(baseUrl, { params, headers });
+ let formattedPath = response.data[0].image?.replace(/\\/g, "/");
+ if (formattedPath?.startsWith("uploads/")) {
+ formattedPath = formattedPath.replace("uploads/", "");
+ if (formattedPath && formattedPath !=="undefined") {
+ formattedPath = `${END_POINT}/${formattedPath}`;
+ }
+ }
+ if(formattedPath!=="undefined" && formattedPath){
+ setImage(formattedPath);
+ }
setAdminData(response.data[0]);
} catch (error) {
console.error("There was a problem with the fetch operation:", error);
@@ -68,7 +79,7 @@ export const Admin = (props) => {
logout(dispatch);
}
return true;
- }, [dispatch]);
+ }, [dispatch,update]);
return (
@@ -76,7 +87,7 @@ export const Admin = (props) => {
setTab(0)}>
@@ -212,7 +223,7 @@ export const Admin = (props) => {
{tab === 0 ? (
-
+
{setUpdate(!update)}} />
) : tab === 1 ? (
) : tab === 2 ? (
diff --git a/frontend/src/pages/Admin/Components/Profile/Profile.jsx b/frontend/src/pages/Admin/Components/Profile/Profile.jsx
index 7461ad16..abfafad4 100644
--- a/frontend/src/pages/Admin/Components/Profile/Profile.jsx
+++ b/frontend/src/pages/Admin/Components/Profile/Profile.jsx
@@ -1,25 +1,158 @@
import React, { useEffect, useState } from "react";
import EditIcon from "@material-ui/icons/Edit";
import CloseIcon from "@material-ui/icons/Close";
-import style from "./profile.module.scss";
-
+import Joi from "joi-browser";
+import { Button2 } from "../../../../components/util/Button";
+import "../../../../pages/Resources/components/ResourceSharingForm/resource-sharing-form.module.scss"
+import style from "./profile.module.scss";
+import Loader from "../../../../components/util/Loader";
+import { END_POINT } from "../../../../config/api";
+import { SimpleToast } from "../../../../components/util/Toast";
export function Profile(props) {
const [name, setName] = useState("Super Admin Name");
+ const [firstName,setFirstName]=useState("");
+ const [lastName,setLastName]=useState("");
const [email, setEmail] = useState("xyz@gmail.com");
+ const [username, setUsername] = useState("xyz");
const [phone, setPhone] = useState("+91-123456789");
const [edit, setEdit] = useState(false);
+ const [errors, setErrors] = useState({});
+ const [loading, setLoading] = useState(false);
+ const [open, setOpenToast] = useState(false);
+ const [toastMessage, setToastMessage] = useState("");
+ const [severity, setSeverity] = useState("success");
+ const [picUrl, setPicUrl] = useState(props.adminData.image);
+ const [pic, setPic] = useState();
+
+ const errorMessages = {
+ firstName: "Invalid First Name",
+ lastName: "Invalid Last Name",
+ email: "Invalid Email",
+ phone: "Invalid Phone Number Format +91XXXXXXXXX",
+ username:"Invalid Username"
+ };
+
+ const validationSchema = {
+ firstName: Joi.string().regex(/^[A-Za-z]+$/).required().label("First Name"),
+ lastName: Joi.string().regex(/^[A-Za-z]*$/).required().label("Last Name"),
+ email: Joi.string().email().required().label("Email"),
+ phone: Joi.string().regex(/[+]91[6-9]{1}[0-9]{9}$/).required().label("Contact Number"),
+ username: Joi.string().regex(/^[A-Za-z]+$/).required().label("Username")
+ };
+
+ const handleCloseToast = () => {
+ setTimeout(() => {
+ setOpenToast(false);
+ }, 500);
+ };
+
+ const validateForm = () => {
+ const obj = { firstName, lastName, email, phone,username };
+ const options = { abortEarly: false };
+ const { error } = Joi.validate(obj, validationSchema, options);
+ if (!error) return null;
+
+ const errors = {};
+ error.details.forEach(err => {
+ const field = err.path[0];
+ errors[field] = errorMessages[field] || "Invalid Input";
+ });
+ return errors;
+ };
+ const updateAdmin = async(e) => {
+ e.preventDefault();
+
+ const validationErrors = validateForm();
+ setErrors(validationErrors || {});
+
+ if (validationErrors) return;
+ setErrors({});
+
+ console.log(firstName,lastName,phone,email);
+ const token =localStorage.getItem('token')
+ const data = {
+ firstName,
+ lastName,
+ username,
+ contact:phone,
+ };
+ const formData = new FormData();
+ formData.append('firstName', firstName);
+ formData.append('lastName', lastName);
+ formData.append('username', username);
+ formData.append('contact', phone);
+ formData.append("image", pic);
+ setLoading(true);
+ try {
+ const response = await fetch(`${END_POINT}/admin/${props.adminData._id}/${token}`, {
+ method: 'PUT',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ body:formData,
+ });
+
+ if (!response.ok) {
+ setToastMessage("Failed to update admin");
+ setOpenToast(true);
+ setSeverity("error");
+ throw new Error('Failed to update admin');
+ }else{
+ setToastMessage("Updated Successfully");
+ setOpenToast(true);
+ setSeverity("success");
+ // re-render the Admin Page as it is updated
+ props.update();
+ }
+ } catch (error) {
+ console.error('Error updating admin:', error);
+ setToastMessage("Failed to update admin");
+ setOpenToast(true);
+ setSeverity("error");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const onPicChange = (event) => {
+ const { target } = event;
+ const { files } = target;
+
+ if (files && files[0]) {
+ setPic(files[0]);
+ let reader = new FileReader();
+ reader.onload = function (e) {
+ setPicUrl(e.target.result);
+ };
+ reader.readAsDataURL(files[0]);
+ }
+ return;
+ };
+
+ const changePic = () => {
+ return edit && document.getElementById("profile-pic-input")?.click();
+ };
useEffect(() => {
let firstName = props.adminData.firstName ? props.adminData.firstName : "";
let lastName = props.adminData.lastName ? props.adminData.lastName : "";
let Name = firstName + " " + lastName;
- setName(Name);
- setEmail(props.adminData.email);
- setPhone(props.adminData.contact);
- }, props);
+ setName(Name || "xyz");
+ setFirstName(firstName || "xyz");
+ setLastName(lastName || "");
+ setEmail(props.adminData.email || "xyz@gmail.com");
+ setPhone(props.adminData.contact || "+919099999990");
+ setUsername(props.adminData.username || "xyzIo");
+ }, [props]);
return (
+
My Profile
setEdit(!edit)}>
@@ -29,28 +162,64 @@ export function Profile(props) {
)}
-
+
{edit ? (
- <>
+
) : (
<>
{name}
diff --git a/frontend/src/pages/Admin/Components/Profile/profile.module.scss b/frontend/src/pages/Admin/Components/Profile/profile.module.scss
index 1be61c0d..058038b5 100644
--- a/frontend/src/pages/Admin/Components/Profile/profile.module.scss
+++ b/frontend/src/pages/Admin/Components/Profile/profile.module.scss
@@ -40,16 +40,20 @@
vertical-align: middle;
background: linear-gradient(45deg, #ff005a 14.23%, #0a183d 83.75%);
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.25);
+ overflow: hidden;
}
.img-admin {
- width: 100px;
- height: 100px;
+ width: 100%;
+ height: 100%;
margin: auto;
+ object-fit: cover;
+ object-position: center;
+ border-radius: 50%;
}
.card-info {
- height: 20rem;
+ height: 32rem;
width: 98%;
background: #fff;
margin: 0 auto;
@@ -89,6 +93,12 @@
width: 50%;
margin: 1rem auto;
}
+.input-wrapper-btn {
+ width: 50%;
+ margin: 1rem auto;
+ display: flex;
+ justify-content: center;
+}
.input-wrapper input {
border: 1px solid #f8f7f7;
@@ -98,6 +108,29 @@
padding: 0 1rem;
box-shadow: inset 2px 2px 5px #c9c9c9, inset -2px -2px 5px #ffffff;
}
+.data-loader {
+ width: 100%;
+ display: flex;
+ height: 60px;
+ justify-content: center;
+ align-items: center;
+}
+.edit-icon{
+ position: absolute;
+ top:0;
+ right:5px;
+ padding:2px;
+ color:black;
+ background-color: white;
+ border:solid white 1px;
+ border-radius: 100px;
+}
+.error-message {
+ height: 1rem;
+ color: red;
+ font-size: 12px;
+ margin-top: 2px;
+}
@media (max-width: 600px) {
.card-info {
diff --git a/frontend/src/pages/Admin/admin.module.scss b/frontend/src/pages/Admin/admin.module.scss
index 29b0d000..d04600c4 100644
--- a/frontend/src/pages/Admin/admin.module.scss
+++ b/frontend/src/pages/Admin/admin.module.scss
@@ -26,7 +26,9 @@ container {
.img-admin {
width: 50%;
+ aspect-ratio: 1 / 1;
margin: 1em;
+ border-radius: 50%;
}
.h1 {