Compare commits
No commits in common. "5fd663a7d09fe268be0585aba24f2c014e43231d" and "099fbddada445c12133ad25fece3788986b76889" have entirely different histories.
5fd663a7d0
...
099fbddada
@ -434,27 +434,19 @@ export const SelectField = ({
|
|||||||
className={`w-full text-sm focus:outline-none ${className}`}
|
className={`w-full text-sm focus:outline-none ${className}`}
|
||||||
style={{
|
style={{
|
||||||
...inputStyle,
|
...inputStyle,
|
||||||
height: 'auto', // override fixed height
|
|
||||||
minHeight: '40px',
|
|
||||||
cursor: readOnly ? 'not-allowed' : 'pointer',
|
cursor: readOnly ? 'not-allowed' : 'pointer',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
padding: '8px 16px',
|
minHeight: '40px',
|
||||||
|
padding: '0 16px'
|
||||||
}}
|
}}
|
||||||
onClick={toggleDropdown}
|
onClick={toggleDropdown}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
tabIndex={readOnly ? -1 : 0}
|
tabIndex={readOnly ? -1 : 0}
|
||||||
>
|
>
|
||||||
<span
|
<span style={{ color: hasValue ? '#232528' : '#9CA3AF' }}>
|
||||||
style={{
|
|
||||||
color: hasValue ? '#232528' : '#9CA3AF',
|
|
||||||
flex: 1,
|
|
||||||
whiteSpace: 'normal',
|
|
||||||
lineHeight: '20px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{getDisplayValue()}
|
{getDisplayValue()}
|
||||||
</span>
|
</span>
|
||||||
{showClear ? (
|
{showClear ? (
|
||||||
|
|||||||
@ -1284,10 +1284,7 @@ setAdditionalForecastMonths([
|
|||||||
>
|
>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th rowSpan="2" className="bg-[#F2ECCF] text-[#92722A] px-4 py-1 text-xs font-semibold uppercase align-middle border-r border-[#E5E7EB]">
|
||||||
rowSpan="2"
|
|
||||||
className="w-[140px] bg-[#F2ECCF] text-[#92722A] px-4 py-1 text-xs font-semibold uppercase align-middle border-r border-[#E5E7EB]"
|
|
||||||
>
|
|
||||||
Metric
|
Metric
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
@ -1456,13 +1453,8 @@ setAdditionalForecastMonths([
|
|||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr className="border-t border-[#E5E7EB]">
|
<tr className="border-t border-[#E5E7EB]">
|
||||||
<td className="w-[180px] bg-[#FFFCF2] px-4 py-3 font-medium text-[#232528] border border-[#E5E7EB] align-top">
|
<td className="bg-[#FFFCF2] px-4 py-3 font-medium text-[#232528] border border-[#E5E7EB]">
|
||||||
<div className="flex flex-col leading-5">
|
Quantity ({product?.unit?.uom || 'units'})
|
||||||
<span>Quantity</span>
|
|
||||||
<span className="text-sm">
|
|
||||||
({product?.unit?.uom || 'units'})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{showPreviousNext ? (
|
{showPreviousNext ? (
|
||||||
|
|||||||
@ -92,7 +92,7 @@ const ManageSubmissions = () => {
|
|||||||
const [isRejecting, setIsRejecting] = React.useState(false);
|
const [isRejecting, setIsRejecting] = React.useState(false);
|
||||||
const [rejectReason, setRejectReason] = React.useState('');
|
const [rejectReason, setRejectReason] = React.useState('');
|
||||||
const [showRejectError, setShowRejectError] = React.useState(false);
|
const [showRejectError, setShowRejectError] = React.useState(false);
|
||||||
const [pageSize, setPageSize] = React.useState(10);
|
const pageSize = 10;
|
||||||
const [isInitialLoad, setIsInitialLoad] = React.useState(true);
|
const [isInitialLoad, setIsInitialLoad] = React.useState(true);
|
||||||
const [filters, setFilters] = React.useState({
|
const [filters, setFilters] = React.useState({
|
||||||
search: '',
|
search: '',
|
||||||
@ -398,7 +398,7 @@ React.useEffect(() => {
|
|||||||
const term = search.trim().toLowerCase();
|
const term = search.trim().toLowerCase();
|
||||||
|
|
||||||
const filteredItems = submissions.filter((item) => {
|
const filteredItems = submissions.filter((item) => {
|
||||||
if (!item || item.status === 'Draft') return false;
|
if (!item) return false;
|
||||||
|
|
||||||
const allValues = Object.values(item)
|
const allValues = Object.values(item)
|
||||||
.map((v) => String(v ?? '').toLowerCase())
|
.map((v) => String(v ?? '').toLowerCase())
|
||||||
@ -423,20 +423,15 @@ React.useEffect(() => {
|
|||||||
return filteredItems;
|
return filteredItems;
|
||||||
}, [submissions, search, year, quarter, emirate, status]);
|
}, [submissions, search, year, quarter, emirate, status]);
|
||||||
|
|
||||||
const nonDraftSubmissions = React.useMemo(
|
|
||||||
() => submissions.filter((i) => i.status !== 'Draft'),
|
|
||||||
[submissions]
|
|
||||||
);
|
|
||||||
|
|
||||||
const summaryCounts = React.useMemo(
|
const summaryCounts = React.useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
total: nonDraftSubmissions.length,
|
total: submissions.length,
|
||||||
approved: nonDraftSubmissions.filter((i) => i.status === 'Approved').length,
|
approved: submissions.filter((i) => i.status === 'Approved').length,
|
||||||
submitted: nonDraftSubmissions.filter((i) => i.status === 'Submitted').length,
|
submitted: submissions.filter((i) => i.status === 'Submitted').length,
|
||||||
rejected: nonDraftSubmissions.filter((i) => i.status === 'Rejected').length,
|
rejected: submissions.filter((i) => i.status === 'Rejected').length,
|
||||||
resubmitted: nonDraftSubmissions.filter((i) => i.status === 'Resubmitted').length,
|
resubmitted: submissions.filter((i) => i.status === 'Resubmitted').length,
|
||||||
}),
|
}),
|
||||||
[nonDraftSubmissions]
|
[submissions]
|
||||||
);
|
);
|
||||||
|
|
||||||
const headers = [
|
const headers = [
|
||||||
@ -699,11 +694,6 @@ const summaryCounts = React.useMemo(
|
|||||||
onPageChange: setCurrentPage,
|
onPageChange: setCurrentPage,
|
||||||
pageSize,
|
pageSize,
|
||||||
totalItems: filtered.length,
|
totalItems: filtered.length,
|
||||||
pageSizeOptions: [10, 20, 50, 100],
|
|
||||||
onPageSizeChange: (size) => {
|
|
||||||
setPageSize(size);
|
|
||||||
setCurrentPage(1);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
renderCell={(value, _, columnIndex) => {
|
renderCell={(value, _, columnIndex) => {
|
||||||
if (columnIndex === 7)
|
if (columnIndex === 7)
|
||||||
|
|||||||
@ -1775,7 +1775,7 @@ const displayedRows = sortedProfiles.map((item) => {
|
|||||||
item.establishmentName || "-",
|
item.establishmentName || "-",
|
||||||
|
|
||||||
// Contact Name (userProfileName → contactPersonName → createdBy → "-")
|
// Contact Name (userProfileName → contactPersonName → createdBy → "-")
|
||||||
// item.userProfileName || item.contactPersonName || "-",
|
item.userProfileName || item.contactPersonName || "-",
|
||||||
|
|
||||||
// Emirate
|
// Emirate
|
||||||
item.emirate || "-",
|
item.emirate || "-",
|
||||||
@ -2617,7 +2617,7 @@ const handleView = async (id) => {
|
|||||||
const requiredFields = [
|
const requiredFields = [
|
||||||
{ field: 'userProfileName', label: 'Name' },
|
{ field: 'userProfileName', label: 'Name' },
|
||||||
{ field: 'userProfileEmail', label: 'Email' },
|
{ field: 'userProfileEmail', label: 'Email' },
|
||||||
// { field: 'contactName', label: 'Contact Name' },
|
{ field: 'contactName', label: 'Contact Name' },
|
||||||
{ field: 'contactAddress', label: 'Contact Address' },
|
{ field: 'contactAddress', label: 'Contact Address' },
|
||||||
{ field: 'contactEmirate', label: 'Emirate' },
|
{ field: 'contactEmirate', label: 'Emirate' },
|
||||||
{ field: 'contactMobileNumber', label: 'Mobile Number' },
|
{ field: 'contactMobileNumber', label: 'Mobile Number' },
|
||||||
@ -3411,7 +3411,7 @@ const handleImport = async () => {
|
|||||||
<Table
|
<Table
|
||||||
headers={[
|
headers={[
|
||||||
{ name: 'Establishment Name', key: 'establishmentName', sortable: true, className: 'whitespace-nowrap' },
|
{ name: 'Establishment Name', key: 'establishmentName', sortable: true, className: 'whitespace-nowrap' },
|
||||||
// { name: 'Contact Name', key: 'contactName', sortable: true, className: 'whitespace-nowrap' },
|
{ name: 'Contact Name', key: 'contactName', sortable: true, className: 'whitespace-nowrap' },
|
||||||
{ name: 'Emirate', key: 'emirate', sortable: true, className: 'whitespace-nowrap' },
|
{ name: 'Emirate', key: 'emirate', sortable: true, className: 'whitespace-nowrap' },
|
||||||
{ name: 'ISIC Code', key: 'isicCode', sortable: false, className: 'whitespace-nowrap' },
|
{ name: 'ISIC Code', key: 'isicCode', sortable: false, className: 'whitespace-nowrap' },
|
||||||
{ name: 'Establishment ID', key: 'establishmentId', sortable: true, className: 'whitespace-nowrap' },
|
{ name: 'Establishment ID', key: 'establishmentId', sortable: true, className: 'whitespace-nowrap' },
|
||||||
@ -3467,7 +3467,7 @@ const handleImport = async () => {
|
|||||||
}}
|
}}
|
||||||
columnWidths={[
|
columnWidths={[
|
||||||
300, // Establishment Name (increased from 250)
|
300, // Establishment Name (increased from 250)
|
||||||
// 180, // Contact Name (increased from 150)
|
180, // Contact Name (increased from 150)
|
||||||
150, // Emirate (increased from 120)
|
150, // Emirate (increased from 120)
|
||||||
150, // ISIC Code (increased from 120)
|
150, // ISIC Code (increased from 120)
|
||||||
200, // Establishment ID (increased from 150)
|
200, // Establishment ID (increased from 150)
|
||||||
|
|||||||
@ -622,7 +622,7 @@ const IsicHsCodes = () => {
|
|||||||
|
|
||||||
// Filter data based on search term
|
// Filter data based on search term
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!searchTerm?.trim()) {
|
if (!searchTerm.trim()) {
|
||||||
setFilteredData(rowsData);
|
setFilteredData(rowsData);
|
||||||
} else {
|
} else {
|
||||||
const lowercasedSearch = searchTerm.toLowerCase();
|
const lowercasedSearch = searchTerm.toLowerCase();
|
||||||
@ -1623,7 +1623,7 @@ const handleExportCSV = () => {
|
|||||||
<option value="">Select Unit</option>
|
<option value="">Select Unit</option>
|
||||||
{unitOptions.map((option) => (
|
{unitOptions.map((option) => (
|
||||||
<option key={option.value} value={option.value}>
|
<option key={option.value} value={option.value}>
|
||||||
{option?.label}
|
{option.label.split('-')[1].trim()}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@ -19,8 +19,7 @@ import { Breadcrumbs } from '@mui/material';
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import Card from '@/components/common/Card';
|
import Card from '@/components/common/Card';
|
||||||
import triggerIcon from '@/assets/trigger.png';
|
import triggerIcon from '@/assets/trigger.png';
|
||||||
import { getManufacturingIndex, getManufacturingMonthlyOverview, autoSubmitSurvey, getManufacturingIndexFiltered } from '@/services/manufacturingIndex/ManufacturingIndex';
|
import { getManufacturingIndex, getManufacturingMonthlyOverview, autoSubmitSurvey } from '@/services/manufacturingIndex/ManufacturingIndex';
|
||||||
import apiClient from '@/services/api/apiClient';
|
|
||||||
|
|
||||||
// Import popup components
|
// Import popup components
|
||||||
import ManufacturingTotal from './ManufacturingIndexPopup/ManufacturingTotal';
|
import ManufacturingTotal from './ManufacturingIndexPopup/ManufacturingTotal';
|
||||||
@ -28,7 +27,6 @@ import ISIC2Digit from './ManufacturingIndexPopup/ISIC2Digit';
|
|||||||
import ISIC3Digit from './ManufacturingIndexPopup/ISIC3Digit';
|
import ISIC3Digit from './ManufacturingIndexPopup/ISIC3Digit';
|
||||||
import ISIC4Digit from './ManufacturingIndexPopup/ISIC4Digit';
|
import ISIC4Digit from './ManufacturingIndexPopup/ISIC4Digit';
|
||||||
|
|
||||||
|
|
||||||
// Custom components
|
// Custom components
|
||||||
const SelectField = (props) => (
|
const SelectField = (props) => (
|
||||||
<BaseSelectField
|
<BaseSelectField
|
||||||
@ -60,27 +58,20 @@ const ManufacturingIndex = () => {
|
|||||||
const [monthlyOverviewData, setMonthlyOverviewData] = useState(null);
|
const [monthlyOverviewData, setMonthlyOverviewData] = useState(null);
|
||||||
const [isPopupOpen, setIsPopupOpen] = useState(false);
|
const [isPopupOpen, setIsPopupOpen] = useState(false);
|
||||||
const [apiLoading, setApiLoading] = useState(false);
|
const [apiLoading, setApiLoading] = useState(false);
|
||||||
const [quarter, setQuarter] = useState('All');
|
const [quarter, setQuarter] = useState('Q2');
|
||||||
const [surveySubmitLoading, setSurveySubmitLoading] = useState(false);
|
const [surveySubmitLoading, setSurveySubmitLoading] = useState(false);
|
||||||
const [surveySubmitStatus, setSurveySubmitStatus] = useState('');
|
const [surveySubmitStatus, setSurveySubmitStatus] = useState('');
|
||||||
const [surveySubmitError, setSurveySubmitError] = useState(null);
|
const [surveySubmitError, setSurveySubmitError] = useState(null);
|
||||||
const [baseYearLoading, setBaseYearLoading] = useState(false);
|
|
||||||
const [baseYearStatus, setBaseYearStatus] = useState('');
|
|
||||||
const [baseYearError, setBaseYearError] = useState('');
|
|
||||||
const [nextScheduledRun, setNextScheduledRun] = useState(''); // Default value
|
const [nextScheduledRun, setNextScheduledRun] = useState(''); // Default value
|
||||||
const [refreshKey, setRefreshKey] = useState(0);
|
const [refreshKey, setRefreshKey] = useState(0);
|
||||||
|
|
||||||
const [pagination, setPagination] = useState({
|
const [pagination, setPagination] = useState({
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 10,
|
pageSize: 20,
|
||||||
totalItems: 0
|
totalItems: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
applyFilters(year, searchMonth, allMonths, quarter);
|
|
||||||
}, [year, quarter, searchMonth, allMonths]);
|
|
||||||
|
|
||||||
// Fetch data from API
|
// Fetch data from API
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
@ -137,7 +128,9 @@ const [baseYearError, setBaseYearError] = useState('');
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Debug: Log the actual data structure
|
// Debug: Log the actual data structure
|
||||||
|
console.log('API Response:', response);
|
||||||
|
console.log('Data Array:', dataArray);
|
||||||
|
console.log('Sample item:', dataArray[0]);
|
||||||
|
|
||||||
// Format the data
|
// Format the data
|
||||||
const formattedData = dataArray.map((item, index) => {
|
const formattedData = dataArray.map((item, index) => {
|
||||||
@ -162,14 +155,6 @@ const [baseYearError, setBaseYearError] = useState('');
|
|||||||
const monthName = dateObj.toLocaleString('en-US', { month: 'short' });
|
const monthName = dateObj.toLocaleString('en-US', { month: 'short' });
|
||||||
year = item.year || dateObj.getFullYear() || 2025;
|
year = item.year || dateObj.getFullYear() || 2025;
|
||||||
monthNumber = item.month || (dateObj.getMonth() + 1) || 1;
|
monthNumber = item.month || (dateObj.getMonth() + 1) || 1;
|
||||||
// Normalize quarter value to form 'Q1'..'Q4'
|
|
||||||
let quarterVal = item.quarter || item.quarter_name || item.quarterName || '-';
|
|
||||||
if (quarterVal !== '-' && quarterVal !== null && quarterVal !== undefined) {
|
|
||||||
quarterVal = String(quarterVal).trim();
|
|
||||||
// If it's numeric like '2' or 2 -> convert to 'Q2'
|
|
||||||
if (/^\d+$/.test(quarterVal)) quarterVal = `Q${quarterVal}`;
|
|
||||||
quarterVal = quarterVal.toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: item.id || `${year}-${monthNumber}` || `item-${index}`,
|
id: item.id || `${year}-${monthNumber}` || `item-${index}`,
|
||||||
@ -198,7 +183,7 @@ const [baseYearError, setBaseYearError] = useState('');
|
|||||||
timeZone: 'Asia/Dubai',
|
timeZone: 'Asia/Dubai',
|
||||||
timeZoneName: 'short'
|
timeZoneName: 'short'
|
||||||
}) : '-',
|
}) : '-',
|
||||||
quarter: quarterVal || '-',
|
quarter: item.quarter || item.quarter_name || '-',
|
||||||
totalWeight: item.total_weight || '-',
|
totalWeight: item.total_weight || '-',
|
||||||
status: item.status || 'Completed',
|
status: item.status || 'Completed',
|
||||||
rawData: item
|
rawData: item
|
||||||
@ -206,7 +191,7 @@ const [baseYearError, setBaseYearError] = useState('');
|
|||||||
});
|
});
|
||||||
|
|
||||||
setAllMonths(formattedData);
|
setAllMonths(formattedData);
|
||||||
applyFilters(year, searchMonth, formattedData, quarter);
|
applyFilters(year, searchMonth, formattedData);
|
||||||
setPagination(prev => ({
|
setPagination(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
totalItems: formattedData.length
|
totalItems: formattedData.length
|
||||||
@ -308,14 +293,12 @@ const [baseYearError, setBaseYearError] = useState('');
|
|||||||
setMonthlyOverviewData(null);
|
setMonthlyOverviewData(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply year, quarter and search filters
|
// Apply both year and search filters
|
||||||
const applyFilters = (selectedYear, searchTerm, monthsData = allMonths, selectedQuarter = quarter) => {
|
const applyFilters = (selectedYear, searchTerm, monthsData = allMonths) => {
|
||||||
|
|
||||||
const filtered = monthsData.filter(month => {
|
const filtered = monthsData.filter(month => {
|
||||||
const matchesYear = selectedYear === 'All' || month.year.toString() === selectedYear;
|
const matchesYear = selectedYear === 'All' || month.year.toString() === selectedYear;
|
||||||
const matchesSearch = searchTerm === '' || month.month.toLowerCase().includes(searchTerm.toLowerCase());
|
const matchesSearch = searchTerm === '' || month.month.toLowerCase().includes(searchTerm.toLowerCase());
|
||||||
const matchesQuarter = !selectedQuarter || selectedQuarter === 'All' || selectedQuarter === '-' ? true : (month.quarter || '').toUpperCase() === String(selectedQuarter).toUpperCase();
|
return matchesYear && matchesSearch;
|
||||||
return matchesYear && matchesSearch && matchesQuarter;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setFilteredMonths(filtered);
|
setFilteredMonths(filtered);
|
||||||
@ -327,47 +310,22 @@ const [baseYearError, setBaseYearError] = useState('');
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleYearChange = (e) => {
|
const handleYearChange = (e) => {
|
||||||
setYear(e.target.value);
|
const selectedYear = e.target.value;
|
||||||
};
|
setYear(selectedYear);
|
||||||
|
applyFilters(selectedYear, searchMonth);
|
||||||
const handleQuarterChange = (e) => {
|
|
||||||
setQuarter(e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSearchMonth = (e) => {
|
const handleSearchMonth = (e) => {
|
||||||
setSearchMonth(e.target.value);
|
const searchTerm = e.target.value;
|
||||||
|
setSearchMonth(searchTerm);
|
||||||
|
applyFilters(year, searchTerm);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBaseYear = async () => {
|
const handleQuarterChange = (e) => {
|
||||||
setSurveySubmitLoading(true);
|
const selectedQuarter = e.target.value;
|
||||||
setSurveySubmitStatus('');
|
setQuarter(selectedQuarter);
|
||||||
setSurveySubmitError('');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await apiClient.post('/calculate_base_year', {
|
|
||||||
baseYear: 2022,
|
|
||||||
forceRecalculate: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
setSurveySubmitStatus(
|
|
||||||
response?.data?.message ||
|
|
||||||
'Base year calculation completed successfully.'
|
|
||||||
);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
setSurveySubmitError(
|
|
||||||
error?.response?.data?.message ||
|
|
||||||
error.message ||
|
|
||||||
'Base year calculation failed.'
|
|
||||||
);
|
|
||||||
|
|
||||||
console.error('Error:', error);
|
|
||||||
} finally {
|
|
||||||
setSurveySubmitLoading(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleTriggerData = async () => {
|
const handleTriggerData = async () => {
|
||||||
setError(null);
|
setError(null);
|
||||||
setSurveySubmitStatus('');
|
setSurveySubmitStatus('');
|
||||||
@ -381,117 +339,12 @@ const handleBaseYear = async () => {
|
|||||||
setSurveySubmitLoading(true);
|
setSurveySubmitLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Trigger backend calculate/auto-submit
|
|
||||||
const response = await autoSubmitSurvey(parseInt(year, 10), quarter);
|
const response = await autoSubmitSurvey(parseInt(year, 10), quarter);
|
||||||
|
|
||||||
setSurveySubmitStatus(
|
setSurveySubmitStatus(
|
||||||
response?.data?.message || 'Survey auto-submit request sent successfully.'
|
response?.data?.message || 'Survey auto-submit request sent successfully.'
|
||||||
);
|
);
|
||||||
|
setRefreshKey(prev => prev + 1);
|
||||||
// After auto-submit, fetch the filtered manufacturing index for selected year and quarter/all
|
|
||||||
try {
|
|
||||||
const filteredResp = await getManufacturingIndexFiltered(
|
|
||||||
parseInt(year, 10),
|
|
||||||
quarter === 'All' ? undefined : quarter
|
|
||||||
);
|
|
||||||
|
|
||||||
// Extract array similar to initial fetch
|
|
||||||
let dataArray = [];
|
|
||||||
if (Array.isArray(filteredResp)) {
|
|
||||||
dataArray = filteredResp;
|
|
||||||
} else if (filteredResp && filteredResp.data && Array.isArray(filteredResp.data)) {
|
|
||||||
dataArray = filteredResp.data;
|
|
||||||
} else if (filteredResp && filteredResp.data && typeof filteredResp.data === 'object') {
|
|
||||||
if (Array.isArray(filteredResp.data.manufacturingIndex) || Array.isArray(filteredResp.data.data)) {
|
|
||||||
dataArray = filteredResp.data.manufacturingIndex || filteredResp.data.data || [];
|
|
||||||
} else {
|
|
||||||
dataArray = Object.values(filteredResp.data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const findArray = (obj) => {
|
|
||||||
if (Array.isArray(obj)) return obj;
|
|
||||||
if (typeof obj === 'object' && obj !== null) {
|
|
||||||
for (const key in obj) {
|
|
||||||
if (Array.isArray(obj[key])) return obj[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
dataArray = findArray(filteredResp);
|
|
||||||
}
|
|
||||||
|
|
||||||
const formattedData = dataArray.map((item, index) => {
|
|
||||||
let dateObj = new Date();
|
|
||||||
let yearVal = 2025;
|
|
||||||
let monthNumber = 1;
|
|
||||||
|
|
||||||
if (item.reference_date) {
|
|
||||||
dateObj = new Date(item.reference_date);
|
|
||||||
} else if (item.date) {
|
|
||||||
dateObj = new Date(item.date);
|
|
||||||
} else if (item.referenceDate) {
|
|
||||||
dateObj = new Date(item.referenceDate);
|
|
||||||
} else if (item.year && item.month) {
|
|
||||||
yearVal = item.year;
|
|
||||||
monthNumber = item.month;
|
|
||||||
dateObj = new Date(yearVal, monthNumber - 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const monthName = dateObj.toLocaleString('en-US', { month: 'short' });
|
|
||||||
yearVal = item.year || dateObj.getFullYear() || 2025;
|
|
||||||
monthNumber = item.month || (dateObj.getMonth() + 1) || 1;
|
|
||||||
// Normalize quarter value to form 'Q1'..'Q4'
|
|
||||||
let quarterVal = item.quarter || item.quarter_name || item.quarterName || '-';
|
|
||||||
if (quarterVal !== '-' && quarterVal !== null && quarterVal !== undefined) {
|
|
||||||
quarterVal = String(quarterVal).trim();
|
|
||||||
if (/^\d+$/.test(quarterVal)) quarterVal = `Q${quarterVal}`;
|
|
||||||
quarterVal = quarterVal.toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: item.id || `${yearVal}-${monthNumber}` || `item-${index}`,
|
|
||||||
month: `${monthName} ${yearVal}`,
|
|
||||||
monthName: monthName,
|
|
||||||
year: yearVal,
|
|
||||||
monthNumber: monthNumber,
|
|
||||||
index: item.manufacturing_index?.toString() || item.index?.toString() || item.manufacturingIndex?.toString() || '-',
|
|
||||||
mom: item.mom_change || item.mom || item.momChange ?
|
|
||||||
(parseFloat(item.mom_change || item.mom || item.momChange) > 0 ?
|
|
||||||
`+${item.mom_change || item.mom || item.momChange}` :
|
|
||||||
(item.mom_change || item.mom || item.momChange).toString()) :
|
|
||||||
'-',
|
|
||||||
yoy: item.yoy_change || item.yoy || item.yoyChange ?
|
|
||||||
(parseFloat(item.yoy_change || item.yoy || item.yoyChange) > 0 ?
|
|
||||||
`+${item.yoy_change || item.yoy || item.yoyChange}` :
|
|
||||||
(item.yoy_change || item.yoy || item.yoyChange).toString()) :
|
|
||||||
'-',
|
|
||||||
generated: item.generated_on || item.generatedOn || item.generated_at ?
|
|
||||||
new Date(item.generated_on || item.generatedOn || item.generated_at).toLocaleString('en-US', {
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit',
|
|
||||||
timeZone: 'Asia/Dubai',
|
|
||||||
timeZoneName: 'short'
|
|
||||||
}) : '-',
|
|
||||||
quarter: quarterVal || '-',
|
|
||||||
totalWeight: item.total_weight || '-',
|
|
||||||
status: item.status || 'Completed',
|
|
||||||
rawData: item
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
setAllMonths(formattedData);
|
|
||||||
applyFilters(year, searchMonth, formattedData, quarter);
|
|
||||||
setPagination(prev => ({
|
|
||||||
...prev,
|
|
||||||
totalItems: formattedData.length,
|
|
||||||
currentPage: 1
|
|
||||||
}));
|
|
||||||
} catch (fetchErr) {
|
|
||||||
console.error('Error fetching filtered manufacturing index after trigger:', fetchErr);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setSurveySubmitError(
|
setSurveySubmitError(
|
||||||
error?.response?.data?.message || error.message || 'Survey auto-submit failed.'
|
error?.response?.data?.message || error.message || 'Survey auto-submit failed.'
|
||||||
@ -742,10 +595,9 @@ const handleBaseYear = async () => {
|
|||||||
Coverage: Manufacturing (ISIC C)
|
Coverage: Manufacturing (ISIC C)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3 w-full sm:w-auto">
|
||||||
<div className="w-[180px]">
|
<div className="flex-1 sm:flex-none w-40">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
<img src="/assets/images/material-symbols_search-rounded.svg" alt="Search" className="h-5 w-5" />
|
<img src="/assets/images/material-symbols_search-rounded.svg" alt="Search" className="h-5 w-5" />
|
||||||
@ -759,39 +611,31 @@ const handleBaseYear = async () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center w-[99px]">
|
<div className="flex items-center w-auto min-w-[88px]">
|
||||||
<SelectField
|
<SelectField
|
||||||
value={year}
|
value={year}
|
||||||
onChange={handleYearChange}
|
onChange={handleYearChange}
|
||||||
className="w-full h-[40px]"
|
className="w-auto h-[40px]"
|
||||||
size="small"
|
size="small"
|
||||||
options={years.map(y => ({ value: y, label: y }))}
|
options={years.map(y => ({ value: y, label: y }))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center w-[89px]">
|
<div className="flex items-center w-auto min-w-[72px]">
|
||||||
<SelectField
|
<SelectField
|
||||||
value={quarter}
|
value={quarter}
|
||||||
onChange={handleQuarterChange}
|
onChange={handleQuarterChange}
|
||||||
className="w-full h-[40px]"
|
className="w-auto h-[40px]"
|
||||||
size="small"
|
size="small"
|
||||||
options={['All', 'Q1', 'Q2', 'Q3', 'Q4'].map(q => ({ value: q, label: q }))}
|
options={['Q1', 'Q2', 'Q3', 'Q4'].map(q => ({ value: q, label: q }))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="h-[40px] px-4 rounded-[6px] bg-[#F7F7F7] border border-[#C3C6CB] text-sm inline-flex items-center gap-2 hover:bg-gray-50 whitespace-nowrap font-medium text-[#232528]"
|
className="h-10 px-3 xl:px-4 rounded-[6px] bg-[#F7F7F7] border border-[#C3C6CB] text-sm inline-flex items-center gap-2 hover:bg-gray-50 whitespace-nowrap font-medium text-[#232528]"
|
||||||
onClick={handleTriggerData}
|
onClick={handleTriggerData}
|
||||||
>
|
>
|
||||||
<img src={triggerIcon} alt="Trigger" className="h-5 w-5" />
|
<img src={triggerIcon} alt="Trigger" className="h-5 w-5" />
|
||||||
Calculate
|
Trigger Data
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="h-[40px] px-4 rounded-[6px] bg-[#F7F7F7] border border-[#C3C6CB] text-sm inline-flex items-center gap-2 hover:bg-gray-50 whitespace-nowrap font-medium text-[#232528]"
|
|
||||||
onClick={handleBaseYear}
|
|
||||||
>
|
|
||||||
<img src={triggerIcon} alt="Trigger" className="h-5 w-5" />
|
|
||||||
Base Year
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="h-10 px-3 xl:px-4 rounded-[6px] bg-[#F7F7F7] border border-[#C3C6CB] text-sm inline-flex items-center gap-2 hover:bg-gray-50 whitespace-nowrap"
|
className="h-10 px-3 xl:px-4 rounded-[6px] bg-[#F7F7F7] border border-[#C3C6CB] text-sm inline-flex items-center gap-2 hover:bg-gray-50 whitespace-nowrap"
|
||||||
@ -801,7 +645,6 @@ const handleBaseYear = async () => {
|
|||||||
<span className="hidden xl:inline font-medium text-[#232528]">Export CSV</span>
|
<span className="hidden xl:inline font-medium text-[#232528]">Export CSV</span>
|
||||||
<span className="xl:hidden font-medium text-[#232528]">Export</span>
|
<span className="xl:hidden font-medium text-[#232528]">Export</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{surveySubmitLoading || surveySubmitStatus || surveySubmitError ? (
|
{surveySubmitLoading || surveySubmitStatus || surveySubmitError ? (
|
||||||
|
|||||||
@ -181,7 +181,7 @@ const showToast = (message, type = 'success') => {
|
|||||||
record.year,
|
record.year,
|
||||||
record.quarter,
|
record.quarter,
|
||||||
`${record.quarter} ${record.year}`,
|
`${record.quarter} ${record.year}`,
|
||||||
// 10, // Always show 10 for Total Products
|
10, // Always show 10 for Total Products
|
||||||
record.product_count || 0, // Show actual submitted products count
|
record.product_count || 0, // Show actual submitted products count
|
||||||
record.total_cost !== undefined ? (
|
record.total_cost !== undefined ? (
|
||||||
<span className="flex items-center">
|
<span className="flex items-center">
|
||||||
@ -237,21 +237,11 @@ const showToast = (message, type = 'success') => {
|
|||||||
</div>
|
</div>
|
||||||
) :
|
) :
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<button
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
setSelectedSubmission(record);
|
|
||||||
}}
|
|
||||||
className="focus:outline-none"
|
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
src="/assets/images/Eye.svg"
|
src="/assets/images/Eye.svg"
|
||||||
alt="View"
|
alt="View"
|
||||||
// className="w-5 h-5 opacity-60 hover:opacity-100 transition-opacity"
|
className="w-5 h-5 text-[#A88632] opacity-60 hover:opacity-100 transition-opacity"
|
||||||
className="w-5 h-5 text-[#92722A] opacity-60 hover:opacity-100 transition-opacity"
|
|
||||||
style={{ filter: 'invert(36%) sepia(49%) saturate(680%) hue-rotate(1deg) brightness(89%) contrast(91%)' }}
|
|
||||||
/>
|
/>
|
||||||
</button>
|
|
||||||
{record.status === 'Rejected' && (
|
{record.status === 'Rejected' && (
|
||||||
<HiMiniArrowPathRoundedSquare
|
<HiMiniArrowPathRoundedSquare
|
||||||
className={`w-5 h-5 transition-opacity ${
|
className={`w-5 h-5 transition-opacity ${
|
||||||
@ -501,7 +491,7 @@ const showToast = (message, type = 'success') => {
|
|||||||
</div>
|
</div>
|
||||||
<section className="bg-white rounded-lg border border-[#E2E8F0] shadow-sm">
|
<section className="bg-white rounded-lg border border-[#E2E8F0] shadow-sm">
|
||||||
<Table
|
<Table
|
||||||
headers={['Year', 'Quarter', 'Submission Window', 'Products Submitted', 'Total Value (AED)', 'Status', 'Date Submitted', 'Action']}
|
headers={['Year', 'Quarter', 'Submission Window', 'Total Products', 'Products Submitted', 'Total Value (AED)', 'Status', 'Date Submitted', 'Action']}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
beforeHeader={toolbar}
|
beforeHeader={toolbar}
|
||||||
separated
|
separated
|
||||||
@ -517,7 +507,7 @@ const showToast = (message, type = 'success') => {
|
|||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (columnIndex === 7) { // Action column
|
if (columnIndex === 8) { // Action column
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className="text-[#92722A] hover:underline text-sm font-medium focus:outline-none"
|
className="text-[#92722A] hover:underline text-sm font-medium focus:outline-none"
|
||||||
|
|||||||
@ -9,6 +9,8 @@ const PublicContactForm = () => {
|
|||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
userType: "establishment_user",
|
userType: "establishment_user",
|
||||||
|
establishmentName: "",
|
||||||
|
establishmentId: "",
|
||||||
email: "",
|
email: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -59,6 +61,9 @@ const PublicContactForm = () => {
|
|||||||
|
|
||||||
if (!data.userType) {
|
if (!data.userType) {
|
||||||
newErrors.userType = "Please select user type";
|
newErrors.userType = "Please select user type";
|
||||||
|
} else if (data.userType === 'establishment_user') {
|
||||||
|
if (!data.establishmentName.trim()) newErrors.establishmentName = "Required.";
|
||||||
|
if (!data.establishmentId.trim()) newErrors.establishmentId = "Required.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.email.trim()) newErrors.email = "Required.";
|
if (!data.email.trim()) newErrors.email = "Required.";
|
||||||
@ -89,6 +94,8 @@ const PublicContactForm = () => {
|
|||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
user_type: formData.userType,
|
user_type: formData.userType,
|
||||||
|
establishment_name: formData.establishmentName.trim(),
|
||||||
|
establishment_code: formData.establishmentId.trim(),
|
||||||
registered_email: formData.email.trim(),
|
registered_email: formData.email.trim(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -249,6 +256,48 @@ const PublicContactForm = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{formData.userType === 'establishment_user' && (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Establishment Name <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="establishmentName"
|
||||||
|
value={formData.establishmentName}
|
||||||
|
onChange={handleChange}
|
||||||
|
className={`block w-full h-10 rounded-md border-2 ${
|
||||||
|
errors.establishmentName ? "border-red-500" : "border-[#92722A]"
|
||||||
|
} focus:border-[#92722A] focus:ring-0 px-4 text-sm`}
|
||||||
|
placeholder="Enter establishment name"
|
||||||
|
/>
|
||||||
|
{errors.establishmentName && (
|
||||||
|
<p className="text-xs text-red-600 mt-1">{errors.establishmentName}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Establishment ID <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="establishmentId"
|
||||||
|
value={formData.establishmentId}
|
||||||
|
onChange={handleChange}
|
||||||
|
className={`block w-full h-10 rounded-md border-2 ${
|
||||||
|
errors.establishmentId ? "border-red-500" : "border-[#92722A]"
|
||||||
|
} focus:border-[#92722A] focus:ring-0 px-4 text-sm`}
|
||||||
|
placeholder="Enter establishment ID"
|
||||||
|
/>
|
||||||
|
{errors.establishmentId && (
|
||||||
|
<p className="text-xs text-red-600 mt-1">{errors.establishmentId}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<InputField
|
<InputField
|
||||||
label="Registered Email"
|
label="Registered Email"
|
||||||
name="email"
|
name="email"
|
||||||
|
|||||||
@ -12,22 +12,6 @@ export const getManufacturingIndex = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fetch manufacturing index with optional filters (year, quarter)
|
|
||||||
export const getManufacturingIndexFiltered = async (year, quarter) => {
|
|
||||||
try {
|
|
||||||
const params = {};
|
|
||||||
if (year) params.year = year.toString();
|
|
||||||
if (quarter) params.quarter = quarter.toString();
|
|
||||||
|
|
||||||
const response = await getRequest(`${MANUFACTURING_ENDPOINT}/getManufacturingIndex`, {
|
|
||||||
params,
|
|
||||||
});
|
|
||||||
return response;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching filtered manufacturing index:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
export const getManufacturingMonthlyOverview = async (year, quarter) => {
|
export const getManufacturingMonthlyOverview = async (year, quarter) => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|||||||
@ -67,8 +67,7 @@ const toUnitOption = (item) => {
|
|||||||
const id = toStringOrNull(item.id ?? item.unit_id ?? item.value ?? item.code);
|
const id = toStringOrNull(item.id ?? item.unit_id ?? item.value ?? item.code);
|
||||||
const short = toStringOrNull(item.uom_short_name ?? item.short_name);
|
const short = toStringOrNull(item.uom_short_name ?? item.short_name);
|
||||||
const name = toStringOrNull(item.uom ?? item.name ?? item.label);
|
const name = toStringOrNull(item.uom ?? item.name ?? item.label);
|
||||||
// const label = toStringOrNull(short && name ? `${short} - ${name}` : name ?? short);
|
const label = toStringOrNull(short && name ? `${short} - ${name}` : name ?? short);
|
||||||
const label = toStringOrNull(name ?? short);
|
|
||||||
const finalLabel = label ?? id;
|
const finalLabel = label ?? id;
|
||||||
if (!finalLabel) return null;
|
if (!finalLabel) return null;
|
||||||
return { label: finalLabel, value: id ?? finalLabel };
|
return { label: finalLabel, value: id ?? finalLabel };
|
||||||
@ -115,7 +114,7 @@ const toIsicOption = (item) => {
|
|||||||
return value ? { label: value, value } : null;
|
return value ? { label: value, value } : null;
|
||||||
}
|
}
|
||||||
if (typeof item === 'object') {
|
if (typeof item === 'object') {
|
||||||
const id = toStringOrNull(item.code ?? item.id ?? item.value);
|
const id = toStringOrNull(item.id ?? item.value ?? item.code);
|
||||||
const code = toStringOrNull(item.code);
|
const code = toStringOrNull(item.code);
|
||||||
const description = toStringOrNull(item.description ?? item.name ?? item.label);
|
const description = toStringOrNull(item.description ?? item.name ?? item.label);
|
||||||
const label = code && description ? `${code} - ${description}` : description ?? code ?? item.label;
|
const label = code && description ? `${code} - ${description}` : description ?? code ?? item.label;
|
||||||
@ -126,7 +125,6 @@ const toIsicOption = (item) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const getVariationReasons = async (config = {}) => {
|
export const getVariationReasons = async (config = {}) => {
|
||||||
const response = await getRequest(endpoints.variationReasons, config);
|
const response = await getRequest(endpoints.variationReasons, config);
|
||||||
return response.data;
|
return response.data;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user