added download sample
This commit is contained in:
parent
4a07c5e47c
commit
f2ba68dd77
@ -116,7 +116,6 @@ const EstablishmentInfo = ({
|
|||||||
try {
|
try {
|
||||||
const response = await getEstablishmentProducts(establishmentId);
|
const response = await getEstablishmentProducts(establishmentId);
|
||||||
if (response.status === 'success') {
|
if (response.status === 'success') {
|
||||||
console.log('Products data:', response.data); // Log the products data
|
|
||||||
setProducts(response.data || []);
|
setProducts(response.data || []);
|
||||||
} else {
|
} else {
|
||||||
setProductsError(response.message || 'Failed to load products');
|
setProductsError(response.message || 'Failed to load products');
|
||||||
@ -149,7 +148,6 @@ const EstablishmentInfo = ({
|
|||||||
}
|
}
|
||||||
// Or use current quarter/year as fallback
|
// Or use current quarter/year as fallback
|
||||||
const current = getCurrentQuarterAndYear();
|
const current = getCurrentQuarterAndYear();
|
||||||
console.log('Using current quarter/year as fallback:', current);
|
|
||||||
|
|
||||||
// Save to localStorage
|
// Save to localStorage
|
||||||
const surveyPeriod = {
|
const surveyPeriod = {
|
||||||
@ -168,7 +166,6 @@ const EstablishmentInfo = ({
|
|||||||
|
|
||||||
// Log when surveyData changes
|
// Log when surveyData changes
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
console.log('surveyData updated:', surveyData);
|
|
||||||
}, [surveyData]);
|
}, [surveyData]);
|
||||||
|
|
||||||
// Update the ref whenever surveyData changes
|
// Update the ref whenever surveyData changes
|
||||||
|
|||||||
@ -391,7 +391,6 @@ const ProductData = ({
|
|||||||
|
|
||||||
setIsLoadingPeriods(true);
|
setIsLoadingPeriods(true);
|
||||||
const quarterNumber = periodQuarter.replace('Q', ''); // Convert 'Q3' to '3'
|
const quarterNumber = periodQuarter.replace('Q', ''); // Convert 'Q3' to '3'
|
||||||
console.log('Fetching quarter periods with:', { year: periodYear, quarter: `Q${quarterNumber}` });
|
|
||||||
const response = await getQuarterPeriods(parseInt(periodYear), `Q${quarterNumber}`);
|
const response = await getQuarterPeriods(parseInt(periodYear), `Q${quarterNumber}`);
|
||||||
setQuarterPeriods(response.data);
|
setQuarterPeriods(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -577,7 +576,6 @@ const location = useLocation();
|
|||||||
year: year || ''
|
year: year || ''
|
||||||
}));
|
}));
|
||||||
}, [quarter, year]);
|
}, [quarter, year]);
|
||||||
console.log("surveyDatatest", surveyData);
|
|
||||||
|
|
||||||
const requiredMessage = 'Required';
|
const requiredMessage = 'Required';
|
||||||
const getAvailableProducts = (currentProductId) => {
|
const getAvailableProducts = (currentProductId) => {
|
||||||
|
|||||||
@ -236,7 +236,6 @@ const buildProductRows = (products = [], options = {}) => {
|
|||||||
_raw: { ...product }
|
_raw: { ...product }
|
||||||
};
|
};
|
||||||
|
|
||||||
// console.log(`Processed product ${index + 1}:`, row); // Debug log
|
|
||||||
return row;
|
return row;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error processing product at index ${index}:`, error, product);
|
console.error(`Error processing product at index ${index}:`, error, product);
|
||||||
|
|||||||
@ -93,6 +93,11 @@ const AdminUsers = () => {
|
|||||||
const [resettingPassword, setResettingPassword] = useState(false);
|
const [resettingPassword, setResettingPassword] = useState(false);
|
||||||
const [resetErrors, setResetErrors] = useState({});
|
const [resetErrors, setResetErrors] = useState({});
|
||||||
const [pageSize, setPageSize] = React.useState(10);
|
const [pageSize, setPageSize] = React.useState(10);
|
||||||
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
|
const [sortConfig, setSortConfig] = useState({
|
||||||
|
key: null,
|
||||||
|
direction: 'asc'
|
||||||
|
});
|
||||||
|
|
||||||
// Helper: show toast
|
// Helper: show toast
|
||||||
const showToast = (message, type = 'success') => {
|
const showToast = (message, type = 'success') => {
|
||||||
@ -193,10 +198,127 @@ const AdminUsers = () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const headers = ['Name', 'Email', 'Last Login', 'Status', 'Actions'];
|
const columnWidths = ['150px', '250px', '200px', '120px', '150px'];
|
||||||
const columnWidths = [100, 130, 150, 100, 130];
|
|
||||||
|
|
||||||
const rows = users.map((user) => [
|
const handleSort = (key) => {
|
||||||
|
let direction = 'asc';
|
||||||
|
if (sortConfig.key === key && sortConfig.direction === 'asc') {
|
||||||
|
direction = 'desc';
|
||||||
|
}
|
||||||
|
setSortConfig({ key, direction });
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSortedData = (data) => {
|
||||||
|
if (!sortConfig.key) return data;
|
||||||
|
|
||||||
|
return [...data].sort((a, b) => {
|
||||||
|
// Handle different data types for sorting
|
||||||
|
let aValue = a[sortConfig.key];
|
||||||
|
let bValue = b[sortConfig.key];
|
||||||
|
|
||||||
|
// For status, sort by the active/inactive state
|
||||||
|
if (sortConfig.key === 'status') {
|
||||||
|
aValue = a.is_active ? 1 : 0;
|
||||||
|
bValue = b.is_active ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For dates (lastLogin)
|
||||||
|
if (sortConfig.key === 'lastLogin' && aValue !== 'Never logged in' && bValue !== 'Never logged in') {
|
||||||
|
aValue = new Date(a.raw.updatedAt || a.raw.last_login || 0).getTime();
|
||||||
|
bValue = new Date(b.raw.updatedAt || b.raw.last_login || 0).getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For string comparison
|
||||||
|
if (typeof aValue === 'string' && typeof bValue === 'string') {
|
||||||
|
aValue = aValue.toLowerCase();
|
||||||
|
bValue = bValue.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aValue < bValue) {
|
||||||
|
return sortConfig.direction === 'asc' ? -1 : 1;
|
||||||
|
}
|
||||||
|
if (aValue > bValue) {
|
||||||
|
return sortConfig.direction === 'asc' ? 1 : -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const headers = [
|
||||||
|
{
|
||||||
|
name: 'Name',
|
||||||
|
key: 'name',
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Email',
|
||||||
|
key: 'email',
|
||||||
|
sortable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Last Login',
|
||||||
|
key: 'lastLogin',
|
||||||
|
sortable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Status',
|
||||||
|
key: 'status',
|
||||||
|
sortable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Actions',
|
||||||
|
key: 'actions',
|
||||||
|
sortable: false
|
||||||
|
}
|
||||||
|
].map(header => {
|
||||||
|
const baseClasses = "text-sm font-medium text-gray-700";
|
||||||
|
|
||||||
|
if (!header.sortable) {
|
||||||
|
return {
|
||||||
|
name: header.name,
|
||||||
|
className: 'text-left pl-6 pr-4 py-3',
|
||||||
|
style: { textAlign: 'left' }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: (
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-1 cursor-pointer"
|
||||||
|
onClick={() => handleSort(header.key)}
|
||||||
|
>
|
||||||
|
{header.name}
|
||||||
|
<img
|
||||||
|
src={
|
||||||
|
sortConfig.key === header.key
|
||||||
|
? "/assets/images/active-sort.svg"
|
||||||
|
: "/assets/images/inactive-sort.svg"
|
||||||
|
}
|
||||||
|
alt="Sort"
|
||||||
|
className={`w-3 h-3 transition-transform ${
|
||||||
|
sortConfig.key === header.key && sortConfig.direction === 'asc' ? 'transform rotate-180' : ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
className: 'text-left pl-6 pr-4 py-3',
|
||||||
|
style: { textAlign: 'left' }
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter users based on search term
|
||||||
|
const filteredUsers = searchTerm
|
||||||
|
? users.filter(
|
||||||
|
(user) =>
|
||||||
|
user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
|
user.email.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
|
)
|
||||||
|
: users;
|
||||||
|
|
||||||
|
// Apply sorting to filtered users
|
||||||
|
const sortedUsers = getSortedData(filteredUsers);
|
||||||
|
|
||||||
|
const rows = sortedUsers.map((user) => [
|
||||||
user.name,
|
user.name,
|
||||||
user.email,
|
user.email,
|
||||||
user.lastLogin,
|
user.lastLogin,
|
||||||
@ -569,21 +691,23 @@ const AdminUsers = () => {
|
|||||||
|
|
||||||
const tableToolbar = (
|
const tableToolbar = (
|
||||||
<div className="px-6 py-4 flex flex-col lg:flex-row lg:items-center lg:justify-between gap-3 bg-white">
|
<div className="px-6 py-4 flex flex-col lg:flex-row lg:items-center lg:justify-between gap-3 bg-white">
|
||||||
<h3 className="text-sm font-medium text-[#232528]">Admin Users</h3>
|
<h3 className="text-sm font-medium text-[#232528] whitespace-nowrap">Admin Users</h3>
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:gap-3 w-full lg:w-auto">
|
<div className="flex flex-col sm:flex-row sm:items-center justify-end gap-4 w-full">
|
||||||
<div className="relative flex-1 min-w-[220px]">
|
<div className="relative">
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Search by Names, Email"
|
|
||||||
className="w-full h-10 rounded-md border border-[#E5E7EB] pl-10 pr-4 text-sm focus:outline-none"
|
|
||||||
/>
|
|
||||||
<img
|
<img
|
||||||
src={searchIconSrc}
|
src={searchIconSrc}
|
||||||
alt="Search"
|
alt="Search"
|
||||||
className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5"
|
className="absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 transform"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search by Names, Email"
|
||||||
|
className="h-10 w-[400px] rounded-lg border border-[#E6EAF5] pl-10 pr-4 text-sm focus:outline-none focus:ring-2 focus:ring-[#B68A35] focus:ring-opacity-50"
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 sm:justify-end">
|
<div className="flex items-center gap-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@ -11,7 +11,8 @@ import {
|
|||||||
updateEstablishment,
|
updateEstablishment,
|
||||||
deleteEstablishment,
|
deleteEstablishment,
|
||||||
uploadCompanyProfileCSV,
|
uploadCompanyProfileCSV,
|
||||||
uploadCompanyProfileCSVAlternative
|
uploadCompanyProfileCSVAlternative,
|
||||||
|
downloadSampleFile
|
||||||
} from '@/services/establishments/establishmentService';
|
} from '@/services/establishments/establishmentService';
|
||||||
import { fetchProducts } from '@/services/masters/masterService';
|
import { fetchProducts } from '@/services/masters/masterService';
|
||||||
|
|
||||||
@ -227,7 +228,7 @@ const validateCSVFile = (file, existingEstablishments = []) => {
|
|||||||
const headers = lines[0].split(',').map(h => h.trim().replace(/"/g, ''));
|
const headers = lines[0].split(',').map(h => h.trim().replace(/"/g, ''));
|
||||||
|
|
||||||
// Check required headers
|
// Check required headers
|
||||||
const requiredHeaders = ['Establishment Id', 'Factory Name', 'Email', 'Emirate', 'Total Employment'];
|
const requiredHeaders = ['Establishment Id', 'Factory Name', 'Email', 'Emirate'];
|
||||||
const missingHeaders = requiredHeaders.filter(header =>
|
const missingHeaders = requiredHeaders.filter(header =>
|
||||||
!headers.some(h => h.toLowerCase() === header.toLowerCase())
|
!headers.some(h => h.toLowerCase() === header.toLowerCase())
|
||||||
);
|
);
|
||||||
@ -422,9 +423,6 @@ const validateCSVFile = (file, existingEstablishments = []) => {
|
|||||||
console.group('Duplicate Errors');
|
console.group('Duplicate Errors');
|
||||||
duplicateErrors.forEach((error, index) => {
|
duplicateErrors.forEach((error, index) => {
|
||||||
console.group(`Error ${index + 1} (Row ${error.row})`);
|
console.group(`Error ${index + 1} (Row ${error.row})`);
|
||||||
console.log('Type:', error.isSystemDuplicate ? 'System Duplicate' : 'File Duplicate');
|
|
||||||
console.log('Establishment ID:', error.value);
|
|
||||||
console.log('Message:', error.message);
|
|
||||||
if (error.duplicateRows) {
|
if (error.duplicateRows) {
|
||||||
console.log('Duplicate Rows:', error.duplicateRows.join(', '));
|
console.log('Duplicate Rows:', error.duplicateRows.join(', '));
|
||||||
}
|
}
|
||||||
@ -437,9 +435,6 @@ const validateCSVFile = (file, existingEstablishments = []) => {
|
|||||||
console.group('Validation Errors');
|
console.group('Validation Errors');
|
||||||
otherErrors.forEach((error, index) => {
|
otherErrors.forEach((error, index) => {
|
||||||
console.group(`Error ${index + 1} (Row ${error.row})`);
|
console.group(`Error ${index + 1} (Row ${error.row})`);
|
||||||
console.log('Column:', error.column);
|
|
||||||
console.log('Value:', error.value);
|
|
||||||
console.log('Message:', error.message);
|
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
});
|
});
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
@ -534,6 +529,40 @@ const CompanyProfile = () => {
|
|||||||
const [isUpdating, setIsUpdating] = React.useState(false);
|
const [isUpdating, setIsUpdating] = React.useState(false);
|
||||||
const [formSubmitted, setFormSubmitted] = React.useState(false);
|
const [formSubmitted, setFormSubmitted] = React.useState(false);
|
||||||
const [isViewMode, setIsViewMode] = React.useState(false);
|
const [isViewMode, setIsViewMode] = React.useState(false);
|
||||||
|
const [sortConfig, setSortConfig] = React.useState({
|
||||||
|
key: null,
|
||||||
|
direction: 'asc'
|
||||||
|
});
|
||||||
|
const handleSort = (key) => {
|
||||||
|
let direction = 'asc';
|
||||||
|
if (sortConfig.key === key && sortConfig.direction === 'asc') {
|
||||||
|
direction = 'desc';
|
||||||
|
}
|
||||||
|
setSortConfig({ key, direction });
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSortedData = (data) => {
|
||||||
|
if (!sortConfig.key) return data;
|
||||||
|
|
||||||
|
return [...data].sort((a, b) => {
|
||||||
|
let aValue = a[sortConfig.key];
|
||||||
|
let bValue = b[sortConfig.key];
|
||||||
|
|
||||||
|
if (sortConfig.key === 'estimatedMapped') {
|
||||||
|
return sortConfig.direction === 'asc'
|
||||||
|
? aValue - bValue
|
||||||
|
: bValue - aValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aValue < bValue) {
|
||||||
|
return sortConfig.direction === 'asc' ? -1 : 1;
|
||||||
|
}
|
||||||
|
if (aValue > bValue) {
|
||||||
|
return sortConfig.direction === 'asc' ? 1 : -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Import CSV state
|
// Import CSV state
|
||||||
@ -729,28 +758,53 @@ const CompanyProfile = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadSampleCSV = () => {
|
// const downloadSampleCSV = () => {
|
||||||
const sampleData = [
|
// const sampleData = [
|
||||||
['Establishment Id', 'Factory Name', 'Email', 'Emirate', 'Total Employment', 'HS Code 1'],
|
// ['Establishment Id', 'Factory Name', 'Email', 'Emirate', 'Total Employment', 'HS Code 1'],
|
||||||
['EST001', 'ABC Manufacturing', 'contact@abcmanufacturing.com', 'Dubai', '150', '1234567890'],
|
// ['EST001', 'ABC Manufacturing', 'contact@abcmanufacturing.com', 'Dubai', '150', '1234567890'],
|
||||||
['EST002', 'XYZ Industries', 'info@xyzindustries.com', 'Sharjah', '200', '9876543210'],
|
// ['EST002', 'XYZ Industries', 'info@xyzindustries.com', 'Sharjah', '200', '9876543210'],
|
||||||
['EST003', 'Global Textiles Ltd', 'sales@globaltextiles.com', 'Abu Dhabi', '85', '4567890123']
|
// ['EST003', 'Global Textiles Ltd', 'sales@globaltextiles.com', 'Abu Dhabi', '85', '4567890123']
|
||||||
];
|
// ];
|
||||||
const csvContent = sampleData.map(row =>
|
// const csvContent = sampleData.map(row =>
|
||||||
row.map(field => `"${field}"`).join(',')
|
// row.map(field => `"${field}"`).join(',')
|
||||||
).join('\n');
|
// ).join('\n');
|
||||||
|
|
||||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
// const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||||
const url = URL.createObjectURL(blob);
|
// const url = URL.createObjectURL(blob);
|
||||||
|
// const link = document.createElement('a');
|
||||||
|
// link.href = url;
|
||||||
|
// link.setAttribute('download', 'company-profile-template.csv');
|
||||||
|
// document.body.appendChild(link);
|
||||||
|
// link.click();
|
||||||
|
// document.body.removeChild(link);
|
||||||
|
// URL.revokeObjectURL(url);
|
||||||
|
// };
|
||||||
|
const downloadSampleCSV = async () => {
|
||||||
|
try {
|
||||||
|
const response = await downloadSampleFile({
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a blob from the response
|
||||||
|
const blob = new Blob([response], { type: 'text/csv' });
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = url;
|
link.href = url;
|
||||||
link.setAttribute('download', 'company-profile-template.csv');
|
link.setAttribute('download', 'company-profile-template.csv');
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
};
|
|
||||||
|
// Show success message
|
||||||
|
showToast('success', 'Sample file downloaded successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading sample file:', error);
|
||||||
|
showToast('error', 'Failed to download sample file');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const currentUserId = React.useMemo(() => {
|
const currentUserId = React.useMemo(() => {
|
||||||
try {
|
try {
|
||||||
const profile = sessionStorage.getItem('user_profile');
|
const profile = sessionStorage.getItem('user_profile');
|
||||||
@ -994,7 +1048,7 @@ const CompanyProfile = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 🟡 Show Remarks ONLY IF Current Production code is filled AND different from Business Register */}
|
{/* Show Remarks ONLY IF Current Production code is filled AND different from Business Register */}
|
||||||
{form.industryCodeProduction && form.industryCodeBusiness !== form.industryCodeProduction && (
|
{form.industryCodeProduction && form.industryCodeBusiness !== form.industryCodeProduction && (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<TextField
|
<TextField
|
||||||
@ -1401,14 +1455,16 @@ const CompanyProfile = () => {
|
|||||||
|
|
||||||
setProducts(formattedProducts);
|
setProducts(formattedProducts);
|
||||||
|
|
||||||
// Filter out already selected products
|
// Filter out already selected products - Updated comparison logic
|
||||||
const available = formattedProducts.filter(p =>
|
const available = formattedProducts.filter(p =>
|
||||||
!selectedProducts.some(sp => sp.id === p.id || sp.product_id === p.product_id)
|
!selectedProducts.some(sp =>
|
||||||
|
(sp.id && (sp.id === p.id || sp.id === p.product_id)) ||
|
||||||
|
(sp.product_id && (sp.product_id === p.id || sp.product_id === p.product_id)) ||
|
||||||
|
(sp.value && (sp.value === p.id || sp.value === p.product_id))
|
||||||
|
)
|
||||||
);
|
);
|
||||||
setAvailableProducts(available);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setAvailableProducts(available);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading products:', error);
|
console.error('Error loading products:', error);
|
||||||
showToast('error', 'Failed to load products');
|
showToast('error', 'Failed to load products');
|
||||||
@ -1430,7 +1486,11 @@ const CompanyProfile = () => {
|
|||||||
|
|
||||||
if (!searchTerm) {
|
if (!searchTerm) {
|
||||||
setAvailableProducts(products.filter(p =>
|
setAvailableProducts(products.filter(p =>
|
||||||
!selectedProducts.some(sp => sp.id === p.id)
|
!selectedProducts.some(sp =>
|
||||||
|
(sp.id && (sp.id === p.id || sp.id === p.product_id)) ||
|
||||||
|
(sp.product_id && (sp.product_id === p.id || sp.product_id === p.product_id)) ||
|
||||||
|
(sp.value && (sp.value === p.id || sp.value === p.product_id))
|
||||||
|
)
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1439,8 +1499,14 @@ const CompanyProfile = () => {
|
|||||||
const productName = (product.productName || product.label || product.product_name || '').toLowerCase();
|
const productName = (product.productName || product.label || product.product_name || '').toLowerCase();
|
||||||
const hsCode = (product.hsCode || product.hs_code || '').toLowerCase();
|
const hsCode = (product.hsCode || product.hs_code || '').toLowerCase();
|
||||||
|
|
||||||
return (productName.includes(searchTerm) || hsCode.includes(searchTerm)) &&
|
const isMatch = productName.includes(searchTerm) || hsCode.includes(searchTerm);
|
||||||
!selectedProducts.some(sp => sp.id === product.id);
|
const isSelected = selectedProducts.some(sp =>
|
||||||
|
(sp.id && (sp.id === product.id || sp.id === product.product_id)) ||
|
||||||
|
(sp.product_id && (sp.product_id === product.id || sp.product_id === product.product_id)) ||
|
||||||
|
(sp.value && (sp.value === product.id || sp.value === product.product_id))
|
||||||
|
);
|
||||||
|
|
||||||
|
return isMatch && !isSelected;
|
||||||
});
|
});
|
||||||
|
|
||||||
setAvailableProducts(filtered);
|
setAvailableProducts(filtered);
|
||||||
@ -1584,7 +1650,9 @@ const CompanyProfile = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const displayedRows = filteredProfiles.map((item) => {
|
const sortedProfiles = getSortedData(filteredProfiles);
|
||||||
|
const displayedRows = sortedProfiles.map((item) => {
|
||||||
|
|
||||||
return [
|
return [
|
||||||
// Establishment Name
|
// Establishment Name
|
||||||
item.establishmentName || "-",
|
item.establishmentName || "-",
|
||||||
@ -3013,6 +3081,8 @@ const handleImport = async () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white w-full rounded-lg shadow-sm ring-1 ring-[#E5E7EB]">
|
<div className="bg-white w-full rounded-lg shadow-sm ring-1 ring-[#E5E7EB]">
|
||||||
{toast && (
|
{toast && (
|
||||||
@ -3142,34 +3212,58 @@ const handleImport = async () => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Table
|
<Table
|
||||||
headers={[
|
headers={[
|
||||||
'Establishment Name',
|
{ name: 'Establishment Name', key: 'establishmentName', sortable: true, className: 'whitespace-nowrap' },
|
||||||
'Contact Name',
|
{ name: 'Contact Name', key: 'contactName', sortable: true, className: 'whitespace-nowrap' },
|
||||||
'Emirate',
|
{ name: 'Emirate', key: 'emirate', sortable: true, className: 'whitespace-nowrap' },
|
||||||
'ISIC Code',
|
{ name: 'ISIC Code', key: 'isicCode', sortable: false, className: 'whitespace-nowrap' },
|
||||||
'Establishment ID',
|
{ name: 'Establishment ID', key: 'establishmentId', sortable: true, className: 'whitespace-nowrap' },
|
||||||
'Products',
|
{ name: 'Products', key: 'products', sortable: false, className: 'whitespace-nowrap' },
|
||||||
'Total Employees',
|
{ name: 'Total Employees', key: 'totalEmployees', sortable: false, className: 'whitespace-nowrap' },
|
||||||
'Created By',
|
{ name: 'Created By', key: 'createdBy', sortable: true, className: 'whitespace-nowrap' },
|
||||||
'Created On',
|
{ name: 'Created On', key: 'createdOn', sortable: true, className: 'whitespace-nowrap' },
|
||||||
'Last Updated',
|
{ name: 'Last Updated', key: 'lastUpdated', sortable: true, className: 'whitespace-nowrap' },
|
||||||
'Status',
|
{ name: 'Status', key: 'status', sortable: false, className: 'whitespace-nowrap' },
|
||||||
'Actions',
|
{ name: 'Actions', key: 'actions', sortable: false, className: 'whitespace-nowrap' }
|
||||||
]}
|
].map(header => {
|
||||||
|
const isSortable = header.sortable;
|
||||||
|
const isActive = sortConfig.key === header.key;
|
||||||
|
const isAsc = sortConfig.direction === 'asc';
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: (
|
||||||
|
<div
|
||||||
|
className={`flex items-center ${isSortable ? 'cursor-pointer gap-1' : 'gap-1'}`}
|
||||||
|
onClick={isSortable ? () => handleSort(header.key) : undefined}
|
||||||
|
>
|
||||||
|
<span className="whitespace-nowrap">{header.name}</span>
|
||||||
|
{isSortable ? (
|
||||||
|
<img
|
||||||
|
src={isActive ? "/assets/images/active-sort.svg" : "/assets/images/inactive-sort.svg"}
|
||||||
|
alt="Sort"
|
||||||
|
className={`w-3 h-3 transition-transform ${isActive && isAsc ? 'transform rotate-180' : ''}`}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span className="w-3 h-3" /> // Invisible placeholder to maintain alignment
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
})}
|
||||||
rows={displayedRows}
|
rows={displayedRows}
|
||||||
columnWidths={[
|
columnWidths={[
|
||||||
250, // Establishment Name (250px)
|
300, // Establishment Name (increased from 250)
|
||||||
150, // Contact Name (150px)
|
180, // Contact Name (increased from 150)
|
||||||
120, // Emirate (120px)
|
150, // Emirate (increased from 120)
|
||||||
120, // ISIC Code (120px)
|
150, // ISIC Code (increased from 120)
|
||||||
150, // Establishment ID (150px)
|
200, // Establishment ID (increased from 150)
|
||||||
100, // Products (100px)
|
150, // Products (increased from 100)
|
||||||
130, // Total Employees (120px)
|
180, // Total Employees (increased from 150)
|
||||||
120, // Created By (120px)
|
180, // Created By (increased from 120)
|
||||||
120, // Created On (120px)
|
150, // Created On (increased from 120)
|
||||||
120, // Last Updated (120px)
|
180, // Last Updated (increased from 120)
|
||||||
100, // Status (100px)
|
120, // Status (increased from 100)
|
||||||
150, // Actions (150px)
|
150 // Actions (increased from 100)
|
||||||
]}
|
]}
|
||||||
pagination={{
|
pagination={{
|
||||||
currentPage,
|
currentPage,
|
||||||
@ -3425,7 +3519,7 @@ const handleImport = async () => {
|
|||||||
<div className="absolute inset-0 bg-black/40" onClick={closeImportModal} />
|
<div className="absolute inset-0 bg-black/40" onClick={closeImportModal} />
|
||||||
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[520px] bg-white rounded-lg shadow-xl border border-[#E5E7EB]">
|
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[520px] bg-white rounded-lg shadow-xl border border-[#E5E7EB]">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-6 border-b border-[#E5E7EB]">
|
<div className="flex items-center justify-between p-6 border-[#E5E7EB]">
|
||||||
<h3 className="text-lg font-semibold text-[#111827]">Import Company Profiles</h3>
|
<h3 className="text-lg font-semibold text-[#111827]">Import Company Profiles</h3>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -3439,10 +3533,10 @@ const handleImport = async () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="p-6 space-y-4">
|
<div className="p-6 hover:bg-gray-50 border-[#D1D5DB] hover:border-[#92722A]
|
||||||
{/* File Upload Area */}
|
mt-2 mb-6 mx-10 border-2 border-dashed"> {/* File Upload Area */}
|
||||||
<div
|
<div
|
||||||
className={`border-2 border-dashed rounded-lg p-6 text-center cursor-pointer transition-colors ${
|
className={` rounded-lg px-4 text-center cursor-pointer transition-colors ${
|
||||||
dragActive
|
dragActive
|
||||||
? 'border-[#92722A] bg-[#FDF8EA]'
|
? 'border-[#92722A] bg-[#FDF8EA]'
|
||||||
: 'border-[#D1D5DB] hover:border-[#92722A] hover:bg-gray-50'
|
: 'border-[#D1D5DB] hover:border-[#92722A] hover:bg-gray-50'
|
||||||
@ -3453,7 +3547,7 @@ const handleImport = async () => {
|
|||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col items-center justify-center space-y-3">
|
<div className="flex flex-col items-center justify-center ">
|
||||||
<div className="w-12 h-12 bg-[#FDF8EA] rounded-full flex items-center justify-center">
|
<div className="w-12 h-12 bg-[#FDF8EA] rounded-full flex items-center justify-center">
|
||||||
<img src={uploadImportIconSrc} alt="Upload" className="w-6 h-6" />
|
<img src={uploadImportIconSrc} alt="Upload" className="w-6 h-6" />
|
||||||
</div>
|
</div>
|
||||||
@ -3461,7 +3555,7 @@ const handleImport = async () => {
|
|||||||
<p className="text-sm font-medium text-gray-900">
|
<p className="text-sm font-medium text-gray-900">
|
||||||
{file ? file.name : 'Drop your CSV file here or browse'}
|
{file ? file.name : 'Drop your CSV file here or browse'}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-gray-500 mt-1">
|
<p className="text-sm text-gray-500 my-1">
|
||||||
Supports .csv files only (Max 10MB)
|
Supports .csv files only (Max 10MB)
|
||||||
</p>
|
</p>
|
||||||
{file && validationResults && (
|
{file && validationResults && (
|
||||||
@ -3472,7 +3566,7 @@ const handleImport = async () => {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="px-4 py-2 text-sm font-medium text-[#92722A] bg-white border border-[#92722A] rounded-md hover:bg-[#FDF8EA]"
|
className="px-4 py-2 mt-2 text-sm font-medium text-[#92722A] bg-white border border-[#92722A] rounded-md hover:bg-[#FDF8EA]"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
fileInputRef.current?.click();
|
fileInputRef.current?.click();
|
||||||
@ -3490,6 +3584,60 @@ const handleImport = async () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mandatory Fields Note */}
|
||||||
|
|
||||||
|
<div className="mt-3">
|
||||||
|
<div className="rounded-md bg-blue-50 p-4">
|
||||||
|
|
||||||
|
{/* Note Title */}
|
||||||
|
<div className="flex items-start gap-2 text-blue-800 text-sm">
|
||||||
|
<svg className="w-3.5 h-3.5 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h2a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
<span className="font-medium">Note:</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Everything below becomes same font size */}
|
||||||
|
<div className="text-xs text-blue-800 ml-6 mt-1">
|
||||||
|
|
||||||
|
<p>The following fields are mandatory for bulk import and must be filled in:</p>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-2 mt-2">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>•</span><span>Establishment ID</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>•</span><span>Factory Name</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>•</span><span>User Name</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>•</span><span>Email</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>•</span><span>Emirate</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>•</span><span>HS Code</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="mt-3">
|
||||||
|
If any of these fields are missing, the application will return an error.
|
||||||
|
Please review your data carefully before uploading.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Error Message */}
|
{/* Error Message */}
|
||||||
{importError && (
|
{importError && (
|
||||||
<div className="p-3 bg-red-50 border border-red-200 rounded-md">
|
<div className="p-3 bg-red-50 border border-red-200 rounded-md">
|
||||||
@ -3502,8 +3650,10 @@ const handleImport = async () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Sample CSV Link */}
|
|
||||||
<div className="text-center">
|
</div>
|
||||||
|
{/* Sample CSV Link */}
|
||||||
|
<div className="text-center -mt-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={downloadSampleCSV}
|
onClick={downloadSampleCSV}
|
||||||
@ -3512,10 +3662,8 @@ const handleImport = async () => {
|
|||||||
Download sample CSV template
|
Download sample CSV template
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="flex justify-end p-6 border-t border-[#E5E7EB] space-x-3">
|
<div className="flex justify-end p-6 border-[#E5E7EB] space-x-3">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={closeImportModal}
|
onClick={closeImportModal}
|
||||||
|
|||||||
@ -227,10 +227,7 @@ const EditCompanyProfile = () => {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const cities = await fetchCityTowns({ emirateId: selectedEmirate.value });
|
const cities = await fetchCityTowns({ emirateId: selectedEmirate.value });
|
||||||
|
|
||||||
console.log('Loaded cities for emirate', emirateName, ':', cities);
|
|
||||||
|
|
||||||
setContactCityOptions(cities);
|
setContactCityOptions(cities);
|
||||||
return cities;
|
return cities;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -242,22 +239,21 @@ const EditCompanyProfile = () => {
|
|||||||
}
|
}
|
||||||
}, [emirateOptions]);
|
}, [emirateOptions]);
|
||||||
|
|
||||||
// Load products using your service function
|
// Load products and filter out selected ones
|
||||||
const loadProducts = useCallback(async () => {
|
const loadProducts = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoadingProducts(true);
|
setIsLoadingProducts(true);
|
||||||
const products = await fetchProducts();
|
const products = await fetchProducts();
|
||||||
|
// Filter out selected products
|
||||||
console.log('Processed products data:', products);
|
const filteredProducts = products.filter(product => {
|
||||||
|
return !selectedProducts.some(selected =>
|
||||||
setAvailableProducts(prevProducts => {
|
(selected.value && (selected.value === product.value || selected.value === product.id || selected.value === product.product_id)) ||
|
||||||
// Only update if products have actually changed to prevent unnecessary re-renders
|
(selected.id && (selected.id === product.id || selected.id === product.value || selected.id === product.product_id)) ||
|
||||||
if (JSON.stringify(prevProducts) !== JSON.stringify(products)) {
|
(selected.product_id && (selected.product_id === product.product_id || selected.product_id === product.id || selected.product_id === product.value))
|
||||||
return products;
|
);
|
||||||
}
|
|
||||||
return prevProducts;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setAvailableProducts(filteredProducts);
|
||||||
return products;
|
return products;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading products:', error);
|
console.error('Error loading products:', error);
|
||||||
@ -265,75 +261,113 @@ const EditCompanyProfile = () => {
|
|||||||
} finally {
|
} finally {
|
||||||
setIsLoadingProducts(false);
|
setIsLoadingProducts(false);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [selectedProducts]);
|
||||||
|
|
||||||
// Product search handler
|
// Product search handler
|
||||||
const handleProductSearch = (e) => {
|
const handleProductSearch = (e) => {
|
||||||
const searchTerm = e.target.value.toLowerCase();
|
const searchTerm = e.target.value.toLowerCase();
|
||||||
setProductSearchTerm(searchTerm);
|
setProductSearchTerm(searchTerm);
|
||||||
|
|
||||||
// Get the full list of products from the API
|
if (!searchTerm) {
|
||||||
const loadAndFilterProducts = async () => {
|
// If search is cleared, reload all products with selected ones filtered out
|
||||||
try {
|
loadProducts();
|
||||||
setIsLoadingProducts(true);
|
return;
|
||||||
const allProducts = await fetchProducts();
|
}
|
||||||
|
|
||||||
if (!searchTerm) {
|
|
||||||
// If search is cleared, show all products except selected ones
|
|
||||||
setAvailableProducts(allProducts.filter(p =>
|
|
||||||
!selectedProducts.some(sp => sp.value === p.value)
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// Filter products based on search term
|
|
||||||
const filtered = allProducts.filter(product =>
|
|
||||||
(product.label?.toLowerCase().includes(searchTerm) ||
|
|
||||||
product.hs_code?.toLowerCase().includes(searchTerm)) &&
|
|
||||||
!selectedProducts.some(sp => sp.value === product.value)
|
|
||||||
);
|
|
||||||
setAvailableProducts(filtered);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error searching products:', error);
|
|
||||||
} finally {
|
|
||||||
setIsLoadingProducts(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loadAndFilterProducts();
|
// Get all products (including those not currently visible)
|
||||||
};
|
const allProducts = [...availableProducts, ...selectedProducts];
|
||||||
|
|
||||||
// Add product handler
|
// Filter products based on search term and exclude selected ones
|
||||||
const handleAddProduct = (product) => {
|
const filtered = allProducts.filter(product => {
|
||||||
setSelectedProducts(prev => {
|
const matchesSearch =
|
||||||
const updated = [...prev, product];
|
(product.label?.toLowerCase().includes(searchTerm) ||
|
||||||
return updated;
|
product.hs_code?.toLowerCase().includes(searchTerm) ||
|
||||||
|
(product.product_name && product.product_name.toLowerCase().includes(searchTerm)));
|
||||||
|
|
||||||
|
const isSelected = selectedProducts.some(selected =>
|
||||||
|
(selected.value && (selected.value === product.value || selected.value === product.id || selected.value === product.product_id)) ||
|
||||||
|
(selected.id && (selected.id === product.id || selected.id === product.value || selected.id === product.product_id)) ||
|
||||||
|
(selected.product_id && (selected.product_id === product.product_id || selected.product_id === product.id || selected.product_id === product.value))
|
||||||
|
);
|
||||||
|
|
||||||
|
return matchesSearch && !isSelected;
|
||||||
|
});
|
||||||
|
|
||||||
|
setAvailableProducts(filtered);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle adding a product
|
||||||
|
const handleAddProduct = (product) => {
|
||||||
|
setSelectedProducts(prev => {
|
||||||
|
// Check if product is already selected
|
||||||
|
const isAlreadySelected = prev.some(p =>
|
||||||
|
(p.value && (p.value === product.value || p.value === product.id || p.value === product.product_id)) ||
|
||||||
|
(p.id && (p.id === product.id || p.id === product.value || p.id === product.product_id)) ||
|
||||||
|
(p.product_id && (p.product_id === product.product_id || p.product_id === product.id || p.product_id === product.value))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isAlreadySelected) {
|
||||||
|
return prev; // Don't add duplicate
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...prev, product];
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mark this product as newly added
|
|
||||||
setNewlyAddedProductIds(prev => new Set([...prev, product.value]));
|
|
||||||
|
|
||||||
// Remove from available products
|
// Remove from available products
|
||||||
setAvailableProducts(prev => prev.filter(p => p.value !== product.value));
|
setAvailableProducts(prev =>
|
||||||
|
prev.filter(p =>
|
||||||
// Clear product error when a product is added
|
!(p.value && (p.value === product.value || p.value === product.id || p.value === product.product_id)) &&
|
||||||
setProductError("");
|
!(p.id && (p.id === product.id || p.id === product.value || p.id === product.product_id)) &&
|
||||||
|
!(p.product_id && (p.product_id === product.product_id || p.product_id === product.id || p.product_id === product.value))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add to newly added products
|
||||||
|
setNewlyAddedProductIds(prev =>
|
||||||
|
new Set([...prev, product.value || product.id || product.product_id])
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove product handler
|
// Remove product handler
|
||||||
const handleRemoveProduct = (product) => {
|
const handleRemoveProduct = (product) => {
|
||||||
// Only allow removing newly added products
|
// First, add the removed product back to available products if it matches the search
|
||||||
if (!newlyAddedProductIds.has(product.value)) {
|
const searchTerm = productSearchTerm.toLowerCase();
|
||||||
return;
|
const shouldAddToAvailable =
|
||||||
}
|
!productSearchTerm ||
|
||||||
setSelectedProducts(prev => prev.filter(p => p.value !== product.value));
|
(product.label?.toLowerCase().includes(searchTerm) ||
|
||||||
|
product.hs_code?.toLowerCase().includes(searchTerm) ||
|
||||||
// Add back to available products if not already there and matches search
|
(product.product_name && product.product_name.toLowerCase().includes(searchTerm)));
|
||||||
if (!productSearchTerm ||
|
|
||||||
(product.hs_code && product.hs_code.toLowerCase().includes(productSearchTerm)) ||
|
// Remove from selected products
|
||||||
(product.label && product.label.toLowerCase().includes(productSearchTerm))) {
|
setSelectedProducts(prev =>
|
||||||
|
prev.filter(p =>
|
||||||
|
!(p.value && (p.value === product.value || p.value === product.id || p.value === product.product_id)) &&
|
||||||
|
!(p.id && (p.id === product.id || p.id === product.value || p.id === product.product_id)) &&
|
||||||
|
!(p.product_id && (p.product_id === product.product_id || p.product_id === product.id || p.product_id === product.value))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add back to available products if it matches the current search
|
||||||
|
if (shouldAddToAvailable) {
|
||||||
setAvailableProducts(prev => {
|
setAvailableProducts(prev => {
|
||||||
if (!prev.some(p => p.value === product.value)) {
|
// Check if already in available products
|
||||||
return [...prev, product];
|
const alreadyExists = prev.some(p =>
|
||||||
|
(p.value && (p.value === product.value || p.value === product.id || p.value === product.product_id)) ||
|
||||||
|
(p.id && (p.id === product.id || p.id === product.value || p.id === product.product_id)) ||
|
||||||
|
(p.product_id && (p.product_id === product.product_id || p.product_id === product.id || p.product_id === product.value))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!alreadyExists) {
|
||||||
|
// Get all products (including the one being removed)
|
||||||
|
const allProducts = [...prev, ...selectedProducts];
|
||||||
|
const uniqueProducts = allProducts.filter((p, index, self) =>
|
||||||
|
index === self.findIndex(t =>
|
||||||
|
(t.value && (t.value === p.value || t.value === p.id || t.value === p.product_id)) ||
|
||||||
|
(t.id && (t.id === p.id || t.id === p.value || t.id === p.product_id)) ||
|
||||||
|
(t.product_id && (t.product_id === p.product_id || t.product_id === p.id || t.product_id === p.value))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return uniqueProducts;
|
||||||
}
|
}
|
||||||
return prev;
|
return prev;
|
||||||
});
|
});
|
||||||
@ -373,10 +407,7 @@ const EditCompanyProfile = () => {
|
|||||||
const loadEmirates = async () => {
|
const loadEmirates = async () => {
|
||||||
try {
|
try {
|
||||||
const emirates = await fetchEmirates({ signal: controller.signal });
|
const emirates = await fetchEmirates({ signal: controller.signal });
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
|
|
||||||
console.log('Loaded emirates:', emirates);
|
|
||||||
|
|
||||||
const lookup = {};
|
const lookup = {};
|
||||||
emirates.forEach((emirate) => {
|
emirates.forEach((emirate) => {
|
||||||
lookup[emirate.value] = emirate.label;
|
lookup[emirate.value] = emirate.label;
|
||||||
@ -399,31 +430,22 @@ const EditCompanyProfile = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Load establishment data and set form with API data
|
// Load establishment data and set form with API data
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('useEffect triggered, establishmentId:', establishmentId);
|
|
||||||
|
|
||||||
// Create an async function inside the effect
|
// Create an async function inside the effect
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
if (!establishmentId) {
|
if (!establishmentId) {
|
||||||
console.log('No establishmentId found, skipping fetch');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
console.log('Starting API call to fetch establishment details for ID:', establishmentId);
|
const response = await fetchEstablishmentDetail(establishmentId);
|
||||||
|
|
||||||
const response = await fetchEstablishmentDetail(establishmentId);
|
|
||||||
console.log('API Response received:', response);
|
|
||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
console.error('Empty response received');
|
console.error('Empty response received');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mappedProfile = mapApiEstablishmentToProfile(response);
|
const mappedProfile = mapApiEstablishmentToProfile(response);
|
||||||
console.log('Mapped Profile:', mappedProfile);
|
|
||||||
|
|
||||||
const formData = {
|
const formData = {
|
||||||
...createEmptyProfile(),
|
...createEmptyProfile(),
|
||||||
...mappedProfile,
|
...mappedProfile,
|
||||||
@ -431,21 +453,13 @@ const EditCompanyProfile = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const totals = computeEmploymentTotals(formData);
|
const totals = computeEmploymentTotals(formData);
|
||||||
Object.assign(formData, totals);
|
Object.assign(formData, totals);
|
||||||
|
|
||||||
console.log('Final Form Data:', formData);
|
|
||||||
|
|
||||||
// First set the form data
|
// First set the form data
|
||||||
setForm(formData);
|
setForm(formData);
|
||||||
initialSnapshotRef.current = formData;
|
initialSnapshotRef.current = formData;
|
||||||
|
|
||||||
// Then load cities and products in parallel
|
// Load products first
|
||||||
const [allProducts] = await Promise.all([
|
const allProducts = await fetchProducts();
|
||||||
loadProducts()
|
|
||||||
]);
|
|
||||||
|
|
||||||
console.log('All products loaded:', allProducts);
|
|
||||||
|
|
||||||
// Set selected products from API response
|
// Set selected products from API response
|
||||||
const establishmentProducts = response.data?.establishment_products || response.establishment_products || [];
|
const establishmentProducts = response.data?.establishment_products || response.establishment_products || [];
|
||||||
if (Array.isArray(establishmentProducts) && establishmentProducts.length > 0) {
|
if (Array.isArray(establishmentProducts) && establishmentProducts.length > 0) {
|
||||||
@ -454,11 +468,17 @@ const EditCompanyProfile = () => {
|
|||||||
const productId = productData.id || ep.product_id;
|
const productId = productData.id || ep.product_id;
|
||||||
|
|
||||||
// Find the product in the loaded products to get the proper format
|
// Find the product in the loaded products to get the proper format
|
||||||
const foundProduct = allProducts.find(p => p.value === String(productId));
|
const foundProduct = allProducts.find(p =>
|
||||||
|
p.value === String(productId) ||
|
||||||
|
p.id === productId ||
|
||||||
|
p.product_id === productId
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...(foundProduct || {
|
...(foundProduct || {
|
||||||
value: String(productId),
|
value: String(productId),
|
||||||
|
id: productId,
|
||||||
|
product_id: productId,
|
||||||
label: productData.product_name || productData.name || 'Unnamed Product',
|
label: productData.product_name || productData.name || 'Unnamed Product',
|
||||||
hs_code: productData.hs_code || '',
|
hs_code: productData.hs_code || '',
|
||||||
product_name: productData.product_name || productData.name || 'Unnamed Product',
|
product_name: productData.product_name || productData.name || 'Unnamed Product',
|
||||||
@ -471,6 +491,19 @@ const EditCompanyProfile = () => {
|
|||||||
setSelectedProducts(formattedSelectedProducts);
|
setSelectedProducts(formattedSelectedProducts);
|
||||||
// Initialize newly added products as empty since we're loading existing data
|
// Initialize newly added products as empty since we're loading existing data
|
||||||
setNewlyAddedProductIds(new Set());
|
setNewlyAddedProductIds(new Set());
|
||||||
|
|
||||||
|
// Now load available products with selected ones filtered out
|
||||||
|
const filteredAvailable = allProducts.filter(product => {
|
||||||
|
return !formattedSelectedProducts.some(selected =>
|
||||||
|
(selected.value && (selected.value === product.value || selected.value === product.id || selected.value === product.product_id)) ||
|
||||||
|
(selected.id && (selected.id === product.id || selected.id === product.value || selected.id === product.product_id)) ||
|
||||||
|
(selected.product_id && (selected.product_id === product.product_id || selected.product_id === product.id || selected.product_id === product.value))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
setAvailableProducts(filteredAvailable);
|
||||||
|
} else {
|
||||||
|
// If no selected products, just set all products as available
|
||||||
|
setAvailableProducts(allProducts);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -534,9 +567,6 @@ const EditCompanyProfile = () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
console.log("Submitted data:", form);
|
|
||||||
console.log("Selected products:", selectedProducts);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
@ -584,14 +614,9 @@ const EditCompanyProfile = () => {
|
|||||||
|
|
||||||
}))
|
}))
|
||||||
: [],
|
: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("Payload sent:", establishmentData);
|
|
||||||
|
|
||||||
if (establishmentId) {
|
if (establishmentId) {
|
||||||
console.log("Updating establishment with ID:", establishmentId);
|
|
||||||
await updateEstablishment(establishmentId, establishmentData);
|
await updateEstablishment(establishmentId, establishmentData);
|
||||||
console.log("Profile updated successfully!");
|
|
||||||
navigate('/survey');
|
navigate('/survey');
|
||||||
} else {
|
} else {
|
||||||
console.error("No establishment ID provided for update");
|
console.error("No establishment ID provided for update");
|
||||||
|
|||||||
@ -206,27 +206,49 @@ const ImportModal = ({ isOpen, onClose, onImport }) => {
|
|||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadSampleCSV = () => {
|
// const downloadSampleCSV = () => {
|
||||||
const sampleData = [
|
// const sampleData = [
|
||||||
['HS Code', 'Product Name', 'Unit'],
|
// ['HS Code', 'Product Name', 'Unit'],
|
||||||
['0101', 'Live Horses', 'kg'],
|
// ['0101', 'Live Horses', 'kg'],
|
||||||
['0102', 'Live Bovine Animals', 'kg'],
|
// ['0102', 'Live Bovine Animals', 'kg'],
|
||||||
];
|
// ];
|
||||||
|
|
||||||
const csvContent = sampleData.map(row =>
|
// const csvContent = sampleData.map(row =>
|
||||||
row.map(field => `"${field}"`).join(',')
|
// row.map(field => `"${field}"`).join(',')
|
||||||
).join('\n');
|
// ).join('\n');
|
||||||
|
|
||||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
// const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||||
const url = URL.createObjectURL(blob);
|
// const url = URL.createObjectURL(blob);
|
||||||
|
// const link = document.createElement('a');
|
||||||
|
// link.href = url;
|
||||||
|
// link.setAttribute('download', 'hs-codes-sample.csv');
|
||||||
|
// document.body.appendChild(link);
|
||||||
|
// link.click();
|
||||||
|
// document.body.removeChild(link);
|
||||||
|
// URL.revokeObjectURL(url);
|
||||||
|
// };
|
||||||
|
const downloadSampleCSV = async () => {
|
||||||
|
try {
|
||||||
|
const response = await productService.downloadSampleFile();
|
||||||
|
|
||||||
|
// Create a blob from the response
|
||||||
|
const blob = new Blob([response], { type: 'text/csv' });
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = url;
|
link.href = url;
|
||||||
link.setAttribute('download', 'hs-codes-sample.csv');
|
link.setAttribute('download', 'hs-codes-sample.csv');
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
};
|
|
||||||
|
// Show success message
|
||||||
|
showToast('Sample file downloaded successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading sample file:', error);
|
||||||
|
showToast('Failed to download sample file', 'error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
|
|
||||||
@ -689,22 +711,15 @@ const IsicHsCodes = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const productId = selected.id;
|
const productId = selected.id;
|
||||||
console.log('Fetching product with ID:', productId);
|
|
||||||
|
|
||||||
// Get fresh data from the server
|
// Get fresh data from the server
|
||||||
const response = await productService.getProductById(productId);
|
const response = await productService.getProductById(productId);
|
||||||
console.log('API Response:', response);
|
|
||||||
|
|
||||||
if (response && response.data) {
|
if (response && response.data) {
|
||||||
const productData = response.data; // The actual product data is in response.data
|
const productData = response.data; // The actual product data is in response.data
|
||||||
|
|
||||||
if (!productData) {
|
if (!productData) {
|
||||||
throw new Error('No product data received in response');
|
throw new Error('No product data received in response');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Product data for edit:', productData);
|
|
||||||
|
|
||||||
// Map the API response fields to form fields
|
// Map the API response fields to form fields
|
||||||
const formData = {
|
const formData = {
|
||||||
id: productData.id,
|
id: productData.id,
|
||||||
@ -714,10 +729,6 @@ const IsicHsCodes = () => {
|
|||||||
unit: productData.unit_id ? String(productData.unit_id) : '',
|
unit: productData.unit_id ? String(productData.unit_id) : '',
|
||||||
status: productData.is_active ? 'Active' : 'Inactive',
|
status: productData.is_active ? 'Active' : 'Inactive',
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('Mapped form data:', formData);
|
|
||||||
|
|
||||||
console.log('Setting form data:', formData); // Debug log
|
|
||||||
setForm(formData);
|
setForm(formData);
|
||||||
setEditingRow(index);
|
setEditingRow(index);
|
||||||
setModalMode('edit');
|
setModalMode('edit');
|
||||||
@ -991,9 +1002,7 @@ const IsicHsCodes = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Get the existing product data to preserve fields
|
// Get the existing product data to preserve fields
|
||||||
const existingProduct = editingRow !== null ? rowsData[editingRow] : null;
|
const existingProduct = editingRow !== null ? rowsData[editingRow] : null;
|
||||||
console.log("existingProduct",existingProduct)
|
|
||||||
|
|
||||||
// Only update the specific fields we want to change
|
// Only update the specific fields we want to change
|
||||||
const response = await productService.updateProduct(productId, productData);
|
const response = await productService.updateProduct(productId, productData);
|
||||||
const selectedUnit = unitOptions.find(u => u.value === form.unit);
|
const selectedUnit = unitOptions.find(u => u.value === form.unit);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect, useMemo, useRef } from 'react';
|
import React, { useState, useEffect, useMemo, useRef } from 'react';
|
||||||
import Table from '@/components/common/Table';
|
import Table from '@/components/common/Table';
|
||||||
import { TextField } from '@/components/common/FormControls';
|
import { TextField } from '@/components/common/FormControls';
|
||||||
import { getUnits, getUnitById, createUnit, updateUnit, deleteUnit, uploadUnitCSV } from '@/services/configuration/unitService';
|
import { getUnits, getUnitById, createUnit, updateUnit, deleteUnit, uploadUnitCSV,downloadSampleFile } from '@/services/configuration/unitService';
|
||||||
|
|
||||||
const downloadIconSrc = '/assets/images/DownloadSimple.svg';
|
const downloadIconSrc = '/assets/images/DownloadSimple.svg';
|
||||||
const addIconSrc = '/assets/images/ic_baseline-plus.svg';
|
const addIconSrc = '/assets/images/ic_baseline-plus.svg';
|
||||||
@ -529,9 +529,6 @@ const UnitMaster = () => {
|
|||||||
|
|
||||||
if (error.response?.data) {
|
if (error.response?.data) {
|
||||||
const errorData = error.response.data;
|
const errorData = error.response.data;
|
||||||
|
|
||||||
console.log('Error response data:', errorData);
|
|
||||||
|
|
||||||
// Handle different error response formats
|
// Handle different error response formats
|
||||||
if (typeof errorData === 'string') {
|
if (typeof errorData === 'string') {
|
||||||
errorMessage = errorData;
|
errorMessage = errorData;
|
||||||
@ -697,30 +694,57 @@ const UnitMaster = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadSampleCSV = () => {
|
// const downloadSampleCSV = () => {
|
||||||
const sampleData = [
|
// const sampleData = [
|
||||||
['Unit Name', 'Description'],
|
// ['Unit Name', 'Description'],
|
||||||
['Kilogram', 'Weight measurement in kilograms'],
|
// ['Kilogram', 'Weight measurement in kilograms'],
|
||||||
['Gram', 'Weight measurement in grams'],
|
// ['Gram', 'Weight measurement in grams'],
|
||||||
['Liter', 'Volume measurement in liters'],
|
// ['Liter', 'Volume measurement in liters'],
|
||||||
['Meter', 'Length measurement in meters']
|
// ['Meter', 'Length measurement in meters']
|
||||||
];
|
// ];
|
||||||
|
|
||||||
const csvContent = sampleData.map(row =>
|
// const csvContent = sampleData.map(row =>
|
||||||
row.map(field => `"${field}"`).join(',')
|
// row.map(field => `"${field}"`).join(',')
|
||||||
).join('\n');
|
// ).join('\n');
|
||||||
|
|
||||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
// const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||||
const url = URL.createObjectURL(blob);
|
// const url = URL.createObjectURL(blob);
|
||||||
|
// const link = document.createElement('a');
|
||||||
|
// link.href = url;
|
||||||
|
// link.setAttribute('download', 'unit-master-template.csv');
|
||||||
|
// document.body.appendChild(link);
|
||||||
|
// link.click();
|
||||||
|
// document.body.removeChild(link);
|
||||||
|
// URL.revokeObjectURL(url);
|
||||||
|
// };
|
||||||
|
const downloadSampleCSV = async () => {
|
||||||
|
try {
|
||||||
|
const response = await downloadSampleFile();
|
||||||
|
|
||||||
|
// Create a blob from the response
|
||||||
|
const blob = new Blob([response], { type: 'text/csv;charset=utf-8;' });
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = url;
|
link.href = url;
|
||||||
link.setAttribute('download', 'unit-master-template.csv');
|
link.setAttribute('download', 'unit-master-sample.csv');
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
};
|
|
||||||
|
// Show success message
|
||||||
|
setToastData({
|
||||||
|
message: 'Sample file downloaded successfully',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading sample file:', error);
|
||||||
|
setToastData({
|
||||||
|
message: 'Failed to download sample file',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
const handleImport = async () => {
|
const handleImport = async () => {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
setImportError('Please select a CSV file to import.');
|
setImportError('Please select a CSV file to import.');
|
||||||
|
|||||||
@ -94,7 +94,23 @@ uploadCSV: async (file) => {
|
|||||||
console.error('Error in productService.uploadCSV:', error);
|
console.error('Error in productService.uploadCSV:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// Download sample CSV file
|
||||||
|
downloadSampleFile: async () => {
|
||||||
|
try {
|
||||||
|
const response = await getRequest('download-sample-product-upload-file', {
|
||||||
|
responseType: 'blob',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'text/csv'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in productService.downloadSampleFile:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default productService;
|
export default productService;
|
||||||
|
|||||||
@ -54,7 +54,6 @@ export const updateUnit = async (id, unitData) => {
|
|||||||
export const deleteUnit = async (id) => {
|
export const deleteUnit = async (id) => {
|
||||||
try {
|
try {
|
||||||
const response = await deleteRequest(`${UNIT_MASTER_ENDPOINT}/${id}`);
|
const response = await deleteRequest(`${UNIT_MASTER_ENDPOINT}/${id}`);
|
||||||
console.log('Unit deleted successfully:', response.data);
|
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting unit:', error);
|
console.error('Error deleting unit:', error);
|
||||||
@ -75,4 +74,23 @@ export const uploadUnitCSV = async (formData) => {
|
|||||||
console.error('Error uploading unit CSV:', error);
|
console.error('Error uploading unit CSV:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// In unitService.js, add this function to the exports
|
||||||
|
export const downloadSampleFile = async () => {
|
||||||
|
try {
|
||||||
|
const response = await getRequest('unit_master_download_sample_file', {
|
||||||
|
responseType: 'blob',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'text/csv'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading sample file:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
@ -3,6 +3,7 @@ import resolveEstablishmentId from '@/services/utils/establishment';
|
|||||||
|
|
||||||
const dashboardEndpoint = '/establishment_dashboard';
|
const dashboardEndpoint = '/establishment_dashboard';
|
||||||
const establishmentsEndpoint = '/establishments';
|
const establishmentsEndpoint = '/establishments';
|
||||||
|
const downloadsample = '/establishment';
|
||||||
|
|
||||||
const buildEstablishmentQueryParams = (params = {}) => {
|
const buildEstablishmentQueryParams = (params = {}) => {
|
||||||
const {
|
const {
|
||||||
@ -109,7 +110,6 @@ export const uploadCompanyProfileCSVAlternative = async (formData, config = {})
|
|||||||
const altFormData = new FormData();
|
const altFormData = new FormData();
|
||||||
altFormData.append('csv_file', file); // Try 'csv_file' parameter
|
altFormData.append('csv_file', file); // Try 'csv_file' parameter
|
||||||
|
|
||||||
console.log('Alternative FormData entries:');
|
|
||||||
for (let pair of altFormData.entries()) {
|
for (let pair of altFormData.entries()) {
|
||||||
console.log(pair[0] + ': ', pair[1]);
|
console.log(pair[0] + ': ', pair[1]);
|
||||||
}
|
}
|
||||||
@ -154,6 +154,16 @@ export const bulkDeleteEstablishments = async (establishmentIds, config = {}) =>
|
|||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Download sample CSV file
|
||||||
|
export const downloadSampleFile = async (config = {}) => {
|
||||||
|
const downloadEndpoint = `${downloadsample}/download-sample-file`;
|
||||||
|
const response = await getRequest(downloadEndpoint, {
|
||||||
|
...config,
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
fetchEstablishments,
|
fetchEstablishments,
|
||||||
fetchEstablishmentDashboard,
|
fetchEstablishmentDashboard,
|
||||||
@ -165,4 +175,5 @@ export default {
|
|||||||
uploadCompanyProfileCSVAlternative,
|
uploadCompanyProfileCSVAlternative,
|
||||||
bulkUpdateEstablishments,
|
bulkUpdateEstablishments,
|
||||||
bulkDeleteEstablishments,
|
bulkDeleteEstablishments,
|
||||||
|
downloadSampleFile,
|
||||||
};
|
};
|
||||||
Loading…
Reference in New Issue
Block a user