mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-25 03:09:32 -06:00
git stash push -m "work in progress"
This commit is contained in:
@@ -7,7 +7,6 @@ export const useGetUser = (userId) => {
|
||||
const [user, setUser] = useState(undefined);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const fetchUser = useCallback(async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
@@ -54,8 +53,29 @@ export const useEditUser = (userId) => {
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
[userId]
|
||||
[userId, t]
|
||||
);
|
||||
|
||||
return [editUser, isLoading, error];
|
||||
const changePassword = useCallback(
|
||||
async (passwordForm) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
await networkService.changePasswordByAdmin({ userId, passwordForm });
|
||||
createToast({
|
||||
body: t("teamPanel.changeTeamPassword.success"),
|
||||
});
|
||||
} catch (error) {
|
||||
createToast({
|
||||
body: error.message,
|
||||
});
|
||||
setError(error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
[userId, t]
|
||||
);
|
||||
|
||||
return [editUser, isLoading, error, changePassword];
|
||||
};
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
import { useState } from "react";
|
||||
import { Button, Stack } from "@mui/material";
|
||||
import { GenericDialog } from "../../../../Components/Dialog/genericDialog";
|
||||
import TextInput from "../../../../Components/Inputs/TextInput";
|
||||
import PasswordTooltip from "../../../Auth/components/PasswordTooltip";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createToast } from "../../../../Utils/toastUtils";
|
||||
import { PasswordEndAdornment } from "../../../../Components/Inputs/TextInput/Adornments";
|
||||
import usePasswordFeedback from "../../../Auth/hooks/usePasswordFeedback";
|
||||
|
||||
const ChangePasswordModal = ({ isSaving, isLoading, userId, changePassword, email }) => {
|
||||
const INITIAL_FORM_STATE = {
|
||||
password: "",
|
||||
confirm: "",
|
||||
};
|
||||
console.log("email", email);
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const { feedback, handlePasswordFeedback } = usePasswordFeedback();
|
||||
const [form, setForm] = useState(INITIAL_FORM_STATE);
|
||||
const [isChangePasswordOpen, setIsChangePasswordOpen] = useState(false);
|
||||
const [errors, setErrors] = useState({});
|
||||
const [isLoadingSubmit, setIsLoadingSubmit] = useState(false);
|
||||
const closeChangePasswordModal = () => {
|
||||
setIsChangePasswordOpen(false);
|
||||
setForm(INITIAL_FORM_STATE);
|
||||
};
|
||||
|
||||
const onChange = (e) => {
|
||||
let { name, value } = e.target;
|
||||
const updatedForm = { ...form, [name]: value };
|
||||
//validateFields(name, value, updatedForm);
|
||||
setForm(updatedForm);
|
||||
|
||||
handlePasswordFeedback(updatedForm, name, value, form, errors, setErrors);
|
||||
};
|
||||
const isFormValid = !errors.password && !errors.confirm;
|
||||
const onsubmitChangePassword = async (event) => {
|
||||
event.preventDefault();
|
||||
if (!isFormValid) return;
|
||||
const newPasswordForm = {
|
||||
password: form.password,
|
||||
newPassword: form.confirm,
|
||||
email: email,
|
||||
};
|
||||
try {
|
||||
setIsLoadingSubmit(true);
|
||||
await changePassword(newPasswordForm);
|
||||
closeChangePasswordModal();
|
||||
} catch (error) {
|
||||
const errorMsg = error.response?.data?.msg || error.message || "unknownError";
|
||||
createToast({
|
||||
type: "error",
|
||||
body: t(errorMsg),
|
||||
});
|
||||
} finally {
|
||||
setIsLoadingSubmit(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={() => setIsChangePasswordOpen(true)}
|
||||
disabled={isLoading || isSaving}
|
||||
>
|
||||
{t("teamPanel.changeTeamPassword.changePasswordMenu")}
|
||||
</Button>
|
||||
<GenericDialog
|
||||
title={t("teamPanel.changeTeamPassword.title")}
|
||||
description={t("teamPanel.changeTeamPassword.description")}
|
||||
open={isChangePasswordOpen}
|
||||
onClose={closeChangePasswordModal}
|
||||
theme={theme}
|
||||
width={{ sm: "55%", md: "50%", lg: "40%", xl: "30%" }}
|
||||
>
|
||||
<PasswordTooltip
|
||||
feedback={feedback}
|
||||
form={form}
|
||||
>
|
||||
<TextInput
|
||||
type="password"
|
||||
id="register-password-input"
|
||||
name="password"
|
||||
label={t("auth.common.inputs.password.label")}
|
||||
isRequired={true}
|
||||
placeholder="••••••••••"
|
||||
value={form.password}
|
||||
onChange={onChange}
|
||||
error={errors.password && errors.password[0] ? true : false}
|
||||
endAdornment={<PasswordEndAdornment />}
|
||||
sx={{ mb: theme.spacing(5) }}
|
||||
/>
|
||||
</PasswordTooltip>
|
||||
<TextInput
|
||||
type="password"
|
||||
id="register-confirm-input"
|
||||
name="confirm"
|
||||
label={t("auth.common.inputs.passwordConfirm.label")}
|
||||
gap={theme.spacing(4)}
|
||||
isRequired={true}
|
||||
placeholder={t("auth.common.inputs.passwordConfirm.placeholder")}
|
||||
autoComplete="current-password"
|
||||
value={form.confirm}
|
||||
onChange={onChange}
|
||||
error={errors.confirm && errors.confirm[0] ? true : false}
|
||||
endAdornment={<PasswordEndAdornment />}
|
||||
sx={{ mb: theme.spacing(5) }}
|
||||
/>
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={theme.spacing(10)}
|
||||
mt={theme.spacing(8)}
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={closeChangePasswordModal}
|
||||
disabled={isLoadingSubmit}
|
||||
>
|
||||
{t("teamPanel.cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="accent"
|
||||
onClick={onsubmitChangePassword}
|
||||
disabled={isLoadingSubmit || !isFormValid}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Stack>
|
||||
</GenericDialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default ChangePasswordModal;
|
||||
@@ -7,6 +7,7 @@ import TextInput from "@/Components/v1/Inputs/TextInput/index.jsx";
|
||||
import Search from "@/Components/v1/Inputs/Search/index.jsx";
|
||||
import Button from "@mui/material/Button";
|
||||
import RoleTable from "../components/RoleTable/index.jsx";
|
||||
import ChangePasswordModal from "../components/ChangePasswordModal";
|
||||
|
||||
// Utils
|
||||
import { useParams } from "react-router-dom";
|
||||
@@ -26,7 +27,7 @@ const EditUser = () => {
|
||||
];
|
||||
|
||||
const [user, isLoading, error] = useGetUser(userId);
|
||||
const [editUser, isSaving, saveError] = useEditUser(userId);
|
||||
const [editUser, isSaving, saveError, changePassword] = useEditUser(userId);
|
||||
const [
|
||||
form,
|
||||
setForm,
|
||||
@@ -104,7 +105,12 @@ const EditUser = () => {
|
||||
roles={form?.role}
|
||||
handleDeleteRole={handleDeleteRole}
|
||||
/>
|
||||
<Box>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={theme.spacing(10)}
|
||||
mt={theme.spacing(8)}
|
||||
//justifyContent="flex-end"
|
||||
>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
@@ -113,7 +119,14 @@ const EditUser = () => {
|
||||
>
|
||||
{t("editUserPage.form.save")}
|
||||
</Button>
|
||||
</Box>
|
||||
<ChangePasswordModal
|
||||
isSaving={isSaving}
|
||||
isLoading={isLoading}
|
||||
userId={userId}
|
||||
changePassword={changePassword}
|
||||
email={user?.email}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -1136,6 +1136,16 @@ class NetworkService {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async changePasswordByAdmin(config) {
|
||||
const { userId, passwordForm } = config;
|
||||
console.log("Password form data:", passwordForm);
|
||||
return this.axiosInstance.put(`auth/users/${userId}/password`, passwordForm, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default NetworkService;
|
||||
|
||||
@@ -425,7 +425,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"role": "Role"
|
||||
"role": "Role",
|
||||
"changeTeamPassword": {
|
||||
"changePasswordMenu": "Reset Password",
|
||||
"title": "Reset team member password",
|
||||
"description": "Create a new password for this team member. You will need to share the password with them securely.",
|
||||
"success": "Password successfully reset. Make sure to provide the credentials to the member in a secure way."
|
||||
}
|
||||
},
|
||||
"monitorState": {
|
||||
"paused": "Paused",
|
||||
|
||||
@@ -456,6 +456,29 @@ class AuthController extends BaseController {
|
||||
SERVICE_NAME,
|
||||
"editUserById"
|
||||
);
|
||||
editUserPasswordById = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
console.log("editUserPasswordById called");
|
||||
const roles = req?.user?.role;
|
||||
if (!roles.includes("superadmin")) {
|
||||
console.log("entro a error editUserPasswordById");
|
||||
throw createError("Unauthorized", 403);
|
||||
}
|
||||
|
||||
const userId = req.params.userId;
|
||||
console.log("userId param:", userId);
|
||||
const updates = { ...req.body };
|
||||
console.log("newPassword body:", req.body);
|
||||
await editUserByIdParamValidation.validateAsync(req.params);
|
||||
console.log("params validated");
|
||||
//await editUserPasswordByIdBodyValidation.validateAsync(req.body);
|
||||
console.log("body validated");
|
||||
await this.userService.setPasswordByUserId( userId, updates );
|
||||
return res.success({ msg: "Password reset successfully" });
|
||||
},
|
||||
SERVICE_NAME, "editUserPasswordById"
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default AuthController;
|
||||
|
||||
@@ -25,6 +25,7 @@ class AuthRoutes {
|
||||
this.router.get("/users", verifyJWT, isAllowed(["admin", "superadmin"]), this.authController.getAllUsers);
|
||||
this.router.get("/users/:userId", verifyJWT, isAllowed(["superadmin"]), this.authController.getUserById);
|
||||
this.router.put("/users/:userId", verifyJWT, isAllowed(["superadmin"]), this.authController.editUserById);
|
||||
this.router.put("/users/:userId/password", verifyJWT, isAllowed(["superadmin"]), this.authController.editUserPasswordById);
|
||||
|
||||
this.router.put("/user", verifyJWT, upload.single("profileImage"), this.authController.editUser);
|
||||
this.router.delete("/user", verifyJWT, this.authController.deleteUser);
|
||||
|
||||
@@ -211,5 +211,10 @@ class UserService {
|
||||
editUserById = async (userId, user) => {
|
||||
await this.db.userModule.editUserById(userId, user);
|
||||
};
|
||||
setPasswordByUserId = async (userId, updates) => {
|
||||
const updatedUser = await this.db.userModule.updateUser({ userId: userId, user: updates, file: null });
|
||||
return updatedUser;
|
||||
}
|
||||
|
||||
}
|
||||
export default UserService;
|
||||
|
||||
@@ -670,6 +670,10 @@ const editSuperadminUserByIdBodyValidation = joi.object({
|
||||
.required(),
|
||||
});
|
||||
|
||||
const editUserPasswordByIdBodyValidation = joi.object({
|
||||
newPassword: joi.string().min(8).required().pattern(passwordPattern),
|
||||
});
|
||||
|
||||
export {
|
||||
roleValidatior,
|
||||
loginValidation,
|
||||
@@ -739,4 +743,5 @@ export {
|
||||
editUserByIdParamValidation,
|
||||
editUserByIdBodyValidation,
|
||||
editSuperadminUserByIdBodyValidation,
|
||||
editUserPasswordByIdBodyValidation,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user