bug fixed

This commit is contained in:
Malini 2025-11-06 11:23:55 +05:30
parent 6c20f2d769
commit e5041738a8
5 changed files with 412 additions and 354 deletions

View File

@ -655,7 +655,7 @@ const ProductData = ({
<h2 className="text-xl font-semibold text-[#232528]">Step 2: Product Data Monthly Output & Cost</h2>
{(quarter || year) && (
<div className="text-sm font-medium text-gray-600">
Current Quarter: <span className="text-[#92722A]">Q{quarter}-{year}</span>
Current Quarter: <span className="text-[#92722A]">{quarter}-{year}</span>
</div>
)}
</div>

View File

@ -1,66 +1,87 @@
import React, { useEffect } from 'react';
import { Link, useNavigate, useLocation } from 'react-router-dom';
import AdminHeader from '@/components/admin/AdminHeader';
import QuarterlyWindows from '@/pages/Admin/configuration/QuarterlyWindows';
import IsicHsCodes from '@/pages/Admin/configuration/IsicHsCodes';
import UnitMaster from '@/pages/Admin/configuration/UnitMaster';
import AdminUsers from '@/pages/Admin/configuration/AdminUsers/ListAdminUsers';
import ListAdminUsers from '@/pages/Admin/configuration/AdminUsers/ListAdminUsers';
import EditAdminUser from '@/pages/Admin/configuration/AdminUsers/EditAdminUser';
import CompanyProfile from '@/pages/Admin/configuration/CompanyProfile';
const caretUpSrc = '/assets/images/caret-up.svg';
import React, { useEffect, useState } from "react";
import { Link, useNavigate, useLocation } from "react-router-dom";
import AdminHeader from "@/components/admin/AdminHeader";
import QuarterlyWindows from "@/pages/Admin/configuration/QuarterlyWindows";
import IsicHsCodes from "@/pages/Admin/configuration/IsicHsCodes";
import UnitMaster from "@/pages/Admin/configuration/UnitMaster";
import AdminUsers from "@/pages/Admin/configuration/AdminUsers/ListAdminUsers";
import CompanyProfile from "@/pages/Admin/configuration/CompanyProfile";
const Configuration = () => {
const navigate = useNavigate();
const location = useLocation();
const tabs = ['Quarterly Windows', 'HS Codes', 'Unit Master', 'Admin Users', 'Company Profile'];
const [active, setActive] = React.useState(tabs[0]);
// Update URL when active tab changes
const tabs = [
"Quarterly Windows",
"HS Codes",
"Unit Master",
"Admin Users",
"Company Profile",
];
const [active, setActive] = useState(tabs[0]);
// Map tab names to routes
const tabToPath = {
"Quarterly Windows": "quarterly",
"HS Codes": "hscodes",
"Unit Master": "unitmaster",
"Admin Users": "admin-users",
"Company Profile": "profile",
};
// Map paths to tab names
const pathToTab = {
quarterly: "Quarterly Windows",
hscodes: "HS Codes",
unitmaster: "Unit Master",
"admin-users": "Admin Users",
profile: "Company Profile",
};
// Navigate to URL when tab changes
useEffect(() => {
const tabToPath = {
'Quarterly Windows': 'quarterly',
'HS Codes': 'hscodes',
'Unit Master': 'unitmaster',
'Admin Users': 'admin-users',
'Company Profile': 'profile'
};
if (tabToPath[active]) {
navigate(`/admin/configuration/${tabToPath[active]}`, { replace: true });
}
}, [active, navigate]);
// Update active tab based on URL
useEffect(() => {
const pathToTab = {
'quarterly': 'Quarterly Windows',
'hscodes': 'HS Codes',
'unitmaster': 'Unit Master',
'admin-users': 'Admin Users',
'profile': 'Company Profile'
};
const path = location.pathname.split('/').pop();
if (pathToTab[path] && pathToTab[path] !== active) {
setActive(pathToTab[path]);
}
}, [location.pathname]);
// Update active tab when URL changes
useEffect(() => {
const path = location.pathname.split("/").pop();
if (pathToTab[path] && pathToTab[path] !== active) {
setActive(pathToTab[path]);
}
}, [location.pathname]);
// Breadcrumb with ">" separators
const Breadcrumbs = (
<nav className="mb-6 text-sm text-[#8F9299] flex items-center gap-3">
<Link to="/admin/dashboard" className="flex items-center gap-1 text-[#232528] hover:underline">
<nav className="mb-6 text-sm text-[#8F9299] flex items-center gap-2">
<Link
to="/admin/dashboard"
className="text-[#232528] hover:underline font-medium"
>
Dashboard
<img src={caretUpSrc} alt="Dashboard" className="h-3 w-3" />
</Link>
<span className="flex items-center gap-1 text-[#92722A] font-medium">
<span className="text-[#8F9299]">{">"}</span>
<Link
to="/admin/configuration"
className="text-[#232528] hover:underline font-medium"
>
Configuration
<img src={caretUpSrc} alt="Configuration" className="h-3 w-3" />
</span>
</Link>
{active && (
<>
<span className="text-[#8F9299]">{">"}</span>
<span className="text-[#92722A] font-medium">{active}</span>
</>
)}
</nav>
);
// Tab header
const PageHeader = (
<div className="w-full mb-6">
<div className="h-14 w-full border-b border-[#E5E7EB] flex items-end">
@ -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(() => {
</div>
);
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 <QuarterlyWindows />;
case "HS Codes":
return <IsicHsCodes />;
case "Unit Master":
return <UnitMaster />;
case "Admin Users":
return <AdminUsers />;
case "Company Profile":
return <CompanyProfile />;
default:
return (
<div className="bg-white w-full rounded-lg shadow-sm ring-1 ring-gray-200">
<div className="px-6 py-10 text-sm text-[#5F646D]">
{active} content coming soon
</div>
</div>
);
}
};
return (
<div className="min-h-screen bg-[#F7F7F7]">
@ -96,22 +135,7 @@ useEffect(() => {
<div className="max-w-[1280px] mx-auto px-4 py-6">
{Breadcrumbs}
{PageHeader}
{isQuarterTab && <QuarterlyWindows />}
{isIsicTab && <IsicHsCodes />}
{isUnitMasterTab && <UnitMaster />}
{isAdminTab && <AdminUsers />}
{isCompanyProfileTab && <CompanyProfile />}
{!isQuarterTab && !isIsicTab && !isUnitMasterTab && !isAdminTab && !isCompanyProfileTab && (
<div className="bg-white w-full rounded-lg shadow-sm ring-1 ring-gray-200">
<div className="px-6 py-10 text-sm text-[#5F646D]">{active} content coming soon</div>
</div>
)}
{renderActiveTab()}
</div>
</div>
);

View File

@ -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' }) => (
<div className="flex min-h-[96px] min-w-[220px] flex-1 items-center justify-between rounded-[16px] border border-[#E6EAF5] bg-white px-6 py-5 shadow-[0_12px_24px_rgba(15,23,42,0.06)]">
@ -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 (
<span
className={`inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium ${
map[status] || map.Closed
map[status] || map.Inactive
}`}
>
{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 (
<div className="space-y-5">
{/* Summary cards */}
{/* Summary cards */}
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
{summaryItems.map((item) => (
<SummaryCard key={item.label} {...item} />
))}
</div>
{/* Table */}
{/* Table */}
<Table
headers={headers}
columnWidths={columnWidths}
@ -280,11 +428,7 @@ const AdminUsers = () => {
onClick={() => handleEdit(rowIndex)}
>
<img
src={
editingRow === rowIndex
? pencilActiveSrc
: pencilInactiveSrc
}
src={editingRow === rowIndex ? pencilActiveSrc : pencilInactiveSrc}
alt="Edit"
className="h-4 w-4"
/>
@ -296,37 +440,90 @@ const AdminUsers = () => {
onClick={() => handleDelete(rowIndex)}
>
<img
src={
deletingRow === rowIndex
? trashActiveSrc
: trashInactiveSrc
}
src={deletingRow === rowIndex ? trashActiveSrc : trashInactiveSrc}
alt="Delete"
className="h-4 w-4"
/>
</button>
<button
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}
>
<img
src={
isActive
? resetPasswordInactiveIconSrc
: resetPasswordInactiveIconSrc
}
alt="Reset password"
className="h-4 w-4"
/>
</button>
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)}
>
<img
src={isActive ? resetPasswordIconSrc : resetPasswordInactiveIconSrc}
alt="Reset password"
className="h-4 w-4"
/>
</button>
{isResetModalOpen && (
<div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/10">
<div className="bg-white rounded-lg w-[450px] shadow-lg p-6 relative">
{/* Close Button */}
<button
className="absolute top-3 right-3 text-gray-500 hover:text-gray-800 text-xl"
onClick={handleCloseResetModal}
>
</button>
{/* Header */}
<h3 className="text-xl font-semibold text-gray-800 mb-6">Reset Password</h3>
{/* Input Fields */}
<div className="flex flex-col gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">New Password</label>
<input
type="password"
placeholder="Enter your new password"
className="w-full border border-[#D9C9A3] rounded-md px-3 py-2 focus:outline-none focus:ring-1 focus:ring-[#B68A35]"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Confirm Password</label>
<input
type="password"
placeholder="Re-enter your new password"
className="w-full border border-[#D9C9A3] rounded-md px-3 py-2 focus:outline-none focus:ring-1 focus:ring-[#B68A35]"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
</div>
</div>
{/* Buttons */}
<div className="flex justify-end gap-3 mt-6">
<button
className="px-5 py-2 rounded-md border border-gray-300 text-gray-700 hover:bg-gray-100"
onClick={handleCloseResetModal}
>
Cancel
</button>
<button
className="px-5 py-2 rounded-md bg-[#B68A35] text-white font-medium hover:bg-[#9c6f28]"
onClick={() => {
console.log({ newPassword, confirmPassword, user: resetUser });
handleCloseResetModal();
}}
>
Reset Password
</button>
</div>
</div>
</div>
)}
</div>
);
}
@ -334,206 +531,42 @@ const AdminUsers = () => {
}}
/>
{/* Add User Modal */}
{/* Add User Modal */}
{isAddUserModalOpen && (
<AddAdminUsers
isOpen={isAddUserModalOpen}
onClose={() => 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 && (
<div className="fixed inset-0 bg-black/30 backdrop-blur-sm flex items-center justify-center p-4 z-[9998]">
<div className="bg-white rounded-2xl w-full max-w-2xl max-h-[90vh] overflow-y-auto">
{/* Header */}
<div className="flex justify-between items-center p-6 border-b sticky top-0 bg-white z-10">
<h2 className="text-xl font-semibold text-[#232528]">Edit User</h2>
<button
type="button"
onClick={() => setIsEditModalOpen(false)}
className="text-gray-400 hover:text-gray-500"
aria-label="Close"
>
<X className="h-5 w-5" />
</button>
</div>
{/* Body */}
<div className="p-8">
<form onSubmit={handleEditSubmit}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<TextField
label="Name"
name="name"
value={formData.name}
onChange={handleFormChange}
error={errors.name}
required
placeholder="Enter name"
/>
<TextField
label="Email"
name="email"
type="email"
value={formData.email}
onChange={handleFormChange}
error={errors.email}
required
placeholder="Enter email"
disabled={true}
/>
<div className="md:col-span-2 space-y-2">
<label className="block text-sm font-medium text-gray-700 mb-1">
Status <span className="text-red-500">*</span>
</label>
<SelectField
name="status"
value={formData.status}
onChange={handleFormChange}
options={[
{ label: 'Active', value: 'Active' },
{ label: 'Inactive', value: 'Inactive' },
]}
error={errors.status}
/>
{errors.status && (
<p className="mt-1 text-sm text-red-600">{errors.status}</p>
)}
</div>
</div>
{/* Footer */}
<div className="flex justify-end space-x-3 pt-6">
<button
type="button"
onClick={() => setIsEditModalOpen(false)}
className="px-5 py-2.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#B68A35] transition-colors"
>
Cancel
</button>
<button
type="submit"
disabled={saving}
className={`px-5 py-2.5 text-sm font-medium text-white rounded-lg focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#B68A35] transition-colors ${
saving
? 'bg-gray-400 cursor-not-allowed'
: 'bg-[#92722A] hover:bg-[#9A762D]'
}`}
>
{saving ? 'Saving...' : 'Update'}
</button>
</div>
</form>
</div>
</div>
</div>
)}
{/* ✅ Toast Notification */}
{/* Toast */}
{toastData && (
<div className="fixed inset-0 z-[9999]">
<CustomToast
message={toastData.message}
type={toastData.type}
onClose={() => setToastData(null)}
/>
<div className="fixed inset-0 z-[9999] pointer-events-none">
<div className="absolute right-4 top-4 pointer-events-auto">
<CustomToast
message={toastData.message}
type={toastData.type}
onClose={() => setToastData(null)}
/>
</div>
</div>
)}
</div>
);
};
// 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;

View File

@ -50,4 +50,5 @@ export const deleteUnit = async (id) => {
console.error('Error deleting unit:', error);
throw error;
}
};

View File

@ -41,4 +41,4 @@ export const deleteUnit = async (id) => {
console.error('Error deleting unit:', error);
throw error;
}
};
};