This commit is contained in:
Senthamilselvi 2025-11-05 17:11:24 +05:30
commit 2eb18764f3
2 changed files with 162 additions and 55 deletions

View File

@ -106,7 +106,7 @@ const createEmptyProfile = () => ({
contactEmail: '',
contactWebsite: '',
// Corporate / head office contact details
corporateSameAs: false,
corporateSameAs: true,
corporateName: '',
corporateAddress: '',
corporateCityTown: '',
@ -812,18 +812,28 @@ const CompanyProfile = () => {
setActiveStep((prev) => Math.min(prev + 1, formSteps.length - 1));
}, [activeStep, form.userProfilePassword, form.userProfileConfirmPassword, formSteps.length, validateStepFields]);
const handleToggleStatus = (item) => {
const updatedStatus = item.status === 'Active' ? 'Inactive' : 'Active';
// Optional: call your API to update backend
// await updateEstablishmentStatus(item.id, updatedStatus);
setEstablishments((prev) =>
prev.map((est) =>
est.id === item.id ? { ...est, status: updatedStatus } : est
const handleToggleStatus = async (establishmentId, currentStatus) => {
try {
const newStatus = !currentStatus;
await updateEstablishment(establishmentId, { is_active: newStatus });
// Update the local state to reflect the change
setProfiles(prevProfiles =>
prevProfiles.map(profile =>
profile.apiId === establishmentId
? { ...profile, isActive: newStatus, status: newStatus ? 'Active' : 'Inactive' }
: profile
)
);
};
showToast('success', `Establishment ${newStatus ? 'activated' : 'deactivated'} successfully`);
} catch (error) {
console.error('Error updating status:', error);
const message = error?.response?.data?.message || 'Failed to update status. Please try again.';
showToast('error', message);
}
};
const filteredProfiles = React.useMemo(() => {
if (!debouncedSearch.trim()) return profiles;
@ -848,7 +858,7 @@ const CompanyProfile = () => {
const renderStatusBadge = (status) => {
const statusMap = {
active: { text: 'Active', bgColor: 'bg-[#F3FAF4]', textColor: 'text-[#2F663C]' },
inactive: { text: 'Inactive', bgColor: 'bg-[#FEF2F2]', textColor: 'text-[#B91C1C]' },
inactive: { text: 'Inactive', bgColor: 'bg-[#E9EDF3]', textColor: 'text-[#232528]' },
pending: { text: 'Pending', bgColor: 'bg-[#FFFBEB]', textColor: 'text-[#B45309]' },
// Add more status mappings as needed
};
@ -901,19 +911,21 @@ const CompanyProfile = () => {
/>
</button>
<button
type="button"
className="h-[20px] w-[40px] grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
title={item.status === 'Active' ? 'Deactivate' : 'Activate'}
aria-label={item.status === 'Active' ? 'Deactivate establishment' : 'Activate establishment'}
onClick={() => handleToggleStatus(item)}
>
<img
src={item.status === 'Active' ? activeToggleSrc : inactiveToggleSrc}
alt={item.status === 'Active' ? 'Deactivate' : 'Activate'}
className="h-[20px] w-[40px] object-contain"
/>
type="button"
className="h-[20px] w-[40px] grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
title={item.status === 'Active' ? 'Deactivate' : 'Activate'}
onClick={(e) => {
e.stopPropagation();
handleToggleStatus(item.apiId, item.status === 'Active');
}}
>
<img
src={item.status === 'Active' ? activeToggleSrc : inactiveToggleSrc}
alt={item.status === 'Active' ? 'Active' : 'Inactive'}
className="h-[20px] w-[40px] object-contain"
/>
</button>
<button
{/* <button
type="button"
className="h-6 w-6 grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
title={item.userProfileEmail ? 'Reset password' : 'Reset password'}
@ -926,7 +938,7 @@ const CompanyProfile = () => {
alt="Reset password"
className="h-4 w-4"
/>
</button>
</button> */}
@ -1890,7 +1902,11 @@ const loadProfiles = async () => {
{isLoading ? (
<div className="px-6 pb-12">
<Loader size="lg" label="Loading company profiles..." />
<Loader size="md" label="Loading company profiles..." />
</div>
) : profiles.length === 0 ? (
<div className="px-4 py-10 text-center">
<h3 className="text-lg font-medium text-gray-900">No results found</h3>
</div>
) : (
<Table

View File

@ -2,7 +2,8 @@ import React from 'react';
import Table from '@/components/common/Table';
import { TextField, SelectField, DateField } from '@/components/common/FormControls';
import StatusBadge from '@/components/common/StatusBadge';
import { createQuarterlyWindow, updateQuarterlyWindow, getQuarterlyWindows } from '@/services/configuration/quarterlyWindows';
import { createQuarterlyWindow, updateQuarterlyWindow, getQuarterlyWindows, deleteQuarterlyWindow } from '@/services/configuration/quarterlyWindows';
import CustomToast from '@/components/common/CustomToast';
const downloadIconSrc = '/assets/images/DownloadSimple.svg';
const addIconSrc = '/assets/images/ic_baseline-plus.svg';
@ -16,14 +17,15 @@ const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
const caretDownSrc = '/assets/images/CaretDown-black.svg';
const createEmptyQuarterForm = () => ({
survey_name: '',
establishment: '-', // Default to hyphen
year: '',
quarter: '',
startDate: '',
endDate: '',
gracePeriod: '',
gracePeriod: '0 days',
submissionCount: '0',
status: '',
status: 'Active', // Default to Active
});
const QuarterlyWindows = () => {
@ -41,6 +43,21 @@ const QuarterlyWindows = () => {
const [currentPage, setCurrentPage] = React.useState(1);
const pageSize = 10;
const [isLoading, setIsLoading] = React.useState(false);
const [toast, setToast] = React.useState({ show: false, message: '', type: 'success' });
// Format date to yyyy-MM-dd for input fields
const formatDateForInput = (dateString) => {
if (!dateString) return '';
const date = new Date(dateString);
return date.toISOString().split('T')[0];
};
// Format date for display
const formatDateForDisplay = (dateString) => {
if (!dateString) return '';
const date = new Date(dateString);
return date.toLocaleDateString('en-GB');
};
// Fetch quarterly windows data on component mount
React.useEffect(() => {
@ -52,13 +69,14 @@ const QuarterlyWindows = () => {
// Transform the API response to match the expected format
const formattedData = response.data.map(item => ({
id: item.id,
establishment: item.establishment || '-', // Default to hyphen if empty
survey_name: item.survey_name || '',
establishment: item.establishment || '-',
year: item.year?.toString() || '',
quarter: item.quarter || '',
startDate: item.start_date ? new Date(item.start_date).toLocaleDateString('en-GB') : '',
endDate: item.end_date ? new Date(item.end_date).toLocaleDateString('en-GB') : '',
startDate: item.start_date || '',
endDate: item.end_date || '',
gracePeriod: item.grace_periods_days ? `${item.grace_periods_days} days` : '0 days',
submissionCount: '0', // Default value since it's not in the API response
submissionCount: '0',
status: item.is_active ? 'Active' : 'Inactive'
}));
setQuarterData(formattedData);
@ -95,6 +113,7 @@ const QuarterlyWindows = () => {
if (!matchesStatus) return false;
if (!term) return true;
const haystack = [
item.survey_name,
item.establishment,
item.year,
item.quarter,
@ -111,11 +130,11 @@ const QuarterlyWindows = () => {
}, [quarterData, searchTerm, statusFilter]);
const rows = filteredRows.map((item) => [
item.establishment,
item.survey_name || '-',
item.year,
item.quarter,
item.startDate,
item.endDate,
item.startDate ? formatDateForDisplay(item.startDate) : '-',
item.endDate ? formatDateForDisplay(item.endDate) : '-',
item.gracePeriod,
item.submissionCount,
<StatusBadge tone={String(item.status).toLowerCase() === 'active' ? 'green' : 'gray'} status={item.status} />,
@ -145,9 +164,15 @@ const QuarterlyWindows = () => {
const openEditModal = (index) => {
const selected = quarterData[index];
// Ensure dates are in the correct format for the form inputs
const formData = {
...selected,
startDate: selected.startDate ? formatDateForInput(selected.startDate) : '',
endDate: selected.endDate ? formatDateForInput(selected.endDate) : ''
};
setEditingRow(index);
setForm({ ...selected });
setOriginalForm({ ...selected });
setForm(formData);
setOriginalForm(formData);
setModalMode('edit');
};
@ -171,6 +196,8 @@ const QuarterlyWindows = () => {
}
};
const openDeleteConfirm = (index) => {
setDeletingRow(index);
setShowDeleteConfirm(true);
@ -181,22 +208,41 @@ const QuarterlyWindows = () => {
setDeletingRow(null);
};
const showToast = (message, type = 'success') => {
setToast({ show: true, message, type });
setTimeout(() => setToast({ ...toast, show: false }), 4000);
};
const handleSave = async () => {
try {
const formatDate = (dateString) => {
if (!form.survey_name || !form.year || !form.quarter || !form.startDate || !form.endDate) {
showToast('Please fill in all required fields', 'error');
return;
}
const formatDateForAPI = (dateString) => {
if (!dateString) return '';
const [day, month, year] = dateString.split('/');
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
// If already in YYYY-MM-DD format
if (dateString.match(/^\d{4}-\d{2}-\d{2}$/)) {
return dateString;
}
// Handle DD/MM/YYYY format
if (dateString.includes('/')) {
const [day, month, year] = dateString.split('/');
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
}
// For any other format, let the Date object handle it
return new Date(dateString).toISOString().split('T')[0];
};
const formattedData = {
year: parseInt(form.year, 10) || 0,
quarter: form.quarter || 'Q1',
start_date: formatDate(form.startDate) || new Date().toISOString().split('T')[0],
end_date: formatDate(form.endDate) || new Date().toISOString().split('T')[0],
grace_periods_days: parseInt(form.gracePeriod?.split(' ')[0], 10) || 0,
survey_name: form.survey_name || '',
year: form.year ? parseInt(form.year, 10) : 0,
quarter: form.quarter || '',
start_date: form.startDate ? formatDateForAPI(form.startDate) : '',
end_date: form.endDate ? formatDateForAPI(form.endDate) : '',
grace_periods_days: form.gracePeriod ? parseInt(form.gracePeriod.split(' ')[0], 10) || 0 : 0,
is_active: form.status === 'Active',
establishment: '-', // Default to hyphen if empty
establishment: form.establishment || '-',
};
if (modalMode === 'add') {
@ -212,9 +258,17 @@ const QuarterlyWindows = () => {
status: formattedData.is_active ? 'Active' : 'Inactive'
};
setQuarterData((prev) => [...prev, newItem]);
showToast('Quarterly window added successfully!');
} else {
showToast('Failed to add quarterly window', 'error');
}
} else if (modalMode === 'edit' && editingRow !== null) {
const response = await updateQuarterlyWindow(quarterData[editingRow].id, formattedData);
const itemId = quarterData[editingRow]?.id;
if (!itemId) {
showToast('Error: Could not find the item to update', 'error');
return;
}
const response = await updateQuarterlyWindow(itemId, formattedData);
if (response.status === 'success') {
setQuarterData((prev) =>
prev.map((item, idx) =>
@ -230,24 +284,60 @@ const QuarterlyWindows = () => {
: item
)
);
showToast('Quarterly window updated successfully!');
} else {
showToast('Failed to update quarterly window', 'error');
}
}
handleCloseModal();
} catch (error) {
console.error('Error saving quarterly window:', error);
// You might want to add error handling UI here
showToast('An error occurred while saving. Please try again.', 'error');
}
};
const handleDelete = () => {
if (deletingRow !== null) {
setQuarterData((prev) => prev.filter((_, index) => index !== deletingRow));
const handleDelete = async () => {
if (deletingRow === null) return;
try {
// First, get the item to ensure we have the latest data
const itemToDelete = quarterData[deletingRow];
if (!itemToDelete?.id) {
showToast('Error: Could not find the item to delete', 'error');
closeDeleteConfirm();
return;
}
// Call the delete API with the ID
const response = await deleteQuarterlyWindow(itemToDelete.id);
if (response.status === 'success') {
// Update the UI by removing the deleted item
setQuarterData(prev => prev.filter(item => item.id !== itemToDelete.id));
showToast('Quarterly window deleted successfully!');
} else {
showToast(response.message || 'Failed to delete quarterly window', 'error');
}
} catch (error) {
console.error('Error deleting quarterly window:', error);
showToast('An error occurred while deleting. Please try again.', 'error');
} finally {
closeDeleteConfirm();
}
};
return (
<div className="w-full max-w-[1280px] rounded-lg border border-[#C3C6CB] bg-white min-h-[348px] pb-6">
<>
{toast.show && (
<div className="fixed top-4 right-4 z-50">
<CustomToast
message={toast.message}
type={toast.type}
onClose={() => setToast({ ...toast, show: false })}
/>
</div>
)}
<div className="w-full max-w-[1280px] rounded-lg border border-[#C3C6CB] bg-white min-h-[348px] pb-6">
<div className="px-6 py-4">
<div className="flex flex-col gap-3 md:h-12 md:flex-row md:items-center md:justify-between">
<h3 className="text-[18px] leading-[28px] font-medium text-[#232528]">Quarterly Windows</h3>
@ -421,8 +511,8 @@ const QuarterlyWindows = () => {
<div className="w-full">
<TextField
label="Survey Name"
value={form.establishment}
onChange={(e) => setForm({ ...form, establishment: e.target.value })}
value={form.survey_name}
onChange={(e) => setForm({ ...form, survey_name: e.target.value })}
placeholder="Enter survey name"
/>
</div>
@ -557,6 +647,7 @@ const QuarterlyWindows = () => {
</div>
)}
</div>
</>
);
};