diff --git a/client/src/containers/Admin/Users/AllUsers.tsx b/client/src/containers/Admin/Users/AllUsers.tsx index 0552619..078cb83 100644 --- a/client/src/containers/Admin/Users/AllUsers.tsx +++ b/client/src/containers/Admin/Users/AllUsers.tsx @@ -43,8 +43,19 @@ const AllUsers: React.FC = () => { const [archivedFilter, setArchivedFilter] = useState("all"); const [page, setPage] = useState(1); + // const [successTimeout, setSuccessTimeout] = useState(null); + const [loading, setLoading] = useState(true); - const [updating, setUpdating] = useState(null); + // const [updating, setUpdating] = useState(null); + // const [updatingArchive, setUpdatingArchive] = useState(null); + // const [updatingPassword, setUpdatingPassword] = useState(null); + const [loadingState, setLoadingState] = useState<{ + archiveId: string | null; + passwordId: string | null; + }>({ + archiveId: null, + passwordId: null, + }); const [successMessage, setSuccessMessage] = useState(""); const [errorMessage, setErrorMessage] = useState(""); @@ -66,6 +77,7 @@ const AllUsers: React.FC = () => { const { data } = await axios.get("/api/user/view"); setUsers(Array.isArray(data) ? data : []); + setPage(1); } catch (error) { console.error("Error fetching users:", error); @@ -99,6 +111,10 @@ const AllUsers: React.FC = () => { }); }, [users, search, archivedFilter]); + useEffect(() => { + setPage(1); + }, [search, archivedFilter]); + // ================= PAGINATION ================= const startIndex = (page - 1) * ITEMS_PER_PAGE; @@ -124,23 +140,54 @@ const AllUsers: React.FC = () => { const toggleArchive = async (id: string, current: boolean) => { try { - setUpdating(id); + setLoadingState({ archiveId: id, passwordId: null }); // await axios.put(`/api/user/archive/${id}`, { // await axios.put(`/api/admin/users/${id}`, { // archived: !current, // }); // await axios.put(`/api/admin/users/${id}/archive`); - await axios.put( + // await axios.put( + // `/api/admin/users/${id}/archive`, + // { + // archived: !current, + // } + // ); + + // setUsers((prev) => + // prev.map((u) => + // u.id === id ? { ...u, archived: !current } : u + // ) + // ); + const res = await axios.put( `/api/admin/users/${id}/archive`, - { - archived: !current, - } + { archived: !current } ); + // setUsers((prev) => + // prev.map((u) => + // u.id === id ? res.data.user ?? { ...u, archived: !current } : u + // ) + // ); + // setUsers((prev) => + // prev.map((u) => + // // u.id === id + // // ? { ...u, ...res.data.user } + // // : u + // u.id === id + // ? { ...u, ...(res.data.user ?? {}) } + // : u + // ) + // ); setUsers((prev) => prev.map((u) => - u.id === id ? { ...u, archived: !current } : u + u.id === id + ? { + ...u, + ...(res.data?.user ?? {}), + archived: res.data?.user?.archived ?? !current, + } + : u ) ); @@ -162,7 +209,7 @@ const AllUsers: React.FC = () => { setErrorMessage(""); }, 3000); } finally { - setUpdating(null); + setLoadingState({ archiveId: null, passwordId: null }); } }; @@ -204,7 +251,7 @@ const AllUsers: React.FC = () => { // return; // } - // setUpdating(selectedUser.id); + // setLoadingState(selectedUser.id); // // BACKEND ROUTE // // await axios.put(`/api/user/update/${selectedUser.id}`, { @@ -232,7 +279,7 @@ const AllUsers: React.FC = () => { // setErrorMessage(""); // }, 3000); // } finally { - // setUpdating(null); + // setLoadingState(null); // } // }; const updatePassword = async () => { @@ -242,32 +289,53 @@ const AllUsers: React.FC = () => { if (!newPassword || newPassword.length < 6) { setErrorMessage("Password must be at least 6 characters long."); - setTimeout(() => setErrorMessage(""), 3000); + // setTimeout(() => setErrorMessage(""), 3000); + clearMessageAfterDelay(setErrorMessage); return; } - setUpdating(selectedUser.id); + setLoadingState({ archiveId: null, passwordId: selectedUser.id }); - await axios.put(`/api/admin/update/${selectedUser.id}`, { - newPassword, // ✅ FIX: match backend expectation + // await axios.put(`/api/admin/update/${selectedUser.id}`, { + // newPassword, // ✅ FIX: match backend expectation + // }); + // await axios.put(`/api/admin/update/${selectedUser.id}`, { + // password: newPassword, + // }); + await axios.put(`/api/admin/users/${selectedUser.id}/password`, { + password: newPassword, }); + // setTimeout(() => setSuccessMessage(""), 3000); + // if (successTimeout) clearTimeout(successTimeout); + + // const t = setTimeout(() => setSuccessMessage(""), 3000); + // setSuccessTimeout(t); setSuccessMessage("Password updated successfully."); - setTimeout(() => setSuccessMessage(""), 3000); + // setTimeout(() => { + // setSuccessMessage(""); + // }, 3000); + clearMessageAfterDelay(setSuccessMessage); + setNewPassword(""); closePasswordModal(); } catch (error) { console.error("Error updating password:", error); setErrorMessage("Failed to update password."); - setTimeout(() => setErrorMessage(""), 3000); + // setTimeout(() => setErrorMessage(""), 3000); + clearMessageAfterDelay(setErrorMessage); } finally { - setUpdating(null); + setLoadingState({ archiveId: null, passwordId: null }); } }; + const clearMessageAfterDelay = (setter: (v: string) => void, delay = 3000) => { + setTimeout(() => setter(""), delay); + }; + return (
@@ -535,7 +603,7 @@ const AllUsers: React.FC = () => { !!user.archived ) } - disabled={updating === user.id} + disabled={loadingState.archiveId === user.id} > {user.archived ? ( <> @@ -679,10 +747,10 @@ const AllUsers: React.FC = () => { className="save-password-btn" onClick={updatePassword} disabled={ - updating === selectedUser.id + loadingState.passwordId === selectedUser.id } > - {updating === selectedUser.id + {loadingState.passwordId === selectedUser.id ? "Updating..." : "Update Password"} diff --git a/controllers/AdminController.js b/controllers/AdminController.js index f96b07c..a05ed4d 100644 --- a/controllers/AdminController.js +++ b/controllers/AdminController.js @@ -522,22 +522,74 @@ router.get("/users/:id", async (req, res) => { }); // UPDATE user +// router.put("/update/:id", async (req, res) => { +// try { +// const user = await db.User.findByPk(req.params.id); +// if (!user) +// return res.status(404).json({ error: true, message: "User not found." }); + +// const payload = { ...req.body }; +// delete payload.id; +// delete payload.createdAt; +// delete payload.updatedAt; + +// // if (payload.password && payload.password.trim()) { +// // payload.password = await bcrypt.hash(payload.password, 10); +// // } else { +// // delete payload.password; +// // } +// if (payload.newPassword?.trim()) { +// payload.password = await bcrypt.hash(payload.newPassword, 10); +// delete payload.newPassword; +// } + +// if (payload.password && payload.password.trim()) { +// payload.password = await bcrypt.hash(payload.password, 10); +// } else { +// delete payload.password; +// } + +// console.log("UPDATE BODY:", req.body); +// normalizeBooleanFields(payload, [ +// "admin", +// "developer", +// "archived", +// "entrepreneur", +// "subscribed", +// "contractor", +// ]); +// normalizeJSONFields(payload, ["qrData", "loadDetails", "companyProfile"]); + +// await user.update(payload); + +// res.json({ +// error: false, +// message: "User updated successfully.", +// data: sanitizeUser(user), +// }); +// } catch (err) { +// console.error("User update error:", err); +// res.status(500).json({ error: true, message: "Update failed." }); +// } +// }); router.put("/update/:id", async (req, res) => { try { const user = await db.User.findByPk(req.params.id); - if (!user) - return res.status(404).json({ error: true, message: "User not found." }); + + if (!user) { + return res.status(404).json({ + error: true, + message: "User not found.", + }); + } const payload = { ...req.body }; + delete payload.id; delete payload.createdAt; delete payload.updatedAt; - - if (payload.password && payload.password.trim()) { - payload.password = await bcrypt.hash(payload.password, 10); - } else { - delete payload.password; - } + delete payload.password; // 🚫 important + delete payload.newPassword; // 🚫 important normalizeBooleanFields(payload, [ "admin", @@ -547,18 +599,22 @@ router.put("/update/:id", async (req, res) => { "subscribed", "contractor", ]); + normalizeJSONFields(payload, ["qrData", "loadDetails", "companyProfile"]); await user.update(payload); - res.json({ + return res.json({ error: false, message: "User updated successfully.", data: sanitizeUser(user), }); } catch (err) { console.error("User update error:", err); - res.status(500).json({ error: true, message: "Update failed." }); + res.status(500).json({ + error: true, + message: "Update failed.", + }); } }); @@ -611,4 +667,41 @@ router.put("/users/:id/archive", async (req, res) => { } }); +router.put("/users/:id/password", async (req, res) => { + try { + const user = await db.User.findByPk(req.params.id); + + if (!user) { + return res.status(404).json({ + error: true, + message: "User not found.", + }); + } + + const { password } = req.body; + + if (!password || password.trim().length < 6) { + return res.status(400).json({ + error: true, + message: "Password must be at least 6 characters.", + }); + } + + const hashed = await bcrypt.hash(password, 10); + + await user.update({ password: hashed }); + + return res.json({ + error: false, + message: "Password updated successfully.", + }); + } catch (err) { + console.error("Password update error:", err); + res.status(500).json({ + error: true, + message: "Password update failed.", + }); + } +}); + module.exports = router;