diff --git a/ipi-survey-platform/src/components/common/Footer.jsx b/ipi-survey-platform/src/components/common/Footer.jsx
index c8878fb..0e7677b 100644
--- a/ipi-survey-platform/src/components/common/Footer.jsx
+++ b/ipi-survey-platform/src/components/common/Footer.jsx
@@ -23,9 +23,9 @@ const Footer = () => {
@@ -44,7 +44,7 @@ const SummaryCard = ({ label, count, icon, iconBg = '#FDF7EB' }) => (
{count}
);
-
+
const StatusBadge = ({ status }) => {
const map = {
Active: 'bg-green-50 text-green-700 ring-1 ring-green-200',
@@ -60,7 +60,7 @@ const StatusBadge = ({ status }) => {
);
};
-
+
const AdminUsers = () => {
const [isAddUserModalOpen, setIsAddUserModalOpen] = useState(false);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
@@ -71,7 +71,7 @@ const AdminUsers = () => {
const [saving, setSaving] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const pageSize = 10;
-
+
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [selectedUser, setSelectedUser] = useState(null);
const [deletingRow, setDeletingRow] = useState(null);
@@ -83,15 +83,16 @@ const AdminUsers = () => {
const [errors, setErrors] = useState({});
const [toastData, setToastData] = useState(null);
const navigate = useNavigate();
-
+
// Reset password modal state
const [isResetModalOpen, setIsResetModalOpen] = useState(false);
const [resetUser, setResetUser] = useState(null);
+ const [oldPassword, setOldPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [resettingPassword, setResettingPassword] = useState(false);
const [resetErrors, setResetErrors] = useState({});
-
+
// Helper: show toast
const showToast = (message, type = 'success') => {
setToastData({ message, type });
@@ -102,14 +103,14 @@ const AdminUsers = () => {
setToastData(null);
}, 4000);
};
-
+
// Fetch users
useEffect(() => {
const fetchUsers = async () => {
try {
setLoading(true);
const res = await getAdminUser();
-
+
const usersData = Array.isArray(res)
? res
: Array.isArray(res?.data)
@@ -117,13 +118,13 @@ const AdminUsers = () => {
: Array.isArray(res?.data?.data)
? res.data.data
: null;
-
+
if (!usersData) {
showToast('Failed to load users', 'error');
setUsers([]);
return;
}
-
+
const mappedUsers = usersData.map((user) => {
const id = user.id ?? user._id ?? user.user_id ?? null;
const name = user.name ?? user.fullName ?? 'N/A';
@@ -150,7 +151,7 @@ const AdminUsers = () => {
raw: user,
};
});
-
+
setUsers(mappedUsers);
} catch (error) {
console.error('Error fetching admin users:', error);
@@ -160,14 +161,14 @@ const AdminUsers = () => {
setLoading(false);
}
};
-
+
fetchUsers();
}, []);
-
+
const totalUsers = users.length;
const activeUsers = users.filter((user) => user.status === 'Active').length;
const inactiveUsers = totalUsers - activeUsers;
-
+
const summaryItems = [
{
label: 'Total Users',
@@ -188,10 +189,10 @@ const AdminUsers = () => {
iconBg: '#F5F5F7',
},
];
-
+
const headers = ['Name', 'Email', 'Last Login', 'Status', 'Actions'];
const columnWidths = [100, 130, 150, 100, 130];
-
+
const rows = users.map((user) => [
user.name,
user.email,
@@ -199,7 +200,7 @@ const AdminUsers = () => {
,
'actions',
]);
-
+
const handleFormChange = (e) => {
if (!e) return;
if (e.target && e.target.name !== undefined) {
@@ -211,30 +212,30 @@ const AdminUsers = () => {
if (errors[e.name]) setErrors((prev) => ({ ...prev, [e.name]: '' }));
}
};
-
+
const handleUpdateUser = async () => {
try {
setLoading(true);
-
+
const updatedUser = {
name: formData.name,
email: formData.email,
is_active: formData.status === "Active",
};
-
+
const res = await updateAdminUser(currentUser.id, updatedUser);
-
+
const success =
res?.status === "success" ||
res?.code === 200 ||
res?.data?.status === "success" ||
(!res.error && res);
-
+
if (!success) throw new Error(res?.message || "Failed to update user");
-
+
showToast("User updated successfully!", "success");
setIsEditModalOpen(false);
-
+
setUsers((prev) =>
prev.map((u) =>
u.id === currentUser.id
@@ -248,7 +249,7 @@ const AdminUsers = () => {
: u
)
);
-
+
setCurrentUser(null);
} catch (error) {
console.error("Error updating user:", error);
@@ -257,27 +258,27 @@ const AdminUsers = () => {
setLoading(false);
}
};
-
+
const validateForm = () => {
const newErrors = {};
if (!formData.name || !formData.name.trim()) newErrors.name = 'Name is required';
if (!formData.status) newErrors.status = 'Status is required';
return newErrors;
};
-
+
const handleDeleteClick = (user) => {
setSelectedUser(user);
setShowDeleteModal(true);
};
-
+
const handleConfirmDelete = async () => {
try {
if (!selectedUser) return;
setDeletingRow(selectedUser.id);
await deleteAdminUser(selectedUser.id);
-
+
showToast("User deleted successfully!", "success");
-
+
setUsers((prev) => prev.filter((u) => u.id !== selectedUser.id));
} catch (error) {
showToast("Failed to delete user!", "error");
@@ -286,26 +287,26 @@ const AdminUsers = () => {
setShowDeleteModal(false);
}
};
-
+
const handleEdit = async (user) => {
if (!user || !user.id) {
showToast('Invalid user selected', 'error');
return;
}
-
+
setEditingRow(user);
setLoading(true);
-
+
try {
const res = await getAdminUserById(user.id);
const payload = res?.data ?? res;
if (!payload) throw new Error('No user data returned from API');
-
+
const name = payload.name ?? payload.fullName ?? user.name ?? '';
const email = payload.email ?? user.email ?? '';
const is_active =
payload.is_active !== undefined ? !!payload.is_active : !!user.is_active;
-
+
setCurrentUser({
...payload,
id: user.id,
@@ -313,13 +314,13 @@ const AdminUsers = () => {
email,
is_active,
});
-
+
setFormData({
name,
email,
status: is_active ? 'Active' : 'Inactive',
});
-
+
setErrors({});
setIsEditModalOpen(true);
} catch (error) {
@@ -329,123 +330,152 @@ const AdminUsers = () => {
setLoading(false);
}
};
-
+
const handleOpenResetModal = (user) => {
setResetUser(user);
+ setOldPassword('');
setNewPassword('');
setConfirmPassword('');
setResetErrors({});
setIsResetModalOpen(true);
};
-
+
const handleCloseResetModal = () => {
setResetUser(null);
+ setOldPassword('');
setNewPassword('');
setConfirmPassword('');
setResetErrors({});
setIsResetModalOpen(false);
setResettingPassword(false);
};
-
- // Validate reset password form
+
+ // Validate reset password form - Updated to allow 8 to 12 characters
const validateResetForm = () => {
const newErrors = {};
-
- if (!newPassword.trim()) {
- newErrors.newPassword = 'Password is required';
- } else if (newPassword.length < 6) {
- newErrors.newPassword = 'Password must be at least 6 characters';
+
+ if (!oldPassword.trim()) {
+ newErrors.oldPassword = 'Old password is required';
}
-
+
+ if (!newPassword.trim()) {
+ newErrors.newPassword = 'New password is required';
+ } else if (newPassword.length < 8) {
+ newErrors.newPassword = 'Password must be at least 8 characters';
+ }
+
if (!confirmPassword.trim()) {
newErrors.confirmPassword = 'Please confirm your password';
} else if (newPassword !== confirmPassword) {
- // Don't show error message, just prevent submission
- newPassword !== confirmPassword;
+ newErrors.confirmPassword = 'Passwords do not match';
}
-
+
return newErrors;
};
-
+
// Reset Password API Integration
const handleResetPassword = async () => {
if (!resetUser || !resetUser.id) {
showToast('No user selected for password reset', 'error');
return;
}
-
+
// Validate form
const formErrors = validateResetForm();
if (Object.keys(formErrors).length > 0) {
setResetErrors(formErrors);
return;
}
-
- // Check if passwords match (without showing error message)
- if (newPassword !== confirmPassword) {
- // Just return without showing error
- return;
- }
-
+
try {
setResettingPassword(true);
-
+
+ // Prepare data according to API specification
const passwordData = {
- newPassword: newPassword,
- confirmPassword: confirmPassword
+ old_password: oldPassword,
+ new_password: newPassword,
+ confirm_password: confirmPassword
};
-
- console.log('Initiating password reset for user:', resetUser.id);
-
+
+ console.log('🔄 Initiating admin password reset for user:', resetUser.id);
+
const res = await changeAdminUserPassword(resetUser.id, passwordData);
-
- console.log('Password reset response:', res);
-
- // Check for different success patterns
+
+ console.log('✅ Admin password reset response:', res);
+
+ // Check for success based on common response patterns
const success =
res?.status === "success" ||
res?.status === 200 ||
res?.code === 200 ||
res?.data?.status === "success" ||
- res?.message?.includes("success") ||
- res?.message?.includes("updated") ||
+ res?.message?.toLowerCase().includes("success") ||
+ res?.message?.toLowerCase().includes("updated") ||
+ res?.message?.toLowerCase().includes("changed") ||
(!res.error && res);
-
+
if (!success) {
throw new Error(res?.message || res?.data?.message || "Failed to reset password");
}
-
- // No success toast message
+
+ showToast('Admin password reset successfully!', 'success');
handleCloseResetModal();
-
+
} catch (error) {
- console.error('Error resetting password:', error);
-
- // More detailed error handling
+ console.error('❌ Error resetting admin password:', error);
+
+ // Handle specific error cases
let errorMessage = 'Failed to reset password';
-
- if (error.response?.data?.message) {
- errorMessage = error.response.data.message;
- } else if (error.response?.data?.errors) {
- // Handle validation errors from backend
- const errors = error.response.data.errors;
- errorMessage = Object.values(errors).flat().join(', ');
- } else if (error.message) {
+
+ if (error.message) {
errorMessage = error.message;
}
-
- showToast(errorMessage, 'error');
+
+ // Check for common admin password change errors
+ if (errorMessage.toLowerCase().includes('old password') ||
+ errorMessage.toLowerCase().includes('current password') ||
+ errorMessage.toLowerCase().includes('incorrect password')) {
+ setResetErrors(prev => ({
+ ...prev,
+ oldPassword: 'The current password is incorrect'
+ }));
+ showToast('The current password you entered is incorrect', 'error');
+ } else if (errorMessage.toLowerCase().includes('match')) {
+ setResetErrors(prev => ({
+ ...prev,
+ confirmPassword: 'New passwords do not match'
+ }));
+ showToast('New passwords do not match', 'error');
+ } else if (errorMessage.toLowerCase().includes('same') ||
+ errorMessage.toLowerCase().includes('previous')) {
+ setResetErrors(prev => ({
+ ...prev,
+ newPassword: 'New password cannot be the same as current password'
+ }));
+ showToast('New password cannot be the same as current password', 'error');
+ } else if (errorMessage.toLowerCase().includes('weak') ||
+ errorMessage.toLowerCase().includes('strength')) {
+ setResetErrors(prev => ({
+ ...prev,
+ newPassword: 'Password is too weak. Use a stronger password.'
+ }));
+ showToast('Password is too weak. Please use a stronger password.', 'error');
+ } else if (error.status === 404) {
+ showToast('Admin user not found', 'error');
+ } else {
+ showToast(errorMessage, 'error');
+ }
} finally {
setResettingPassword(false);
}
};
-
+
const handleDelete = (rowIndex) => {
const user = users[rowIndex];
setSelectedUser(user);
setShowDeleteModal(true);
};
-
+
const handleEditSubmit = async (e) => {
e.preventDefault();
const formErrors = validateForm();
@@ -454,12 +484,12 @@ const AdminUsers = () => {
showToast('Please fix the form errors', 'error');
return;
}
-
+
if (!currentUser || !currentUser.id) {
showToast('No user selected for update', 'error');
return;
}
-
+
setSaving(true);
try {
const payload = {
@@ -467,16 +497,16 @@ const AdminUsers = () => {
email: formData.email,
is_active: formData.status === 'Active',
};
-
+
const res = await updateAdminUser(currentUser.id, payload);
const success =
res?.status === 'success' ||
res?.status === 200 ||
res?.data?.status === 'success' ||
(!res.error && res);
-
+
if (!success) throw new Error(res?.message || 'Failed to update user');
-
+
setUsers((prev) =>
prev.map((u) =>
u.id === currentUser.id
@@ -490,7 +520,7 @@ const AdminUsers = () => {
: u
)
);
-
+
showToast('User updated successfully', 'success');
setIsEditModalOpen(false);
setEditingRow(null);
@@ -502,7 +532,7 @@ const AdminUsers = () => {
setSaving(false);
}
};
-
+
const tableToolbar = (
Admin Users
@@ -524,26 +554,26 @@ const AdminUsers = () => {
type="button"
onClick={() => {
if (!users.length) return;
-
+
const csvHeader = ['Name', 'Email', 'Status', 'Last Login'].join(',');
-
+
const csvRows = users.map((user) =>
[user.name, user.email, user.status, user.lastLogin]
.map((value) => `"${String(value ?? '').replace(/"/g, '""')}"`)
.join(',')
);
-
+
const blob = new Blob([csvHeader + '\n' + csvRows.join('\n')], {
type: 'text/csv;charset=utf-8;',
});
-
+
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'admin-users.csv');
document.body.appendChild(link);
link.click();
-
+
document.body.removeChild(link);
URL.revokeObjectURL(url);
}}
@@ -557,7 +587,7 @@ const AdminUsers = () => {
Export CSV
-
+
);
-
+
return (
{/* Summary cards */}
@@ -578,7 +608,7 @@ const AdminUsers = () => {
))}
-
+
{/* Table */}