Merge branch 'master' of https://bitbucket.org/jubilian/fcsc_ipi_frontend
This commit is contained in:
commit
2eb18764f3
@ -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
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user