import bug fixed
This commit is contained in:
parent
722d9fcaae
commit
142bd4f21f
@ -77,7 +77,7 @@ const AddAdminUsers = ({ isOpen, onClose, onSave }) => {
|
||||
if (!/[a-z]/.test(password)) return 'Must contain at least one lowercase letter';
|
||||
if (!/\d/.test(password)) return 'Must contain at least one number';
|
||||
if (!/[!@#$%^&*]/.test(password)) return 'Must contain at least one special character (!@#$%^&*)';
|
||||
return '';
|
||||
return ''; // Return empty string if password is valid
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
@ -85,20 +85,27 @@ const AddAdminUsers = ({ isOpen, onClose, onSave }) => {
|
||||
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||
|
||||
// Clear any existing error for this field
|
||||
if (errors[name]) setErrors((prev) => ({ ...prev, [name]: '' }));
|
||||
if (errors[name]) {
|
||||
setErrors(prev => ({ ...prev, [name]: '' }));
|
||||
}
|
||||
|
||||
// Real-time password validation
|
||||
// Handle password field changes
|
||||
if (name === 'password') {
|
||||
setPasswordTouched(true);
|
||||
const error = validatePassword(value);
|
||||
setPasswordError(error);
|
||||
|
||||
// If password is valid and matches confirm password, clear confirm password error
|
||||
if (!error && formData.confirmPassword && value === formData.confirmPassword) {
|
||||
setErrors(prev => ({ ...prev, confirmPassword: '' }));
|
||||
}
|
||||
}
|
||||
|
||||
// Clear confirm password error when either password changes
|
||||
if ((name === 'password' || name === 'confirmPassword') && formData.password && formData.confirmPassword) {
|
||||
if (formData.password !== formData.confirmPassword) {
|
||||
// Handle confirm password field changes
|
||||
if (name === 'confirmPassword') {
|
||||
if (value && formData.password !== value) {
|
||||
setErrors(prev => ({ ...prev, confirmPassword: 'Passwords do not match' }));
|
||||
} else {
|
||||
} else if (value && formData.password === value) {
|
||||
setErrors(prev => ({ ...prev, confirmPassword: '' }));
|
||||
}
|
||||
}
|
||||
@ -211,9 +218,9 @@ const AddAdminUsers = ({ isOpen, onClose, onSave }) => {
|
||||
placeholder="Confirm password"
|
||||
showToggle
|
||||
/>
|
||||
{formData.confirmPassword && !errors.confirmPassword && (
|
||||
{/* {formData.confirmPassword && !errors.confirmPassword && (
|
||||
<p className="mt-1 text-xs text-green-600">Passwords match</p>
|
||||
)}
|
||||
)} */}
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-2 space-y-2">
|
||||
|
||||
@ -17,10 +17,7 @@ const EditUserModal = ({ formData, setFormData, onClose, onUpdate }) => {
|
||||
|
||||
{/* Form */}
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
onUpdate();
|
||||
}}
|
||||
onSubmit={onUpdate}
|
||||
className="flex flex-col gap-4"
|
||||
>
|
||||
{/* Name & Email in one row */}
|
||||
|
||||
@ -104,11 +104,13 @@ const AdminUsers = () => {
|
||||
}, 4000);
|
||||
};
|
||||
|
||||
// Fetch users
|
||||
// Fetch users with pagination
|
||||
useEffect(() => {
|
||||
const fetchUsers = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
// Pass currentPage and pageSize to the API if it supports pagination
|
||||
// If not, we'll handle pagination client-side
|
||||
const res = await getAdminUser();
|
||||
|
||||
const usersData = Array.isArray(res)
|
||||
@ -117,7 +119,7 @@ const AdminUsers = () => {
|
||||
? res.data
|
||||
: Array.isArray(res?.data?.data)
|
||||
? res.data.data
|
||||
: null;
|
||||
: [];
|
||||
|
||||
if (!usersData) {
|
||||
showToast('Failed to load users', 'error');
|
||||
@ -163,7 +165,7 @@ const AdminUsers = () => {
|
||||
};
|
||||
|
||||
fetchUsers();
|
||||
}, []);
|
||||
}, [currentPage]); // Add currentPage as a dependency
|
||||
|
||||
const totalUsers = users.length;
|
||||
const activeUsers = users.filter((user) => user.status === 'Active').length;
|
||||
@ -294,38 +296,41 @@ const AdminUsers = () => {
|
||||
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');
|
||||
setLoading(true);
|
||||
|
||||
// Fetch the latest user data from the API
|
||||
const response = await getAdminUserById(user.id);
|
||||
const userData = response?.data?.data || response?.data || response;
|
||||
|
||||
if (!userData) {
|
||||
throw new Error('Failed to fetch user data');
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
// Map the API response to match our form data structure
|
||||
const userToEdit = {
|
||||
id: userData.id || userData._id || user.id,
|
||||
name: userData.name || userData.fullName || '',
|
||||
email: userData.email || '',
|
||||
status: userData.is_active ? 'Active' : 'Inactive',
|
||||
is_active: userData.is_active !== undefined ? userData.is_active : true
|
||||
};
|
||||
|
||||
// Set the current user and form data
|
||||
setCurrentUser(userToEdit);
|
||||
setFormData({
|
||||
name,
|
||||
email,
|
||||
status: is_active ? 'Active' : 'Inactive',
|
||||
name: userToEdit.name,
|
||||
email: userToEdit.email,
|
||||
status: userToEdit.status,
|
||||
});
|
||||
|
||||
setErrors({});
|
||||
|
||||
setEditingRow(userToEdit);
|
||||
setIsEditModalOpen(true);
|
||||
setErrors({});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching user data:', error);
|
||||
showToast(error?.response?.data?.message || 'Failed to load user data', 'error');
|
||||
showToast('Failed to load user data. Please try again.', 'error');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@ -476,13 +481,21 @@ const AdminUsers = () => {
|
||||
};
|
||||
|
||||
const handleDelete = (rowIndex) => {
|
||||
const user = users[rowIndex];
|
||||
setSelectedUser(user);
|
||||
setShowDeleteModal(true);
|
||||
// Calculate the actual index in the full users array based on pagination
|
||||
const actualIndex = (currentPage - 1) * pageSize + rowIndex;
|
||||
const user = users[actualIndex];
|
||||
if (user) {
|
||||
setSelectedUser(user);
|
||||
setShowDeleteModal(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
// Prevent default form submission if event is provided
|
||||
if (e && e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
const formErrors = validateForm();
|
||||
if (Object.keys(formErrors).length > 0) {
|
||||
setErrors(formErrors);
|
||||
@ -512,6 +525,7 @@ const AdminUsers = () => {
|
||||
|
||||
if (!success) throw new Error(res?.message || 'Failed to update user');
|
||||
|
||||
// Update the users list with the updated user data
|
||||
setUsers((prev) =>
|
||||
prev.map((u) =>
|
||||
u.id === currentUser.id
|
||||
@ -521,6 +535,13 @@ const AdminUsers = () => {
|
||||
email: formData.email,
|
||||
status: formData.status,
|
||||
is_active: formData.status === 'Active',
|
||||
lastLogin: u.lastLogin, // Preserve the last login time
|
||||
raw: {
|
||||
...(u.raw || {}),
|
||||
name: formData.name,
|
||||
email: formData.email,
|
||||
is_active: formData.status === 'Active'
|
||||
}
|
||||
}
|
||||
: u
|
||||
)
|
||||
@ -530,6 +551,13 @@ const AdminUsers = () => {
|
||||
setIsEditModalOpen(false);
|
||||
setEditingRow(null);
|
||||
setCurrentUser(null);
|
||||
|
||||
// Reset form data
|
||||
setFormData({
|
||||
name: '',
|
||||
email: '',
|
||||
status: 'Active',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error updating user:', error);
|
||||
showToast(error?.response?.data?.message || 'Failed to update user', 'error');
|
||||
@ -630,7 +658,11 @@ const AdminUsers = () => {
|
||||
}}
|
||||
renderCell={(value, rowIndex, colIndex) => {
|
||||
if (colIndex === 4) {
|
||||
const user = users[rowIndex];
|
||||
// Calculate the actual index in the full users array based on pagination
|
||||
const actualIndex = (currentPage - 1) * pageSize + rowIndex;
|
||||
const user = users[actualIndex];
|
||||
if (!user) return null;
|
||||
|
||||
const isActive = user.status === 'Active';
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
@ -638,7 +670,7 @@ const AdminUsers = () => {
|
||||
type="button"
|
||||
className="h-8 w-8 grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
||||
title="Edit"
|
||||
onClick={() => handleEdit(user)}
|
||||
onClick={() => handleEdit(user.raw || user)}
|
||||
>
|
||||
<img
|
||||
src={editingRow?.id === user.id ? pencilActiveSrc : pencilInactiveSrc}
|
||||
@ -707,12 +739,18 @@ const AdminUsers = () => {
|
||||
)}
|
||||
|
||||
{/* Edit Modal */}
|
||||
{isEditModalOpen && (
|
||||
{isEditModalOpen && currentUser && (
|
||||
<EditUserModal
|
||||
formData={formData}
|
||||
setFormData={setFormData}
|
||||
onClose={() => setIsEditModalOpen(false)}
|
||||
onUpdate={handleUpdateUser}
|
||||
onClose={() => {
|
||||
setIsEditModalOpen(false);
|
||||
setCurrentUser(null);
|
||||
setFormData({ name: '', email: '', status: 'Active' });
|
||||
setErrors({});
|
||||
}}
|
||||
onUpdate={handleEditSubmit}
|
||||
key={currentUser.id} // Add key to force re-render when user changes
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@ -535,13 +535,16 @@ const IsicHsCodes = () => {
|
||||
});
|
||||
|
||||
const handleView = async (index) => {
|
||||
const selected = rowsData[index];
|
||||
// Calculate the actual index in the filtered data based on pagination
|
||||
const actualIndex = (currentPage - 1) * pageSize + index;
|
||||
const selected = filteredData[actualIndex];
|
||||
if (!selected) return;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const productId = selected.id;
|
||||
const response = await productService.getProductById(productId);
|
||||
// Get fresh data from the server using the ID
|
||||
const response = await productService.getProductById(selected.id);
|
||||
|
||||
if (response && response.data) {
|
||||
const product = response.data;
|
||||
setForm({
|
||||
@ -551,8 +554,8 @@ const IsicHsCodes = () => {
|
||||
status: product.is_active ? 'Active' : 'Inactive',
|
||||
description: product.hs_description || '',
|
||||
createdBy: selected.createdBy,
|
||||
createdOn: selected.createdOn,
|
||||
updated: selected.updated
|
||||
createdOn: selected.createdOn || new Date(product.created_at).toLocaleDateString('en-GB'),
|
||||
updated: selected.updated || (product.updated_at ? new Date(product.updated_at).toLocaleDateString('en-GB') : '')
|
||||
});
|
||||
setModalMode('view');
|
||||
} else {
|
||||
@ -568,7 +571,9 @@ const IsicHsCodes = () => {
|
||||
};
|
||||
|
||||
const handleEdit = async (index) => {
|
||||
const selected = rowsData[index];
|
||||
// Calculate the actual index in the filtered data based on pagination
|
||||
const actualIndex = (currentPage - 1) * pageSize + index;
|
||||
const selected = filteredData[actualIndex];
|
||||
if (!selected) return;
|
||||
|
||||
try {
|
||||
@ -620,39 +625,38 @@ const IsicHsCodes = () => {
|
||||
const [deletingRowIndex, setDeletingRowIndex] = React.useState(null);
|
||||
|
||||
const handleDelete = async (paginationIndex) => {
|
||||
const dataIndex = (currentPage - 1) * pageSize + paginationIndex;
|
||||
if (dataIndex < 0 || dataIndex >= rowsData.length) {
|
||||
console.error('Invalid row index for deletion');
|
||||
const dataIndex = (currentPage - 1) * pageSize + paginationIndex;
|
||||
const selected = rowsData[dataIndex];
|
||||
|
||||
if (!selected || !selected.id) {
|
||||
console.error('No product selected or missing ID');
|
||||
showToast('Cannot delete: Product information is incomplete', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const productId = rowsData[dataIndex]?.id;
|
||||
if (!productId) {
|
||||
console.error('No product ID found for deletion');
|
||||
return;
|
||||
}
|
||||
|
||||
setDeletingRowIndex(dataIndex);
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await productService.getProductById(productId);
|
||||
setIsLoading(true);
|
||||
|
||||
// Fetch the latest product data before showing delete confirmation
|
||||
const response = await productService.getProductById(selected.id);
|
||||
|
||||
if (!response || !response.data) {
|
||||
throw new Error('Invalid product data received');
|
||||
}
|
||||
|
||||
// Update the local data with the latest information
|
||||
const updatedRowsData = [...rowsData];
|
||||
updatedRowsData[dataIndex] = {
|
||||
...updatedRowsData[dataIndex],
|
||||
...selected,
|
||||
...response.data
|
||||
};
|
||||
|
||||
setRowsData(updatedRowsData);
|
||||
setDeletingRowIndex(dataIndex);
|
||||
setShowDeleteConfirm(true);
|
||||
} catch (error) {
|
||||
console.error('Error preparing product for deletion:', error);
|
||||
showToast('Error loading product details for deletion', 'error');
|
||||
setDeletingRowIndex(null);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@ -670,7 +674,6 @@ const IsicHsCodes = () => {
|
||||
};
|
||||
|
||||
const handleDeleteConfirm = async () => {
|
||||
|
||||
if (deletingRowIndex === null || deletingRowIndex < 0 || deletingRowIndex >= rowsData.length) {
|
||||
const errorMsg = 'Invalid row selected for deletion';
|
||||
console.error(errorMsg, { deletingRowIndex, rowsDataLength: rowsData.length });
|
||||
@ -693,6 +696,16 @@ const IsicHsCodes = () => {
|
||||
try {
|
||||
await productService.deleteProduct(productId);
|
||||
|
||||
// Calculate the current page after deletion
|
||||
const currentItemsCount = rowsData.length - 1; // After deletion
|
||||
const maxPage = Math.max(1, Math.ceil(currentItemsCount / pageSize));
|
||||
|
||||
// If we're on a page that no longer exists after deletion, go to the previous page
|
||||
// But only if we're not already on the first page
|
||||
if (currentPage > maxPage && maxPage > 0) {
|
||||
setCurrentPage(maxPage);
|
||||
}
|
||||
|
||||
const newData = [...rowsData];
|
||||
newData.splice(deletingRowIndex, 1);
|
||||
setRowsData(newData);
|
||||
@ -701,6 +714,16 @@ const IsicHsCodes = () => {
|
||||
} catch (apiError) {
|
||||
console.error('API Error:', apiError);
|
||||
if (apiError.response && apiError.response.status === 204) {
|
||||
// Calculate the current page after deletion for 204 response
|
||||
const currentItemsCount = rowsData.length - 1; // After deletion
|
||||
const maxPage = Math.max(1, Math.ceil(currentItemsCount / pageSize));
|
||||
|
||||
// If we're on a page that no longer exists after deletion, go to the previous page
|
||||
// But only if we're not already on the first page
|
||||
if (currentPage > maxPage && maxPage > 0) {
|
||||
setCurrentPage(maxPage);
|
||||
}
|
||||
|
||||
const newData = [...rowsData];
|
||||
newData.splice(deletingRowIndex, 1);
|
||||
setRowsData(newData);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect, useMemo, useRef } from 'react';
|
||||
import Table from '@/components/common/Table';
|
||||
import { TextField } from '@/components/common/FormControls';
|
||||
import { getUnits, createUnit, updateUnit, deleteUnit, uploadUnitCSV } from '@/services/configuration/unitService';
|
||||
import { getUnits, getUnitById, createUnit, updateUnit, deleteUnit, uploadUnitCSV } from '@/services/configuration/unitService';
|
||||
|
||||
const downloadIconSrc = '/assets/images/DownloadSimple.svg';
|
||||
const addIconSrc = '/assets/images/ic_baseline-plus.svg';
|
||||
@ -193,27 +193,108 @@ const UnitMaster = () => {
|
||||
setForm(createEmptyUnitForm());
|
||||
setModalMode('add');
|
||||
};
|
||||
const handleView = (index) => {
|
||||
const selected = units[index];
|
||||
if (!selected) return;
|
||||
setSelectedUnit(selected);
|
||||
setModalMode('view');
|
||||
const handleView = async (index) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// Get the actual index in the full units array based on pagination
|
||||
const actualIndex = (currentPage - 1) * pageSize + index;
|
||||
const selected = filteredUnits[actualIndex];
|
||||
|
||||
if (!selected) return;
|
||||
|
||||
// Fetch the latest unit data by ID using the getUnitById function
|
||||
const response = await getUnitById(selected.id);
|
||||
|
||||
if (!response) {
|
||||
throw new Error('Failed to fetch unit data');
|
||||
}
|
||||
|
||||
// The API returns data in response.data
|
||||
const unitData = response.data || response;
|
||||
|
||||
setSelectedUnit(unitData);
|
||||
setModalMode('view');
|
||||
} catch (error) {
|
||||
console.error('Error fetching unit data:', error);
|
||||
setToastData({
|
||||
message: 'Failed to load unit data. Please try again.',
|
||||
type: 'error'
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEdit = (index) => {
|
||||
const selected = filteredUnits[index];
|
||||
if (!selected) return;
|
||||
setEditingRow(index);
|
||||
setSelectedUnit(selected);
|
||||
setForm({
|
||||
uom: selected.uom || '',
|
||||
description: selected.description || '',
|
||||
});
|
||||
setModalMode('edit');
|
||||
const handleEdit = async (index) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// Get the actual index in the full units array based on pagination
|
||||
const actualIndex = (currentPage - 1) * pageSize + index;
|
||||
const selected = filteredUnits[actualIndex];
|
||||
|
||||
if (!selected) return;
|
||||
|
||||
// Fetch the latest unit data by ID using the getUnitById function
|
||||
const response = await getUnitById(selected.id);
|
||||
|
||||
if (!response) {
|
||||
throw new Error('Failed to fetch unit data');
|
||||
}
|
||||
|
||||
// The API returns data in response.data
|
||||
const unitData = response.data || response;
|
||||
|
||||
setEditingRow(selected.id);
|
||||
setSelectedUnit(unitData);
|
||||
setForm({
|
||||
uom: unitData.uom || '',
|
||||
description: unitData.description || '',
|
||||
});
|
||||
setModalMode('edit');
|
||||
} catch (error) {
|
||||
console.error('Error fetching unit data:', error);
|
||||
setToastData({
|
||||
message: 'Failed to load unit data. Please try again.',
|
||||
type: 'error'
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = (index) => {
|
||||
setDeletingRow(index);
|
||||
const handleDelete = async (index) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// Get the actual index in the full units array based on pagination
|
||||
const actualIndex = (currentPage - 1) * pageSize + index;
|
||||
const selected = filteredUnits[actualIndex];
|
||||
|
||||
if (!selected) return;
|
||||
|
||||
// Fetch the latest unit data by ID using the getUnitById function
|
||||
const response = await getUnitById(selected.id);
|
||||
|
||||
if (!response) {
|
||||
throw new Error('Failed to fetch unit data');
|
||||
}
|
||||
|
||||
// The API returns data in response.data
|
||||
const unitData = response.data || response;
|
||||
|
||||
// Set the actual index in the filtered array for deletion
|
||||
setDeletingRow(actualIndex);
|
||||
} catch (error) {
|
||||
console.error('Error fetching unit data:', error);
|
||||
setToastData({
|
||||
message: 'Failed to load unit data. Please try again.',
|
||||
type: 'error'
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
@ -276,8 +357,7 @@ const UnitMaster = () => {
|
||||
setSaveLoading(true);
|
||||
|
||||
if (modalMode === 'edit' && editingRow !== null) {
|
||||
const unitToUpdate = filteredUnits[editingRow];
|
||||
await updateUnit(unitToUpdate.id, unitData);
|
||||
await updateUnit(editingRow, unitData);
|
||||
|
||||
setToastData({
|
||||
message: 'Unit updated successfully!',
|
||||
|
||||
@ -10,6 +10,16 @@ export const getQuarterlyWindows = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getQuarterlyWindowById = async (id) => {
|
||||
try {
|
||||
const response = await getRequest(`/quarterly_windows/${id}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching quarterly window with ID ${id}:`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const createQuarterlyWindow = async (data) => {
|
||||
try {
|
||||
const response = await postRequest('/quarterly_windows', data);
|
||||
|
||||
@ -13,6 +13,17 @@ export const getUnits = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getUnitById = async (id) => {
|
||||
try {
|
||||
const response = await getRequest(`${UNIT_MASTER_ENDPOINT}/${id}`);
|
||||
// Handle nested data structure
|
||||
return response.data || response;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching unit with ID ${id}:`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const createUnit = async (unitData) => {
|
||||
try {
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user