bug fixed
This commit is contained in:
parent
24fcea6189
commit
2edb38db14
@ -132,8 +132,9 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => {
|
||||
if (type === 'approve') {
|
||||
setShowApproveModal(true);
|
||||
} else {
|
||||
// Clear previous state
|
||||
setRejectionReason('');
|
||||
setShowRejectError(false);
|
||||
setShowRejectError(false); // Don't show error immediately
|
||||
setShowRejectModal(true);
|
||||
}
|
||||
};
|
||||
@ -320,22 +321,42 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => {
|
||||
<div className="relative group">
|
||||
<CheckCircle2
|
||||
size={22}
|
||||
className={`${item.status?.toLowerCase() === 'approved' ? 'text-gray-400 cursor-not-allowed' : 'text-green-600 cursor-pointer hover:scale-110'} transition-transform`}
|
||||
onClick={item.status?.toLowerCase() !== 'approved' ? () => handleActionClick(item, 'approve') : undefined}
|
||||
className={`${
|
||||
item.status?.toLowerCase() === 'approved' || item.status?.toLowerCase() === 'rejected'
|
||||
? 'text-gray-400 cursor-not-allowed'
|
||||
: 'text-green-600 cursor-pointer hover:scale-110'
|
||||
} transition-transform`}
|
||||
onClick={['submitted', 'resubmitted'].includes(item.status?.toLowerCase())
|
||||
? () => handleActionClick(item, 'approve')
|
||||
: undefined}
|
||||
/>
|
||||
<span className="absolute bottom-full mb-1 left-1/2 -translate-x-1/2 bg-black text-white text-xs px-2 py-[2px] rounded opacity-0 group-hover:opacity-100 transition">
|
||||
{item.status?.toLowerCase() === 'approved' ? 'Already Approved' : 'Approve'}
|
||||
{item.status?.toLowerCase() === 'approved'
|
||||
? 'Already Approved'
|
||||
: item.status?.toLowerCase() === 'rejected'
|
||||
? 'Cannot approve rejected'
|
||||
: 'Approve'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="relative group">
|
||||
<XCircle
|
||||
size={22}
|
||||
className={`${item.status?.toLowerCase() === 'rejected' ? 'text-gray-400 cursor-not-allowed' : 'text-red-600 cursor-pointer hover:scale-110'} transition-transform`}
|
||||
onClick={item.status?.toLowerCase() !== 'rejected' ? () => handleActionClick(item, 'reject') : undefined}
|
||||
className={`${
|
||||
['approved', 'rejected'].includes(item.status?.toLowerCase())
|
||||
? 'text-gray-400 cursor-not-allowed'
|
||||
: 'text-red-600 cursor-pointer hover:scale-110'
|
||||
} transition-transform`}
|
||||
onClick={!['approved', 'rejected'].includes(item.status?.toLowerCase())
|
||||
? () => handleActionClick(item, 'reject')
|
||||
: undefined}
|
||||
/>
|
||||
<span className="absolute bottom-full mb-1 left-1/2 -translate-x-1/2 bg-black text-white text-xs px-2 py-[2px] rounded opacity-0 group-hover:opacity-100 transition">
|
||||
{item.status?.toLowerCase() === 'rejected' ? 'Already Rejected' : 'Reject'}
|
||||
<span className="absolute bottom-full mb-1 left-1/2 -translate-x-1/2 bg-black text-white text-xs px-2 py-[2px] rounded opacity-0 group-hover:opacity-100 transition whitespace-nowrap">
|
||||
{item.status?.toLowerCase() === 'rejected'
|
||||
? 'Already Rejected'
|
||||
: item.status?.toLowerCase() === 'approved'
|
||||
? 'Cannot reject approved'
|
||||
: 'Reject'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -486,9 +507,15 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => {
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRejectConfirm}
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-[#92722A] border border-transparent rounded-md shadow-sm hover:bg-[#7c5e24] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#92722A] flex items-center gap-2"
|
||||
disabled={isProcessing || !rejectionReason.trim()}
|
||||
onClick={() => {
|
||||
if (!rejectionReason.trim()) {
|
||||
setShowRejectError(true);
|
||||
return;
|
||||
}
|
||||
handleRejectConfirm();
|
||||
}}
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-[#92722A] border border-transparent rounded-md shadow-sm hover:bg-[#7c5e24] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#92722A] flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
disabled={isProcessing}
|
||||
>
|
||||
{isProcessing ? (
|
||||
<>
|
||||
|
||||
@ -579,7 +579,7 @@ const ValidationReview = () => {
|
||||
</label>
|
||||
<textarea
|
||||
rows="2"
|
||||
className="w-[1100px] rounded-[10px] border border-[#D1D5DB] bg-[#FFFCF2] px-4 py-2 mb-4 text-sm text-[#232528] focus:ring-2 focus:ring-[#92722A] focus:outline-none"
|
||||
className="w-[580px] rounded-[10px] border border-[#D1D5DB] bg-[#FFFCF2] px-4 py-2 mb-4 text-sm text-[#232528] focus:ring-2 focus:ring-[#92722A] focus:outline-none"
|
||||
placeholder="Please enter remarks here"
|
||||
></textarea>
|
||||
</div>
|
||||
@ -727,7 +727,7 @@ const ValidationReview = () => {
|
||||
<div className="flex justify-end gap-3 mt-6">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md border border-gray-300 bg-[#92722A] px-4 py-2 text-sm font-medium text-[#92722A] hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
className="rounded-md border border-gray-300 bg-[#fffff] px-4 py-2 text-sm font-medium text-[#92722A] hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-[#92722A] focus:ring-offset-2"
|
||||
onClick={() => setIsApproveOpen(false)}
|
||||
disabled={actionLoading}
|
||||
>
|
||||
|
||||
@ -523,13 +523,13 @@ React.useEffect(() => {
|
||||
data.forEach((item) => {
|
||||
const normalized = String(item.status || '').toLowerCase();
|
||||
let status = '—';
|
||||
if (normalized === 'pending') status = 'Under Review';
|
||||
else if (normalized === 'rejected') status = 'Returned';
|
||||
else if (['submitted', 'approved'].includes(normalized)) status = 'Approved';
|
||||
if (normalized === 'submitted') status = 'Submitted';
|
||||
else if (normalized === 'rejected') status = 'Rejected';
|
||||
else if (['approved'].includes(normalized)) status = 'Approved';
|
||||
else status = item.status || '—';
|
||||
|
||||
const productCount = Number(item.products) || 0;
|
||||
const products = `${productCount} ${productCount === 1 ? 'product' : 'products'}`;
|
||||
const products = `${productCount} `;
|
||||
|
||||
const row = [
|
||||
item.establishment || '—',
|
||||
@ -680,22 +680,42 @@ React.useEffect(() => {
|
||||
<div className="relative group">
|
||||
<CheckCircle2
|
||||
size={22}
|
||||
className={`${item.status?.toLowerCase() === 'approved' ? 'text-gray-400 cursor-not-allowed' : 'text-green-600 cursor-pointer hover:scale-110'} transition-transform`}
|
||||
onClick={item.status?.toLowerCase() !== 'approved' ? () => handleApproveClick(item) : undefined}
|
||||
className={`${
|
||||
item.status?.toLowerCase() === 'approved' || item.status?.toLowerCase() === 'rejected'
|
||||
? 'text-gray-400 cursor-not-allowed'
|
||||
: 'text-green-600 cursor-pointer hover:scale-110'
|
||||
} transition-transform`}
|
||||
onClick={['submitted', 'resubmitted'].includes(item.status?.toLowerCase())
|
||||
? () => handleApproveClick(item)
|
||||
: undefined}
|
||||
/>
|
||||
<span className="absolute bottom-full mb-1 left-1/2 -translate-x-1/2 bg-black text-white text-xs px-2 py-[2px] rounded opacity-0 group-hover:opacity-100 transition">
|
||||
{item.status?.toLowerCase() === 'approved' ? 'Already Approved' : 'Approve'}
|
||||
<span className="absolute bottom-full mb-1 left-1/2 -translate-x-1/2 bg-black text-white text-xs px-2 py-[2px] rounded opacity-0 group-hover:opacity-100 transition whitespace-nowrap">
|
||||
{item.status?.toLowerCase() === 'approved'
|
||||
? 'Already Approved'
|
||||
: item.status?.toLowerCase() === 'rejected'
|
||||
? 'Cannot approve rejected'
|
||||
: 'Approve'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="relative group">
|
||||
<XCircle
|
||||
size={22}
|
||||
className={`${item.status?.toLowerCase() === 'rejected' ? 'text-gray-400 cursor-not-allowed' : 'text-red-600 cursor-pointer hover:scale-110'} transition-transform`}
|
||||
onClick={item.status?.toLowerCase() !== 'rejected' ? () => handleRejectClick(item) : undefined}
|
||||
className={`${
|
||||
['approved', 'rejected'].includes(item.status?.toLowerCase())
|
||||
? 'text-gray-400 cursor-not-allowed'
|
||||
: 'text-red-600 cursor-pointer hover:scale-110'
|
||||
} transition-transform`}
|
||||
onClick={!['approved', 'rejected'].includes(item.status?.toLowerCase())
|
||||
? () => handleRejectClick(item)
|
||||
: undefined}
|
||||
/>
|
||||
<span className="absolute bottom-full mb-1 left-1/2 -translate-x-1/2 bg-black text-white text-xs px-2 py-[2px] rounded opacity-0 group-hover:opacity-100 transition">
|
||||
{item.status?.toLowerCase() === 'rejected' ? 'Already Rejected' : 'Reject'}
|
||||
<span className="absolute bottom-full mb-1 left-1/2 -translate-x-1/2 bg-black text-white text-xs px-2 py-[2px] rounded opacity-0 group-hover:opacity-100 transition whitespace-nowrap">
|
||||
{item.status?.toLowerCase() === 'rejected'
|
||||
? 'Already Rejected'
|
||||
: item.status?.toLowerCase() === 'approved'
|
||||
? 'Cannot reject approved'
|
||||
: 'Reject'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1943,8 +1943,25 @@ const loadProfiles = async () => {
|
||||
|
||||
// Website URL validation
|
||||
if ((field === 'contactWebsite' || field === 'corporateWebsite') && value) {
|
||||
if (!/^https?:\/\/.+\..+/.test(value)) {
|
||||
return 'Please enter a valid URL (e.g., https://example.com)';
|
||||
// Supports:
|
||||
// - http://example.com
|
||||
// - https://example.com
|
||||
// - www.example.com
|
||||
// - example.com
|
||||
// - subdomain.example.com
|
||||
// - localhost:3000 (for development)
|
||||
// - IP addresses (e.g., 192.168.1.1)
|
||||
// - With or without path/query parameters
|
||||
const urlRegex = /^(https?:\/\/)?(www\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?(\/\S*)?$/;
|
||||
|
||||
// Also allow localhost with optional port for development
|
||||
// const localhostRegex = /^https?:\/\/localhost(:\d+)?(\/\S*)?$/;
|
||||
|
||||
// Also allow IP addresses with optional port
|
||||
const ipRegex = /^https?:\/\/(\d{1,3}\.){3}\d{1,3}(:\d+)?(\/\S*)?$/;
|
||||
|
||||
if (!urlRegex.test(value)&& !ipRegex.test(value)) {
|
||||
return 'Please enter a valid URL (e.g., https://example.com, example.com, or www.example.com)';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -485,7 +485,7 @@ const IsicHsCodes = () => {
|
||||
'HS Code',
|
||||
'Product Name',
|
||||
'Unit',
|
||||
'Estimated Mapped',
|
||||
'Establishment Mapped',
|
||||
'Created By',
|
||||
'Last Updated',
|
||||
'Action',
|
||||
|
||||
@ -38,13 +38,13 @@ const CustomToast = ({ message, type, onClose }) => {
|
||||
const getIcon = () => {
|
||||
switch (type) {
|
||||
case 'success':
|
||||
return '✅';
|
||||
return '';
|
||||
case 'error':
|
||||
return '❌';
|
||||
return '';
|
||||
case 'warning':
|
||||
return '⚠️';
|
||||
return '';
|
||||
default:
|
||||
return 'ℹ️';
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
@ -399,11 +399,21 @@ const QuarterlyWindows = () => {
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
console.error('Failed to add quarterly window');
|
||||
// Show error message from API response if available
|
||||
const errorMessage = response.message || 'Failed to add quarterly window';
|
||||
setToastData({
|
||||
message: errorMessage,
|
||||
type: 'error'
|
||||
});
|
||||
console.error('Failed to add quarterly window:', errorMessage);
|
||||
}
|
||||
} else if (modalMode === 'edit' && editingRow !== null) {
|
||||
const itemId = quarterData[editingRow]?.id;
|
||||
if (!itemId) {
|
||||
setToastData({
|
||||
message: 'Error: Could not find the item to update',
|
||||
type: 'error'
|
||||
});
|
||||
console.error('Error: Could not find the item to update');
|
||||
return;
|
||||
}
|
||||
@ -430,11 +440,22 @@ const QuarterlyWindows = () => {
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
console.error('Failed to update quarterly window');
|
||||
// Show error message from API response if available
|
||||
const errorMessage = response.message || 'Failed to update quarterly window';
|
||||
setToastData({
|
||||
message: errorMessage,
|
||||
type: 'error'
|
||||
});
|
||||
console.error('Failed to update quarterly window:', errorMessage);
|
||||
}
|
||||
}
|
||||
handleCloseModal();
|
||||
} catch (error) {
|
||||
const errorMessage = error.response?.data?.message || error.message || 'An error occurred while saving';
|
||||
setToastData({
|
||||
message: errorMessage,
|
||||
type: 'error'
|
||||
});
|
||||
console.error('Error saving quarterly window:', error);
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user