bug fixed in admin profile
This commit is contained in:
parent
b25e35d4c9
commit
d0d8bd13f3
@ -170,15 +170,29 @@ const ProductDetails = ({ product, onBack, showToast: shouldShowToast, onToastSh
|
|||||||
<span className="text-sm font-medium">{toast.message}</span>
|
<span className="text-sm font-medium">{toast.message}</span>
|
||||||
{toast.type === 'rejected' && (
|
{toast.type === 'rejected' && (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// Add your resubmit logic here
|
navigate('/survey', {
|
||||||
// You can navigate to the edit page or trigger a resubmit function
|
state: {
|
||||||
}}
|
submission: {
|
||||||
className="ml-auto px-3 py-1 bg-[#FEF2F2] text-[#7C2320] text-sm font-medium rounded hover:bg-[#FEF2F2] transition-colors"
|
id: product.submission_id,
|
||||||
>
|
quarter: product.quarter,
|
||||||
Resubmit
|
year: product.year,
|
||||||
<img src="/assets/images/arrow-right-bold 1.svg" alt="" className="w-4 h-4 ml-1 inline-block" />
|
establishment_name: product.establishment_name,
|
||||||
</button>
|
permanent_factory_code: product.permanent_factory_code,
|
||||||
|
industry_code: product.industry_code,
|
||||||
|
license_number: product.license_number,
|
||||||
|
isic_code: product.isic_code,
|
||||||
|
emirate: product.emirate
|
||||||
|
},
|
||||||
|
isResubmit: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className="ml-auto px-3 py-1 bg-[#FEF2F2] text-[#7C2320] text-sm font-medium rounded hover:bg-[#FEF2F2] transition-colors"
|
||||||
|
>
|
||||||
|
Resubmit
|
||||||
|
<img src="/assets/images/arrow-right-bold 1.svg" alt="" className="w-4 h-4 ml-1 inline-block" />
|
||||||
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -735,6 +735,7 @@ const ReviewSubmit = ({
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<textarea
|
<textarea
|
||||||
placeholder="Enter your remarks here..."
|
placeholder="Enter your remarks here..."
|
||||||
|
readOnly
|
||||||
value={remarks}
|
value={remarks}
|
||||||
onChange={(e) => setRemarks(e.target.value)}
|
onChange={(e) => setRemarks(e.target.value)}
|
||||||
className="w-full min-h-[100px] p-3 border border-#CBA344 rounded-md focus:ring-2 focus:ring-[#CBA344] focus:border-transparent"
|
className="w-full min-h-[100px] p-3 border border-#CBA344 rounded-md focus:ring-2 focus:ring-[#CBA344] focus:border-transparent"
|
||||||
|
|||||||
@ -0,0 +1,40 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { AlertTriangle } from "lucide-react";
|
||||||
|
|
||||||
|
const DeleteUserModal = ({ userName, onClose, onDelete }) => {
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/20">
|
||||||
|
<div className="bg-white rounded-lg w-[420px] p-6 shadow-lg relative">
|
||||||
|
<div className="flex items-center gap-3 mb-4">
|
||||||
|
<div className="bg-red-100 p-2 rounded-full">
|
||||||
|
<AlertTriangle className="text-red-500" size={20} />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-semibold text-gray-800">Delete User</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-gray-600 mb-6">
|
||||||
|
Are you sure you want to delete{" "}
|
||||||
|
<span className="font-semibold text-gray-900">{userName}</span>?<br />
|
||||||
|
This action will permanently delete the data and cannot be undone.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-3">
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="px-5 py-2 rounded-md border border-gray-300 text-gray-700 hover:bg-gray-100"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={onDelete}
|
||||||
|
className="px-5 py-2 rounded-md bg-[#92722A] text-white font-medium"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeleteUserModal;
|
||||||
@ -0,0 +1,102 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const EditUserModal = ({ formData, setFormData, onClose, onUpdate }) => {
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/10">
|
||||||
|
<div className="bg-white rounded-lg w-[480px] shadow-lg p-6 relative">
|
||||||
|
{/* Close Button */}
|
||||||
|
<button
|
||||||
|
className="absolute top-3 right-3 text-gray-500 hover:text-gray-800"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Header */}
|
||||||
|
<h3 className="text-xl font-semibold text-gray-800 mb-6">Edit User</h3>
|
||||||
|
|
||||||
|
{/* Form */}
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onUpdate();
|
||||||
|
}}
|
||||||
|
className="flex flex-col gap-4"
|
||||||
|
>
|
||||||
|
{/* Name & Email in one row */}
|
||||||
|
<div className="flex gap-4">
|
||||||
|
{/* Name */}
|
||||||
|
<div className="flex-1">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Name <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter Name"
|
||||||
|
className="w-full border border-[#D9C9A3] rounded-md px-3 py-2 focus:ring-1 focus:ring-[#B68A35]"
|
||||||
|
value={formData.name}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormData((prev) => ({ ...prev, name: e.target.value }))
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Email */}
|
||||||
|
<div className="flex-1">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Email <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
placeholder="Enter Email"
|
||||||
|
className="w-full border border-[#D9C9A3] rounded-md px-3 py-2 focus:ring-1 focus:ring-[#B68A35]"
|
||||||
|
value={formData.email}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormData((prev) => ({ ...prev, email: e.target.value }))
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Status */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Status
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
className="w-full border border-[#D9C9A3] rounded-md px-3 py-2 focus:ring-1 focus:ring-[#B68A35]"
|
||||||
|
value={formData.status}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormData((prev) => ({ ...prev, status: e.target.value }))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="Active">Active</option>
|
||||||
|
<option value="Inactive">Inactive</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Buttons */}
|
||||||
|
<div className="flex justify-end gap-3 mt-6">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="px-5 py-2 rounded-md border border-gray-300 text-gray-700 hover:bg-gray-100"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="px-5 py-2 rounded-md bg-[#B68A35] text-white font-medium hover:bg-[#9c6f28]"
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditUserModal;
|
||||||
@ -6,11 +6,14 @@ import {
|
|||||||
getAdminUser,
|
getAdminUser,
|
||||||
getAdminUserById,
|
getAdminUserById,
|
||||||
updateAdminUser,
|
updateAdminUser,
|
||||||
deleteAdminUser
|
deleteAdminUser, // ✅ Added import
|
||||||
} from '@/services/configuration/adminService';
|
} from '@/services/configuration/adminService';
|
||||||
|
|
||||||
import CustomToast from '@/components/common/CustomToast';
|
import CustomToast from '@/components/common/CustomToast';
|
||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
import { TextField, SelectField } from '@/components/common/FormControls';
|
import { TextField, SelectField } from '@/components/common/FormControls';
|
||||||
|
import EditUserModal from './EditUserModal';
|
||||||
|
import DeleteUserModal from './DeleteUserModal';
|
||||||
|
|
||||||
const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
|
const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
|
||||||
const exportIconSrc = '/assets/images/DownloadSimple.svg';
|
const exportIconSrc = '/assets/images/DownloadSimple.svg';
|
||||||
@ -63,11 +66,14 @@ const AdminUsers = () => {
|
|||||||
const [currentUser, setCurrentUser] = useState(null);
|
const [currentUser, setCurrentUser] = useState(null);
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
const [editingRow, setEditingRow] = useState(null);
|
const [editingRow, setEditingRow] = useState(null);
|
||||||
const [deletingRow, setDeletingRow] = useState(null);
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const pageSize = 10;
|
const pageSize = 10;
|
||||||
|
|
||||||
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||||
|
const [selectedUser, setSelectedUser] = useState(null);
|
||||||
|
const [deletingRow, setDeletingRow] = useState(null);
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
@ -202,6 +208,54 @@ const [confirmPassword, setConfirmPassword] = useState('');
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleUpdateUser = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const updatedUser = {
|
||||||
|
name: formData.name,
|
||||||
|
email: formData.email,
|
||||||
|
is_active: formData.status === "Active", // ✅ status handled via is_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);
|
||||||
|
|
||||||
|
// ✅ update UI
|
||||||
|
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
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
setCurrentUser(null);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating user:", error);
|
||||||
|
showToast(error?.response?.data?.message || "Failed to update user", "error");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors = {};
|
const newErrors = {};
|
||||||
if (!formData.name || !formData.name.trim()) newErrors.name = 'Name is required';
|
if (!formData.name || !formData.name.trim()) newErrors.name = 'Name is required';
|
||||||
@ -209,49 +263,70 @@ const [confirmPassword, setConfirmPassword] = useState('');
|
|||||||
return newErrors;
|
return newErrors;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEdit = async (index) => {
|
const handleDeleteClick = (user) => {
|
||||||
const user = users[index];
|
setSelectedUser(user);
|
||||||
if (!user || !user.id) {
|
setShowDeleteModal(true);
|
||||||
showToast('Invalid user selected', 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setEditingRow(index);
|
|
||||||
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,
|
|
||||||
name,
|
|
||||||
email,
|
|
||||||
is_active,
|
|
||||||
});
|
|
||||||
|
|
||||||
setFormData({
|
|
||||||
name,
|
|
||||||
email,
|
|
||||||
status: is_active ? 'Active' : 'Inactive',
|
|
||||||
});
|
|
||||||
|
|
||||||
setErrors({});
|
|
||||||
setIsEditModalOpen(true);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching user data:', error);
|
|
||||||
showToast(error?.response?.data?.message || 'Failed to load user data', 'error');
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
const handleConfirmDelete = async () => {
|
||||||
|
try {
|
||||||
|
if (!selectedUser) return;
|
||||||
|
setDeletingRow(selectedUser.id);
|
||||||
|
await deleteAdminUser(selectedUser.id); // 🔥 API call
|
||||||
|
|
||||||
|
showToast("User deleted successfully!", "success"); // ✅ use showToast()
|
||||||
|
|
||||||
|
setUsers((prev) => prev.filter((u) => u.id !== selectedUser.id));
|
||||||
|
} catch (error) {
|
||||||
|
showToast("Failed to delete user!", "error"); // ✅ same toast handler for error
|
||||||
|
} finally {
|
||||||
|
setDeletingRow(null);
|
||||||
|
setShowDeleteModal(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEdit = async (user) => {
|
||||||
|
if (!user || !user.id) {
|
||||||
|
showToast('Invalid user selected', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEditingRow(user); // store which row is being edited
|
||||||
|
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,
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
is_active,
|
||||||
|
});
|
||||||
|
|
||||||
|
setFormData({
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
status: is_active ? 'Active' : 'Inactive',
|
||||||
|
});
|
||||||
|
|
||||||
|
setErrors({});
|
||||||
|
setIsEditModalOpen(true); // ✅ opens your modal
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching user data:', error);
|
||||||
|
showToast(error?.response?.data?.message || 'Failed to load user data', 'error');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleOpenResetModal = (user) => {
|
const handleOpenResetModal = (user) => {
|
||||||
setResetUser(user);
|
setResetUser(user);
|
||||||
@ -266,40 +341,12 @@ const handleCloseResetModal = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// ✅ DELETE FUNCTIONALITY IMPLEMENTED HERE
|
// ✅ DELETE FUNCTIONALITY IMPLEMENTED HERE
|
||||||
const handleDelete = async (index) => {
|
const handleDelete = (rowIndex) => {
|
||||||
const user = users[index];
|
const user = users[rowIndex];
|
||||||
if (!user || !user.id) {
|
setSelectedUser(user);
|
||||||
showToast('Invalid user selected for delete', 'error');
|
setShowDeleteModal(true);
|
||||||
return;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const confirmDelete = window.confirm(`Are you sure you want to delete "${user.name}"?`);
|
|
||||||
if (!confirmDelete) return;
|
|
||||||
|
|
||||||
setDeletingRow(index);
|
|
||||||
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
|
// ✅ END DELETE FUNCTIONALITY
|
||||||
|
|
||||||
const handleEditSubmit = async (e) => {
|
const handleEditSubmit = async (e) => {
|
||||||
@ -376,10 +423,49 @@ const handleCloseResetModal = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 sm:justify-end">
|
<div className="flex items-center gap-2 sm:justify-end">
|
||||||
<button className="h-9 px-3 rounded-md bg-[#F7F7F7] border border-[#C3C6CB] text-sm inline-flex items-center gap-2 cursor-pointer">
|
<button
|
||||||
<img src={exportIconSrc} alt="Export" className="h-4 w-4" />
|
type="button"
|
||||||
<span>Export CSV</span>
|
onClick={() => {
|
||||||
</button>
|
if (!users.length) return;
|
||||||
|
|
||||||
|
// Define CSV headers (excluding date/time column logic)
|
||||||
|
const csvHeader = ['Name', 'Email', 'Status', 'Last Login'].join(',');
|
||||||
|
|
||||||
|
// Map user data into CSV rows
|
||||||
|
const csvRows = users.map((user) =>
|
||||||
|
[user.name, user.email, user.status, user.lastLogin]
|
||||||
|
.map((value) => `"${String(value ?? '').replace(/"/g, '""')}"`)
|
||||||
|
.join(',')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create the CSV file content
|
||||||
|
const blob = new Blob([csvHeader + '\n' + csvRows.join('\n')], {
|
||||||
|
type: 'text/csv;charset=utf-8;',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create download link
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.setAttribute('download', 'admin-users.csv'); // 👈 No date in filename
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}}
|
||||||
|
className={`h-9 px-3 rounded-md border text-sm inline-flex items-center gap-2 ${
|
||||||
|
users.length
|
||||||
|
? 'bg-[#F7F7F7] border-[#C3C6CB] text-[#232528]'
|
||||||
|
: 'bg-[#F3F4F6] border-[#E5E7EB] text-[#9CA3AF] cursor-not-allowed'
|
||||||
|
}`}
|
||||||
|
disabled={!users.length}
|
||||||
|
>
|
||||||
|
<img src={exportIconSrc} alt="Export" className="h-4 w-4" />
|
||||||
|
<span>Export CSV</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsAddUserModalOpen(true)}
|
onClick={() => setIsAddUserModalOpen(true)}
|
||||||
className="h-9 px-3 rounded-md bg-[#B68A35] text-white text-sm inline-flex items-center gap-2 cursor-pointer"
|
className="h-9 px-3 rounded-md bg-[#B68A35] text-white text-sm inline-flex items-center gap-2 cursor-pointer"
|
||||||
@ -422,29 +508,31 @@ const handleCloseResetModal = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="h-8 w-8 grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
className="h-8 w-8 grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
||||||
title="Edit"
|
title="Edit"
|
||||||
onClick={() => handleEdit(rowIndex)}
|
onClick={() => handleEdit(user)} // pass user directly instead of rowIndex
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={editingRow === rowIndex ? pencilActiveSrc : pencilInactiveSrc}
|
src={editingRow?.id === user.id ? pencilActiveSrc : pencilInactiveSrc}
|
||||||
alt="Edit"
|
alt="Edit"
|
||||||
className="h-4 w-4"
|
className="h-4 w-4"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
type="button"
|
<button
|
||||||
className="h-8 w-8 grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
type="button"
|
||||||
title="Delete"
|
className="h-8 w-8 grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
||||||
onClick={() => handleDelete(rowIndex)}
|
title="Delete"
|
||||||
>
|
onClick={() => handleDelete(rowIndex)}
|
||||||
<img
|
>
|
||||||
src={deletingRow === rowIndex ? trashActiveSrc : trashInactiveSrc}
|
<img
|
||||||
alt="Delete"
|
src={deletingRow === rowIndex ? trashActiveSrc : trashInactiveSrc}
|
||||||
className="h-4 w-4"
|
alt="Delete"
|
||||||
/>
|
className="h-4 w-4"
|
||||||
</button>
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -523,6 +611,26 @@ const handleCloseResetModal = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
|
{/* Edit Modal */}
|
||||||
|
{isEditModalOpen && (
|
||||||
|
<EditUserModal
|
||||||
|
formData={formData}
|
||||||
|
setFormData={setFormData}
|
||||||
|
onClose={() => setIsEditModalOpen(false)}
|
||||||
|
onUpdate={handleUpdateUser}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Delete Modal */}
|
||||||
|
{showDeleteModal && (
|
||||||
|
<DeleteUserModal
|
||||||
|
userName={selectedUser?.name}
|
||||||
|
onClose={() => setShowDeleteModal(false)}
|
||||||
|
onDelete={handleConfirmDelete}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -559,9 +559,17 @@ const CompanyProfile = () => {
|
|||||||
<TextField
|
<TextField
|
||||||
label="Postal Code"
|
label="Postal Code"
|
||||||
value={form.contactPostalCode}
|
value={form.contactPostalCode}
|
||||||
onChange={handleFormChange('contactPostalCode')}
|
onChange={(e) => {
|
||||||
|
// Only allow numeric input
|
||||||
|
if (e.target.value === '' || /^[0-9\b]+$/.test(e.target.value)) {
|
||||||
|
handleFormChange('contactPostalCode')(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
placeholder="Enter postal code"
|
placeholder="Enter postal code"
|
||||||
width="100%"
|
width="100%"
|
||||||
|
type="tel"
|
||||||
|
inputMode="numeric"
|
||||||
|
pattern="[0-9]*"
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="PO Box"
|
label="PO Box"
|
||||||
@ -704,9 +712,17 @@ const CompanyProfile = () => {
|
|||||||
<TextField
|
<TextField
|
||||||
label="Postal Code"
|
label="Postal Code"
|
||||||
value={form.corporatePostalCode}
|
value={form.corporatePostalCode}
|
||||||
onChange={handleFormChange('corporatePostalCode')}
|
onChange={(e) => {
|
||||||
|
// Only allow numeric input
|
||||||
|
if (e.target.value === '' || /^[0-9\b]+$/.test(e.target.value)) {
|
||||||
|
handleFormChange('corporatePostalCode')(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
placeholder="Enter postal code"
|
placeholder="Enter postal code"
|
||||||
width="100%"
|
width="100%"
|
||||||
|
type="tel"
|
||||||
|
inputMode="numeric"
|
||||||
|
pattern="[0-9]*"
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="PO Box"
|
label="PO Box"
|
||||||
|
|||||||
@ -311,8 +311,10 @@ const filteredRows = useMemo(() => {
|
|||||||
: hscodeFilter.trim();
|
: hscodeFilter.trim();
|
||||||
return entry.product_name?.toLowerCase().includes(productName.toLowerCase());
|
return entry.product_name?.toLowerCase().includes(productName.toLowerCase());
|
||||||
})();
|
})();
|
||||||
|
const matchesStatus = statusFilter === 'all' ||
|
||||||
|
entry.status?.toLowerCase() === statusFilter.toLowerCase();
|
||||||
|
|
||||||
return matchesSearch && matchesYear && matchesQuarter && matchesHscode;
|
return matchesSearch && matchesYear && matchesQuarter && matchesHscode && matchesStatus;
|
||||||
});
|
});
|
||||||
|
|
||||||
setPagination((prev) => ({
|
setPagination((prev) => ({
|
||||||
@ -322,7 +324,7 @@ const filteredRows = useMemo(() => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
}, [auditHistory, searchTerm, yearFilter, quarterFilter, hscodeFilter]);
|
}, [auditHistory, searchTerm, yearFilter, quarterFilter, hscodeFilter,statusFilter]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -42,12 +42,40 @@ export const getAdminUserById = async (id) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// export const deleteAdminUser = async (id) => {
|
||||||
|
// try {
|
||||||
|
// const response = await deleteRequest(`${admin}/${id}`);
|
||||||
|
// return response.data;
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('Error deleting admin user:', error);
|
||||||
|
// throw error;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
export const deleteAdminUser = async (id) => {
|
export const deleteAdminUser = async (id) => {
|
||||||
try {
|
try {
|
||||||
|
if (!id) throw new Error("Invalid user ID");
|
||||||
|
|
||||||
const response = await deleteRequest(`${admin}/${id}`);
|
const response = await deleteRequest(`${admin}/${id}`);
|
||||||
return response.data;
|
|
||||||
|
// Log for debugging
|
||||||
|
|
||||||
|
// Safely check success from API
|
||||||
|
const resData = response?.data || response;
|
||||||
|
|
||||||
|
const success =
|
||||||
|
resData?.status === "success" ||
|
||||||
|
resData?.success === true ||
|
||||||
|
resData?.code === 200 ||
|
||||||
|
resData?.message?.toLowerCase()?.includes("deleted");
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw new Error("Delete API did not return success");
|
||||||
|
}
|
||||||
|
|
||||||
|
return resData;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting admin user:', error);
|
console.error("Error deleting admin user:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { getRequest, postRequest } from '@/services/api/CommonService';
|
import { getRequest, postRequest } from '@/services/api/CommonService';
|
||||||
import resolveEstablishmentId from '@/services/utils/establishment';
|
import resolveEstablishmentId from '@/services/utils/establishment';
|
||||||
|
import { putRequest } from '../api/CommonService';
|
||||||
|
|
||||||
const endpoint = '/submissions';
|
const endpoint = '/submissions';
|
||||||
const QUARTER_PERIODS_ENDPOINT = '/getQuarterPeriods';
|
const QUARTER_PERIODS_ENDPOINT = '/getQuarterPeriods';
|
||||||
@ -32,6 +33,23 @@ export const submitSurvey = async (payload = {}, config = {}) => {
|
|||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const resubmitSurvey = async (submissionId, payload = {}, config = {}) => {
|
||||||
|
if (!submissionId) {
|
||||||
|
throw new Error('Submission ID is required for resubmission');
|
||||||
|
}
|
||||||
|
|
||||||
|
const establishmentId = resolveEstablishmentId(payload.establishment_id);
|
||||||
|
const requestBody = {
|
||||||
|
...payload,
|
||||||
|
...(establishmentId !== undefined ? { establishment_id: establishmentId } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = `${endpoint}/${submissionId}`;
|
||||||
|
// Use putRequest instead of postRequest with method override
|
||||||
|
const response = await putRequest(url, requestBody, config);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
export const fetchSubmissionHistory = async (establishmentId, config = {}) => {
|
export const fetchSubmissionHistory = async (establishmentId, config = {}) => {
|
||||||
const resolvedId = resolveEstablishmentId(establishmentId);
|
const resolvedId = resolveEstablishmentId(establishmentId);
|
||||||
if (resolvedId === undefined) {
|
if (resolvedId === undefined) {
|
||||||
@ -126,6 +144,7 @@ export const getSubmissionAuditHistory = async (establishmentId, params = {}) =>
|
|||||||
export default {
|
export default {
|
||||||
getSubmissions,
|
getSubmissions,
|
||||||
submitSurvey,
|
submitSurvey,
|
||||||
|
resubmitSurvey,
|
||||||
fetchSubmissionHistory,
|
fetchSubmissionHistory,
|
||||||
fetchSubmissionDetail,
|
fetchSubmissionDetail,
|
||||||
getQuarterPeriods,
|
getQuarterPeriods,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user