@@ -72,8 +93,8 @@ useEffect(() => {
onClick={() => setActive(t)}
className={`pb-3 text-sm whitespace-nowrap border-b-2 cursor-pointer transition-colors ${
active === t
- ? 'text-[#92722A] border-[#92722A] font-medium'
- : 'text-[#232528] border-transparent hover:text-[#92722A]'
+ ? "text-[#92722A] border-[#92722A] font-medium"
+ : "text-[#232528] border-transparent hover:text-[#92722A]"
}`}
>
{t}
@@ -84,11 +105,29 @@ useEffect(() => {
);
- const isQuarterTab = active === 'Quarterly Windows';
- const isIsicTab = active === 'HS Codes';
- const isUnitMasterTab = active === 'Unit Master';
- const isAdminTab = active === 'Admin Users';
- const isCompanyProfileTab = active === 'Company Profile';
+ // ✅ Render selected tab content
+ const renderActiveTab = () => {
+ switch (active) {
+ case "Quarterly Windows":
+ return
;
+ case "HS Codes":
+ return
;
+ case "Unit Master":
+ return
;
+ case "Admin Users":
+ return
;
+ case "Company Profile":
+ return
;
+ default:
+ return (
+
+
+ {active} content coming soon…
+
+
+ );
+ }
+ };
return (
@@ -96,22 +135,7 @@ useEffect(() => {
{Breadcrumbs}
{PageHeader}
-
- {isQuarterTab &&
}
-
- {isIsicTab &&
}
-
- {isUnitMasterTab &&
}
-
- {isAdminTab &&
}
-
- {isCompanyProfileTab &&
}
-
- {!isQuarterTab && !isIsicTab && !isUnitMasterTab && !isAdminTab && !isCompanyProfileTab && (
-
-
{active} content coming soon…
-
- )}
+ {renderActiveTab()}
);
diff --git a/ipi-survey-platform/src/pages/Admin/configuration/AdminUsers/ListAdminUsers.jsx b/ipi-survey-platform/src/pages/Admin/configuration/AdminUsers/ListAdminUsers.jsx
index 8cdcae7..56ad328 100644
--- a/ipi-survey-platform/src/pages/Admin/configuration/AdminUsers/ListAdminUsers.jsx
+++ b/ipi-survey-platform/src/pages/Admin/configuration/AdminUsers/ListAdminUsers.jsx
@@ -2,10 +2,15 @@ import React, { useState, useEffect } from 'react';
import Table from '@/components/common/Table';
import AddAdminUsers from './AddAdminUsers';
import { useNavigate } from 'react-router-dom';
-import { getAdminUser, getAdminUserById, updateAdminUser } from '@/services/configuration/adminService';
+import {
+ getAdminUser,
+ getAdminUserById,
+ updateAdminUser,
+ deleteAdminUser, // ✅ Added import
+} from '@/services/configuration/adminService';
import CustomToast from '@/components/common/CustomToast';
import { X } from 'lucide-react';
-import { TextField, SelectField } from '@/components/common/FormControls';
+import { TextField, SelectField } from '@/components/common/FormControls';
const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
const exportIconSrc = '/assets/images/DownloadSimple.svg';
@@ -18,7 +23,8 @@ const adminUserIconSrc = '/assets/images/UserGear.svg';
const activeUserIconSrc = '/assets/images/active-user.svg';
const inactiveUserIconSrc = '/assets/images/inactive-user.svg';
const resetPasswordIconSrc = '/assets/images/hugeicons_reset-password.svg';
-const resetPasswordInactiveIconSrc = '/assets/images/hugeicons_reset-password-inactive.svg';
+const resetPasswordInactiveIconSrc =
+ '/assets/images/hugeicons_reset-password-inactive.svg';
const SummaryCard = ({ label, count, icon, iconBg = '#FDF7EB' }) => (
@@ -38,12 +44,12 @@ const SummaryCard = ({ label, count, icon, iconBg = '#FDF7EB' }) => (
const StatusBadge = ({ status }) => {
const map = {
Active: 'bg-green-50 text-green-700 ring-1 ring-green-200',
- Closed: 'bg-gray-100 text-gray-700 ring-1 ring-gray-200',
+ Inactive: 'bg-gray-100 text-gray-700 ring-1 ring-gray-200',
};
return (
{status}
@@ -71,47 +77,70 @@ const AdminUsers = () => {
const [toastData, setToastData] = useState(null);
const navigate = useNavigate();
- // ✅ Fetch users from API
+ // Helper: show toast
+ const showToast = (message, type = 'success') => {
+ setToastData({ message, type });
+ if (window.toastTimeout) {
+ clearTimeout(window.toastTimeout);
+ }
+ window.toastTimeout = setTimeout(() => {
+ setToastData(null);
+ }, 4000);
+ };
+
+ // Fetch users
useEffect(() => {
const fetchUsers = async () => {
try {
setLoading(true);
- const response = await getAdminUser();
-
- // Check if response is an array directly or if it's in a data property
- const usersData = Array.isArray(response) ? response :
- (response?.data && Array.isArray(response.data) ? response.data : null);
-
- if (usersData) {
- // Map API data into UI format
- const mappedUsers = usersData.map((user) => ({
- id: user.id,
- name: user.name || 'N/A',
- email: user.email || 'N/A',
- lastLogin: user.updatedAt || 'N/A',
- // lastLogin: user.updatedAt
- // ? new Date(user.last_login).toLocaleString('en-GB', {
- // day: '2-digit',
- // month: 'short',
- // year: 'numeric',
- // hour: '2-digit',
- // minute: '2-digit',
- // })
- // : 'Never logged in',
- status: user.is_active ? 'Active' : 'Active',
- is_active: user.is_active, // Keep the original is_active for reference
- }));
-
- setUsers(mappedUsers);
- } else {
- const errorMsg = 'Invalid response format: Expected an array of users';
- console.error(errorMsg, response);
- showToast('Failed to load users. Please try again.', 'error');
- setUsers([]); // Reset to empty array
+ const res = await getAdminUser();
+
+ const usersData = Array.isArray(res)
+ ? res
+ : Array.isArray(res?.data)
+ ? res.data
+ : 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';
+ const email = user.email ?? 'N/A';
+ const lastLoginRaw = user.updatedAt ?? user.last_login ?? user.lastLogin ?? null;
+ const lastLogin = lastLoginRaw
+ ? new Date(lastLoginRaw).toLocaleString('en-GB', {
+ day: '2-digit',
+ month: 'short',
+ year: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ })
+ : 'Never logged in';
+ const is_active =
+ user.is_active !== undefined ? !!user.is_active : user.active ?? true;
+ return {
+ id,
+ name,
+ email,
+ lastLogin,
+ status: is_active ? 'Active' : 'Inactive',
+ is_active,
+ raw: user,
+ };
+ });
+
+ setUsers(mappedUsers);
} catch (error) {
console.error('Error fetching admin users:', error);
- CustomToast('Error fetching admin users', 'error');
+ showToast('Error fetching admin users', 'error');
+ setUsers([]);
} finally {
setLoading(false);
}
@@ -147,6 +176,11 @@ const AdminUsers = () => {
const headers = ['Name', 'Email', 'Last Login', 'Status', 'Actions'];
const columnWidths = [100, 130, 150, 100, 130];
+ const [isResetModalOpen, setIsResetModalOpen] = useState(false);
+const [resetUser, setResetUser] = useState(null);
+const [newPassword, setNewPassword] = useState('');
+const [confirmPassword, setConfirmPassword] = useState('');
+
const rows = users.map((user) => [
user.name,
@@ -156,59 +190,173 @@ const AdminUsers = () => {
'actions',
]);
- // Show toast helper
- const showToast = (message, type = 'success') => {
- setToastData({ message, type });
- if (window.toastTimeout) {
- clearTimeout(window.toastTimeout);
+ const handleFormChange = (e) => {
+ if (!e) return;
+ if (e.target && e.target.name !== undefined) {
+ const { name, value } = e.target;
+ setFormData((prev) => ({ ...prev, [name]: value }));
+ if (errors[name]) setErrors((prev) => ({ ...prev, [name]: '' }));
+ } else if (e.name && e.value !== undefined) {
+ setFormData((prev) => ({ ...prev, [e.name]: e.value }));
+ if (errors[e.name]) setErrors((prev) => ({ ...prev, [e.name]: '' }));
}
- window.toastTimeout = setTimeout(() => {
- setToastData(null);
- }, 4000);
+ };
+
+ 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 handleEdit = async (index) => {
const user = users[index];
+ if (!user || !user.id) {
+ showToast('Invalid user selected', 'error');
+ return;
+ }
+
setEditingRow(index);
-
+ setLoading(true);
+
try {
- setLoading(true);
- const userData = await getAdminUserById(user.id);
-
- if (!userData) {
- throw new Error('No user data received');
- }
-
- // Ensure we have all required fields with fallbacks
- const processedUser = {
- ...userData,
+ 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,
- name: userData.name || user.name || '',
- email: userData.email || user.email || '',
- is_active: userData.is_active !== undefined ? userData.is_active : user.is_active
- };
-
- setCurrentUser(processedUser);
-
- setFormData({
- name: processedUser.name,
- email: processedUser.email,
- status: processedUser.is_active ? 'Active' : 'Inactive',
+ name,
+ email,
+ is_active,
});
-
+
+ setFormData({
+ name,
+ email,
+ status: is_active ? 'Active' : 'Inactive',
+ });
+
+ setErrors({});
setIsEditModalOpen(true);
} catch (error) {
console.error('Error fetching user data:', error);
- const errorMsg = error.response?.data?.message || error.message || 'Failed to load user data';
- showToast(errorMsg, 'error');
+ showToast(error?.response?.data?.message || 'Failed to load user data', 'error');
} finally {
setLoading(false);
}
};
- const handleDelete = (index) => {
+ const handleOpenResetModal = (user) => {
+ setResetUser(user);
+ setNewPassword('');
+ setConfirmPassword('');
+ setIsResetModalOpen(true);
+};
+
+const handleCloseResetModal = () => {
+ setResetUser(null);
+ setIsResetModalOpen(false);
+};
+
+ // ✅ DELETE FUNCTIONALITY IMPLEMENTED HERE
+ const handleDelete = async (index) => {
+ const user = users[index];
+ if (!user || !user.id) {
+ showToast('Invalid user selected for delete', 'error');
+ return;
+ }
+
+ const confirmDelete = window.confirm(`Are you sure you want to delete "${user.name}"?`);
+ if (!confirmDelete) return;
+
setDeletingRow(index);
- // Future: open delete confirmation modal
+ setLoading(true);
+
+ try {
+ const res = await deleteAdminUser(user.id);
+
+ const success =
+ res?.status === 'success' ||
+ res?.code === 200 ||
+ res?.data?.status === 'success' ||
+ res?.message?.toLowerCase().includes('deleted');
+
+ if (!success) throw new Error(res?.message || 'Failed to delete user');
+
+ setUsers((prev) => prev.filter((u) => u.id !== user.id));
+ showToast('User deleted successfully', 'success');
+ } catch (error) {
+ console.error('Error deleting user:', error);
+ showToast(error?.response?.data?.message || error.message || 'Delete failed', 'error');
+ } finally {
+ setDeletingRow(null);
+ setLoading(false);
+ }
+ };
+ // ✅ END DELETE FUNCTIONALITY
+
+ const handleEditSubmit = async (e) => {
+ e.preventDefault();
+ const formErrors = validateForm();
+ if (Object.keys(formErrors).length > 0) {
+ setErrors(formErrors);
+ 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 = {
+ name: formData.name,
+ 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
+ ? {
+ ...u,
+ name: formData.name,
+ email: formData.email,
+ status: formData.status,
+ is_active: formData.status === 'Active',
+ }
+ : u
+ )
+ );
+
+ showToast('User updated successfully', 'success');
+ setIsEditModalOpen(false);
+ setEditingRow(null);
+ setCurrentUser(null);
+ } catch (error) {
+ console.error('Error updating user:', error);
+ showToast(error?.response?.data?.message || 'Failed to update user', 'error');
+ } finally {
+ setSaving(false);
+ }
};
const tableToolbar = (
@@ -246,14 +394,14 @@ const AdminUsers = () => {
return (
- {/* ✅ Summary cards */}
+ {/* Summary cards */}
{summaryItems.map((item) => (
))}
- {/* ✅ Table */}
+ {/* Table */}
{
onClick={() => handleEdit(rowIndex)}
>
@@ -296,37 +440,90 @@ const AdminUsers = () => {
onClick={() => handleDelete(rowIndex)}
>
+
+ type="button"
+ className={`h-8 w-8 grid place-items-center rounded hover:bg-gray-50 cursor-pointer ${
+ isActive ? '' : 'opacity-60'
+ }`}
+ title={isActive ? 'Reset password' : 'Reset disabled for inactive user'}
+ disabled={!isActive}
+ onClick={() => handleOpenResetModal(user)}
+>
+
+
+
+ {isResetModalOpen && (
+
+
+ {/* Close Button */}
+
+
+ {/* Header */}
+
Reset Password
+
+ {/* Input Fields */}
+
+
+ {/* Buttons */}
+
+
+
+
+
+
+
+)}
+
);
}
@@ -334,206 +531,42 @@ const AdminUsers = () => {
}}
/>
- {/* ✅ Add User Modal */}
+ {/* Add User Modal */}
{isAddUserModalOpen && (
setIsAddUserModalOpen(false)}
onSave={(newUser) => {
- setUsers(prev => [
+ setUsers((prev) => [
...prev,
{
...newUser,
+ id: newUser.id ?? `local-${Date.now()}`,
lastLogin: new Date().toLocaleString('en-GB'),
status: 'Active',
+ is_active: true,
},
]);
setIsAddUserModalOpen(false);
+ showToast('User added locally', 'success');
}}
/>
)}
- {/* ✅ Edit User Modal */}
- {isEditModalOpen && (
-
-
- {/* Header */}
-
-
Edit User
-
-
-
- {/* Body */}
-
-
-
- )}
-
- {/* ✅ Toast Notification */}
+ {/* Toast */}
{toastData && (
-
-
setToastData(null)}
- />
+
+
+ setToastData(null)}
+ />
+
)}
);
};
- // Form change handler
- const handleFormChange = (e) => {
- const { name, value } = e.target;
- setFormData(prev => ({
- ...prev,
- [name]: value
- }));
-
- // Clear error for the field being edited
- if (errors[name]) {
- setErrors(prev => ({
- ...prev,
- [name]: ''
- }));
- }
- };
-
- // Form validation
- const validateForm = () => {
- const newErrors = {};
- if (!formData.name.trim()) newErrors.name = 'Name is required';
- if (!formData.status) newErrors.status = 'Status is required';
- return newErrors;
- };
-
- // Handle edit form submission
- const handleEditSubmit = async (e) => {
- e.preventDefault();
-
- const formErrors = validateForm();
- if (Object.keys(formErrors).length > 0) {
- setErrors(formErrors);
- showToast('Please fix the form errors', 'error');
- return;
- }
-
- if (!currentUser || !currentUser.id) {
- showToast('No user selected for update', 'error');
- return;
- }
-
- setSaving(true);
- try {
- // Update the user in the backend
- const response = await updateAdminUser(currentUser.id, {
- name: formData.name,
- email: formData.email,
- is_active: formData.status === 'Active',
- });
-
- if (response && response.status === 'success') {
- // Update the user in the local state
- setUsers(prevUsers =>
- prevUsers.map(user =>
- user.id === currentUser.id
- ? {
- ...user,
- name: formData.name,
- status: formData.status,
- email: formData.email
- }
- : user
- )
- );
-
- showToast('User updated successfully', 'success');
- setIsEditModalOpen(false);
- } else {
- throw new Error(response?.message || 'Failed to update user');
- }
- } catch (error) {
- console.error('Error updating user:', error);
- const errorMessage = error.response?.data?.message || error.message || 'Failed to update user';
- showToast(errorMessage, 'error');
- } finally {
- setSaving(false);
- }
- };
-
export default AdminUsers;
diff --git a/ipi-survey-platform/src/services/configuration/adminService.js b/ipi-survey-platform/src/services/configuration/adminService.js
index e716ec7..548d9ce 100644
--- a/ipi-survey-platform/src/services/configuration/adminService.js
+++ b/ipi-survey-platform/src/services/configuration/adminService.js
@@ -50,4 +50,5 @@ export const deleteUnit = async (id) => {
console.error('Error deleting unit:', error);
throw error;
}
+
};
\ No newline at end of file
diff --git a/ipi-survey-platform/src/services/configuration/unitService.js b/ipi-survey-platform/src/services/configuration/unitService.js
index 2b357be..d18047f 100644
--- a/ipi-survey-platform/src/services/configuration/unitService.js
+++ b/ipi-survey-platform/src/services/configuration/unitService.js
@@ -41,4 +41,4 @@ export const deleteUnit = async (id) => {
console.error('Error deleting unit:', error);
throw error;
}
-};
\ No newline at end of file
+};