diff --git a/ipi-survey-platform/src/components/admin/SubmissionTable.jsx b/ipi-survey-platform/src/components/admin/SubmissionTable.jsx index 86c4bbe..78259e1 100644 --- a/ipi-survey-platform/src/components/admin/SubmissionTable.jsx +++ b/ipi-survey-platform/src/components/admin/SubmissionTable.jsx @@ -12,25 +12,36 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => { const navigate = useNavigate(); - useEffect(() => { - const fetchDashboardData = async () => { - try { - setLoading(true); - const response = await apiClient.get('/admin_dashboard'); - const result = response?.data; - if (result?.status === 'success') { - setData(result.recent_submissions || []); - } else { - console.error('Error: Invalid response', result); - } - } catch (error) { - console.error('Error fetching recent submissions:', error); - } finally { - setLoading(false); + const fetchDashboardData = async (quarter, year) => { + try { + setLoading(true); + const params = new URLSearchParams(); + + if (quarter && quarter !== 'All') { + params.append('quarter', quarter); } - }; - fetchDashboardData(); - }, []); + + if (year && year !== 'All') { + params.append('year', year); + } + + const response = await apiClient.get(`/admin_dashboard?${params.toString()}`); + const result = response?.data; + if (result?.status === 'success') { + setData(result.recent_submissions || []); + } else { + console.error('Error: Invalid response', result); + } + } catch (error) { + console.error('Error fetching recent submissions:', error); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchDashboardData(selectedQuarter, selectedYear); + }, [selectedQuarter, selectedYear]); const filtered = useMemo(() => { let list = [...data]; diff --git a/ipi-survey-platform/src/components/dashboard/SubmissionHistory.jsx b/ipi-survey-platform/src/components/dashboard/SubmissionHistory.jsx index cf8abf1..b6b55b6 100644 --- a/ipi-survey-platform/src/components/dashboard/SubmissionHistory.jsx +++ b/ipi-survey-platform/src/components/dashboard/SubmissionHistory.jsx @@ -32,7 +32,7 @@ const getStatusBadge = (status) => { ); } else if (normalized === 'submitted') { return ( - + Submitted ); @@ -152,7 +152,7 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' }) {item.submission_on}, productsLabel, + document.body.removeChild(link); + URL.revokeObjectURL(url); + }} + className={`h-9 px-3 rounded-md border text-sm inline-flex items-center gap-2 ${ + users.length + ? 'bg-[#F7F7F7] border-[#C3C6CB] text-[#232528]' + : 'bg-[#F3F4F6] border-[#E5E7EB] text-[#9CA3AF] cursor-not-allowed' + }`} + disabled={!users.length} + > + Export + Export CSV + - - - + type="button" + className="h-8 w-8 grid place-items-center rounded hover:bg-gray-50 cursor-pointer" + title="Edit" + onClick={() => handleEdit(user)} + > + Edit + - - {isResetModalOpen && ( -
-
- {/* Close Button */} - - - {/* Header */} -

Reset Password

- - {/* Input Fields */} -
-
- - setNewPassword(e.target.value)} - /> -
- -
- - setConfirmPassword(e.target.value)} - /> -
-
- - {/* Buttons */} -
- - -
-
-
- -)} -{/* Edit Modal */} -{isEditModalOpen && ( - setIsEditModalOpen(false)} - onUpdate={handleUpdateUser} - /> -)} - -{/* Delete Modal */} -{showDeleteModal && ( - setShowDeleteModal(false)} - onDelete={handleConfirmDelete} - /> -)} - - + type="button" + className="h-8 w-8 grid place-items-center rounded hover:bg-gray-50 cursor-pointer" + title="Delete" + onClick={() => handleDelete(rowIndex)} + > + Delete + + ); } @@ -661,6 +669,39 @@ const handleCloseResetModal = () => { /> )} + {/* Edit Modal */} + {isEditModalOpen && ( + setIsEditModalOpen(false)} + onUpdate={handleUpdateUser} + /> + )} + + {/* Delete Modal */} + {showDeleteModal && ( + setShowDeleteModal(false)} + onDelete={handleConfirmDelete} + /> + )} + + {/* Reset Password Modal */} + + {/* Toast */} {toastData && (
@@ -677,4 +718,4 @@ const handleCloseResetModal = () => { ); }; -export default AdminUsers; +export default AdminUsers; \ No newline at end of file diff --git a/ipi-survey-platform/src/pages/Admin/configuration/AdminUsers/ResetPasswordModal.jsx b/ipi-survey-platform/src/pages/Admin/configuration/AdminUsers/ResetPasswordModal.jsx new file mode 100644 index 0000000..5f92582 --- /dev/null +++ b/ipi-survey-platform/src/pages/Admin/configuration/AdminUsers/ResetPasswordModal.jsx @@ -0,0 +1,182 @@ +import React, { useState } from 'react'; + +const ResetPasswordModal = ({ + isOpen, + onClose, + newPassword, + setNewPassword, + confirmPassword, + setConfirmPassword, + onReset, + loading = false +}) => { + const [errors, setErrors] = useState({}); + + const validateForm = () => { + const newErrors = {}; + + if (!newPassword.trim()) { + newErrors.newPassword = 'New password is required'; + } else if (newPassword.length < 6) { + newErrors.newPassword = 'Password must be at least 6 characters'; + } + + if (!confirmPassword.trim()) { + newErrors.confirmPassword = 'Please confirm your password'; + } + // Removed "Passwords do not match" error display + + return newErrors; + }; + + const handleSubmit = () => { + const formErrors = validateForm(); + + // Check if passwords match (without showing error) + if (newPassword !== confirmPassword) { + return; // Just return without showing error + } + + if (Object.keys(formErrors).length > 0) { + setErrors(formErrors); + return; + } + + setErrors({}); + onReset(); + }; + + const handleInputChange = (field, value) => { + if (field === 'newPassword') setNewPassword(value); + if (field === 'confirmPassword') setConfirmPassword(value); + + // Clear error when user starts typing + if (errors[field]) { + setErrors(prev => ({ ...prev, [field]: '' })); + } + }; + + const handleKeyPress = (e) => { + if (e.key === 'Enter' && !loading) { + handleSubmit(); + } + }; + + if (!isOpen) return null; + + return ( +
+
+ {/* Header */} +
+

Reset Password

+ +
+ + {/* Form */} +
+
+ + handleInputChange('newPassword', e.target.value)} + onKeyPress={handleKeyPress} + disabled={loading} + /> + {errors.newPassword && ( +

+ + + + {errors.newPassword} +

+ )} +
+ +
+ + handleInputChange('confirmPassword', e.target.value)} + onKeyPress={handleKeyPress} + disabled={loading} + /> + {errors.confirmPassword && ( +

+ + + + {errors.confirmPassword} +

+ )} +
+
+ + {/* Note Section */} +
+
+ + + +

+ Note: Password must be at least 6 characters long. +

+
+
+ + {/* Action Buttons */} +
+ + +
+
+
+ ); +}; + +export default ResetPasswordModal; \ No newline at end of file diff --git a/ipi-survey-platform/src/pages/Admin/configuration/IsicHsCodes.jsx b/ipi-survey-platform/src/pages/Admin/configuration/IsicHsCodes.jsx index 2e831da..6d532f5 100644 --- a/ipi-survey-platform/src/pages/Admin/configuration/IsicHsCodes.jsx +++ b/ipi-survey-platform/src/pages/Admin/configuration/IsicHsCodes.jsx @@ -28,7 +28,7 @@ const createEmptyCodeForm = () => ({ }); // Toast notification component -const Toast = ({ message, onClose }) => { +const Toast = ({ message, type = 'success', onClose }) => { React.useEffect(() => { const timer = setTimeout(() => { onClose(); @@ -36,15 +36,267 @@ const Toast = ({ message, onClose }) => { return () => clearTimeout(timer); }, [onClose]); + const getToastStyles = () => { + switch (type) { + case 'success': + return 'bg-[#F3FAF4] text-[#3F8E50] border border-[#3F8E50]'; + case 'error': + return 'bg-red-50 text-red-800 border border-red-200'; + case 'warning': + return 'bg-yellow-50 text-yellow-800 border border-yellow-200'; + default: + return 'bg-blue-50 text-blue-800 border border-blue-200'; + } + }; + return (
-
+
{message}
); }; +// Import Modal Component +const ImportModal = ({ isOpen, onClose, onImport }) => { + const [file, setFile] = React.useState(null); + const [dragActive, setDragActive] = React.useState(false); + const [loading, setLoading] = React.useState(false); + const [error, setError] = React.useState(''); + + const fileInputRef = React.useRef(null); + + const handleDrag = (e) => { + e.preventDefault(); + e.stopPropagation(); + if (e.type === "dragenter" || e.type === "dragover") { + setDragActive(true); + } else if (e.type === "dragleave") { + setDragActive(false); + } + }; + + const handleDrop = (e) => { + e.preventDefault(); + e.stopPropagation(); + setDragActive(false); + + if (e.dataTransfer.files && e.dataTransfer.files[0]) { + const droppedFile = e.dataTransfer.files[0]; + validateAndSetFile(droppedFile); + } + }; + + const handleFileChange = (e) => { + if (e.target.files && e.target.files[0]) { + const selectedFile = e.target.files[0]; + validateAndSetFile(selectedFile); + } + }; + + const validateAndSetFile = (file) => { + setError(''); + + // Check file type + if (!file.name.toLowerCase().endsWith('.csv')) { + setError('Only .csv files are allowed.'); + return; + } + + // Check file size (10MB limit) + const maxSize = 10 * 1024 * 1024; // 10MB in bytes + if (file.size > maxSize) { + setError('File size exceeds limit (10MB)'); + return; + } + + setFile(file); + }; + + const handleImport = async () => { + if (!file) { + setError('Please select a file to import'); + return; + } + + try { + setLoading(true); + setError(''); + + // Simulate file upload - replace with actual API call + await new Promise(resolve => setTimeout(resolve, 2000)); + + // Call the onImport callback with the file + await onImport(file); + + // Reset and close on success + setFile(null); + onClose(); + } catch (err) { + setError('Failed to import file. Please try again.'); + } finally { + setLoading(false); + } + }; + + const handleClose = () => { + setFile(null); + setError(''); + setDragActive(false); + onClose(); + }; + + const downloadSampleCSV = () => { + const sampleData = [ + ['HS Code', 'Product Name', 'Unit', 'Description', 'Status'], + ['0101', 'Live Horses', 'kg', 'Live pure-bred breeding horses', 'Active'], + ['0102', 'Live Bovine Animals', 'kg', 'Live bovine animals', 'Active'], + ['0103', 'Live Swine', 'kg', 'Live swine', 'Active'], + ['0104', 'Live Sheep And Goats', 'kg', 'Live sheep and goats', 'Active'], + ['0105', 'Live Poultry', 'kg', 'Live poultry', 'Active'] + ]; + + const csvContent = sampleData.map(row => + row.map(field => `"${field}"`).join(',') + ).join('\n'); + + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + 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); + }; + + if (!isOpen) return null; + + return ( +
+
+
+ {/* Header */} +
+

Import HS Codes

+ +
+ + {/* Content */} +
+ {/* File Upload Area */} +
fileInputRef.current?.click()} + > +
+
+ Upload +
+
+

+ {file ? file.name : 'Drop your CSV file here or browse'} +

+

+ Supports .csv files only (Max 10MB) +

+
+ +
+ +
+ + {/* Error Message */} + {error && ( +
+

+ + + + {error} +

+
+ )} + + {/* Sample CSV Link */} +
+ +
+
+ + {/* Footer */} +
+ + +
+
+
+ ); +}; + const IsicHsCodes = () => { const [rowsData, setRowsData] = React.useState([]); const [filteredData, setFilteredData] = React.useState([]); @@ -59,7 +311,8 @@ const IsicHsCodes = () => { const [modalMode, setModalMode] = React.useState(null); const [form, setForm] = React.useState(createEmptyCodeForm()); const [unitOptions, setUnitOptions] = React.useState([]); - const [toast, setToast] = React.useState({ show: false, message: '' }); + const [toast, setToast] = React.useState({ show: false, message: '', type: 'success' }); + const [showImportModal, setShowImportModal] = React.useState(false); // Get user profile from session const userProfile = React.useMemo(() => { @@ -127,39 +380,31 @@ const IsicHsCodes = () => { if (products.length > 0) { const formattedData = await Promise.all(products.map(async (product) => { - // Try to get creator's name if available - let createdByName = '-'; - const createdById = product.created_by; + let createdByName = '-'; + const createdById = product.created_by; + try { + const userProfileStr = sessionStorage.getItem('user_profile'); + if (userProfileStr) { + const userProfile = JSON.parse(userProfileStr); + if (createdById && createdById === userProfile.id) { + createdByName = userProfile.name || '-'; + } + } + } catch (error) { + console.error('Error getting user from session:', error); + } - - - // If still not available, try to fetch the creator's name by ID - // if (!createdByName && product.created_by) { - try { - const userProfileStr = sessionStorage.getItem('user_profile'); - if (userProfileStr) { - const userProfile = JSON.parse(userProfileStr); - // If the current user is the creator, use their name - if (createdById && createdById === userProfile.id) { - createdByName = userProfile.name || '-'; - } - } - } catch (error) { - console.error('Error getting user from session:', error); - } + const createdDate = product.created_at + ? new Date(product.created_at).toLocaleDateString('en-GB') + : '-'; + + const updatedDate = product.updated_at + ? new Date(product.updated_at).toLocaleDateString('en-GB') + : createdDate; - - const createdDate = product.created_at - ? new Date(product.created_at).toLocaleDateString('en-GB') - : '-'; - - const updatedDate = product.updated_at - ? new Date(product.updated_at).toLocaleDateString('en-GB') - : createdDate; // Fallback to created date if updated_at is not available if (product.created_by) { try { - // If the API returns the creator's name in the product data, use it createdByName = product.created_by_user?.name || '-'; } catch (error) { console.error('Error fetching creator info:', error); @@ -175,12 +420,11 @@ const IsicHsCodes = () => { createdBy: createdByName, createdOn: createdDate || null, updated: updatedDate, - createdById: createdById, // Store the ID for reference + createdById: createdById, status: product.is_active ? 'Active' : 'Inactive', description: product.hs_description || '', _createdAt: product.created_at, _updatedAt: product.updated_at - }; })); setRowsData(formattedData); @@ -229,33 +473,32 @@ const IsicHsCodes = () => { }, [searchTerm, rowsData]); const rows = filteredData.map((item) => { - // If we only have the ID and it matches the current user, use the current user's name - let displayName = item.createdBy; - if (displayName === '-' && item.createdById) { - try { - const userProfileStr = sessionStorage.getItem('user_profile'); - if (userProfileStr) { - const userProfile = JSON.parse(userProfileStr); - if (item.createdById === userProfile.id) { - displayName = userProfile.name || '-'; + let displayName = item.createdBy; + if (displayName === '-' && item.createdById) { + try { + const userProfileStr = sessionStorage.getItem('user_profile'); + if (userProfileStr) { + const userProfile = JSON.parse(userProfileStr); + if (item.createdById === userProfile.id) { + displayName = userProfile.name || '-'; + } } + } catch (error) { + console.error('Error getting user from session:', error); } - } catch (error) { - console.error('Error getting user from session:', error); } - } - return [ - item.code, - item.product, - item.unit, - item.estimatedMapped, - displayName, // Use the resolved display name - item.updated || item.createdOn || '-', - item.status, - 'actions', - ]; -}); + return [ + item.code, + item.product, + item.unit, + item.estimatedMapped, + displayName, + item.updated || item.createdOn || '-', + item.status, + 'actions', + ]; + }); const handleView = async (index) => { const selected = rowsData[index]; @@ -266,13 +509,11 @@ const IsicHsCodes = () => { const productId = selected.id; console.log(`Fetching product details for ID: ${productId}`); - // Make API call to get product details const response = await productService.getProductById(productId); console.log('Product details response:', response); if (response && response.data) { const product = response.data; - // Map API response to form fields setForm({ code: product.hs_code || '', product: product.product_name || '', @@ -286,11 +527,11 @@ const IsicHsCodes = () => { setModalMode('view'); } else { console.error('Invalid product data received from API'); - showToast('Failed to load product details'); + showToast('Failed to load product details', 'error'); } } catch (error) { console.error('Error fetching product details:', error); - showToast('Error loading product details'); + showToast('Error loading product details', 'error'); } finally { setIsLoading(false); } @@ -305,13 +546,11 @@ const IsicHsCodes = () => { const productId = selected.id; console.log(`Fetching product details for editing ID: ${productId}`); - // Fetch the latest product data by ID const response = await productService.getProductById(productId); console.log('Product details for edit:', response); if (response && response.data) { const product = response.data; - // Map API response to form fields setForm({ code: product.hs_code || '', product: product.product_name || '', @@ -323,21 +562,19 @@ const IsicHsCodes = () => { setModalMode('edit'); } else { console.error('Invalid product data received from API'); - showToast('Failed to load product details for editing'); + showToast('Failed to load product details for editing', 'error'); } } catch (error) { console.error('Error fetching product details for edit:', error); - showToast('Error loading product details for editing'); + showToast('Error loading product details for editing', 'error'); } finally { setIsLoading(false); } }; - // Store the row index when delete is clicked const [deletingRowIndex, setDeletingRowIndex] = React.useState(null); const handleDelete = async (paginationIndex) => { - // Convert pagination index to actual data index const dataIndex = (currentPage - 1) * pageSize + paginationIndex; console.log('Delete button clicked, paginationIndex:', paginationIndex, 'dataIndex:', dataIndex); @@ -352,14 +589,12 @@ const IsicHsCodes = () => { return; } - // Store the row index for later use setDeletingRowIndex(dataIndex); try { setIsLoading(true); console.log('Fetching product details before deletion, ID:', productId); - // Fetch the latest product data by ID const response = await productService.getProductById(productId); console.log('Product details from API:', response); @@ -367,7 +602,6 @@ const IsicHsCodes = () => { throw new Error('Invalid product data received'); } - // Update the row data with the latest from the server const updatedRowsData = [...rowsData]; updatedRowsData[dataIndex] = { ...updatedRowsData[dataIndex], @@ -419,39 +653,24 @@ const IsicHsCodes = () => { } console.log('Deleting product:', { productId, product }); - console.log('Product ID to delete:', productId); - - if (!productId) { - const errorMsg = 'Product ID not found for deletion'; - console.error(errorMsg); - throw new Error(errorMsg); - } - - // Call delete API - console.log(`Deleting product with ID: ${productId}`); try { await productService.deleteProduct(productId); - // If we get here, the delete was successful (204 No Content) - // Remove from local state const newData = [...rowsData]; - newData.splice(deletingRow, 1); + newData.splice(deletingRowIndex, 1); setRowsData(newData); - showToast('Product deleted successfully!'); + showToast('Product deleted successfully!', 'success'); } catch (apiError) { console.error('API Error:', apiError); - // If we get a 204, it's actually a success (No Content) if (apiError.response && apiError.response.status === 204) { - // Remove from local state const newData = [...rowsData]; - newData.splice(deletingRow, 1); + newData.splice(deletingRowIndex, 1); setRowsData(newData); - showToast('Product deleted successfully!'); + showToast('Product deleted successfully!', 'success'); } else { - // Handle other errors const errorMessage = apiError.response?.data?.message || 'Failed to delete product. Please try again.'; setError(errorMessage); showToast(errorMessage, 'error'); @@ -465,12 +684,10 @@ const IsicHsCodes = () => { } finally { setIsLoading(false); setShowDeleteConfirm(false); - setDeletingRow(null); + setDeletingRowIndex(null); } }; - // Unit options are now fetched from the API and will be used in the dropdown - const statusOptions = [ { label: 'Active', value: 'Active' }, { label: 'Inactive', value: 'Inactive' }, @@ -488,8 +705,8 @@ const IsicHsCodes = () => { setForm(createEmptyCodeForm()); }; - const showToast = (message) => { - setToast({ show: true, message }); + const showToast = (message, type = 'success') => { + setToast({ show: true, message, type }); setTimeout(() => { setToast(prev => ({ ...prev, show: false })); }, 3000); @@ -500,9 +717,55 @@ const IsicHsCodes = () => { setForm((prev) => ({ ...prev, [field]: value })); }; - const getToday = () => { - const now = new Date(); - return now.toLocaleDateString('en-GB'); + const handleImportCSV = async (file) => { + try { + // Simulate CSV processing - replace with actual API call + console.log('Importing file:', file); + + // Simulate successful import + await new Promise(resolve => setTimeout(resolve, 1500)); + + // Show success message + showToast('HS Codes imported successfully! New codes appear in the list.', 'success'); + + // In a real implementation, you would: + // 1. Call your import API endpoint + // 2. Refresh the data from the server + // 3. Update the rowsData state with the new data + + } catch (error) { + showToast('Failed to import CSV file. Please try again.', 'error'); + } + }; + + const handleExportCSV = () => { + const csvHeader = headers.slice(0, headers.length - 1).join(','); + const csvRows = rowsData.map((item) => { + return [ + item.code, + item.product, + item.unit, + item.estimatedMapped, + item.createdBy, + item.updated || item.createdOn || '-', + item.status, + ] + .map((value) => `"${String(value ?? '').replace(/"/g, '""')}"`) + .join(','); + }); + + const csvContent = csvHeader + '\n' + csvRows.join('\n'); + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', 'hs-codes-export.csv'); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + + showToast('CSV exported successfully!', 'success'); }; const handleSaveForm = async () => { @@ -517,22 +780,20 @@ const IsicHsCodes = () => { try { setIsLoading(true); - setError(null); // Clear previous errors + setError(null); - // Always include these fields for both create and update const productData = { product_name: form.product, is_active: form.status === 'Active', hs_code: form.code, unit_id: form.unit ? parseInt(form.unit) : null, - created_by: userProfile?.id || null, // Add created_by with user ID + created_by: userProfile?.id || null, ...(form.description && { hs_description: form.description }) }; console.log('Saving product data:', JSON.stringify(productData, null, 2)); if (modalMode === 'edit' && editingRow !== null) { - // Update existing product const productId = rowsData[editingRow]?.id; if (!productId) { throw new Error('Product ID not found for editing'); @@ -541,21 +802,12 @@ const IsicHsCodes = () => { console.log(`Updating product with ID: ${productId}`); try { - console.log('Sending update request with data:', { - productId, - productData, - formUnit: form.unit, - unitOptions: unitOptions - }); - const response = await productService.updateProduct(productId, productData); console.log('Update response:', response); - // Find the selected unit from unitOptions to get the uom const selectedUnit = unitOptions.find(u => u.value === form.unit); console.log('Selected unit for update:', selectedUnit); - // Create the updated product object const updatedProduct = { ...rowsData[editingRow], code: form.code, @@ -567,14 +819,12 @@ const IsicHsCodes = () => { description: form.description || '' }; - // Update the product in the list while maintaining the same position setRowsData(prev => { const updated = [...prev]; updated[editingRow] = updatedProduct; return updated; }); - // Also update filteredData if needed setFilteredData(prev => { const updated = [...prev]; const index = updated.findIndex(item => item.id === updatedProduct.id); @@ -584,7 +834,7 @@ const IsicHsCodes = () => { return updated; }); - showToast('Product updated successfully!'); + showToast('Product updated successfully!', 'success'); closeModal(); return; } catch (updateError) { @@ -598,43 +848,34 @@ const IsicHsCodes = () => { } } - // If we get here, it's a create operation console.log('Creating new product...'); try { const response = await productService.createProduct(productData); console.log('Create response:', response); - // Find the selected unit from unitOptions to get the uom const selectedUnit = unitOptions.find(u => u.value === form.unit); const newProduct = { - id: response.data?.id || Date.now(), // Fallback to timestamp if no ID in response + id: response.data?.id || Date.now(), code: form.code, product: form.product, unit: selectedUnit ? selectedUnit.label : 'N/A', unit_id: form.unit ? parseInt(form.unit) : null, estimatedMapped: 0, - createdBy: userProfile?.name || 'System', // Use user's name or fallback to 'System' - createdById: userProfile?.id || null, // Store user ID for reference + createdBy: userProfile?.name || 'System', + createdById: userProfile?.id || null, updated: new Date().toLocaleDateString('en-GB'), status: form.status, description: form.description || '' }; - // Add new product to the beginning of the array to show it at the top setRowsData(prev => [newProduct, ...prev]); setFilteredData(prev => [newProduct, ...prev]); console.log('Product created successfully'); - showToast('Product created successfully!'); + showToast('Product created successfully!', 'success'); closeModal(); } catch (createError) { console.error('Error in product creation:', createError); - console.error('Error details:', { - message: createError.message, - response: createError.response, - config: createError.config - }); - let errorMessage = 'Failed to create product. Please try again.'; if (createError.response?.data?.message) { errorMessage = createError.response.data.message; @@ -646,9 +887,6 @@ const IsicHsCodes = () => { let errorMessage = 'An unexpected error occurred. Please try again.'; if (error.response) { - console.error('Response data:', error.response.data); - console.error('Response status:', error.response.status); - if (error.response.status === 401) { errorMessage = 'Authentication required. Please log in again.'; } else if (error.response.status === 403) { @@ -661,10 +899,8 @@ const IsicHsCodes = () => { errorMessage = error.response.data.message; } } else if (error.request) { - console.error('No response received:', error.request); errorMessage = 'No response from server. Please check your network connection.'; } else { - console.error('Request setup error:', error.message); errorMessage = error.message || 'Error setting up the request.'; } @@ -679,311 +915,322 @@ const IsicHsCodes = () => { {toast.show && ( setToast(prev => ({ ...prev, show: false }))} /> )} -
- {/* Header */} -
-

HS Codes

-
-
-
- Search -
- setSearchTerm(e.target.value)} - placeholder="Search by code or name" - className="h-10 w-full rounded-md border border-[#C3C6CB] pl-9 pr-3 text-sm focus:outline-none focus:ring-1 focus:ring-[#92722A]" - /> -
- - - -
-
- - {/* Table */} - { - // STATUS COLUMN - if (colIndex === 6) { - return ( - - ); - } - - // ACTIONS COLUMN - if (colIndex === 7) { - return ( -
- - - -
- ); - } - - return value; - }} - pagination={{ - currentPage, - onPageChange: setCurrentPage, - pageSize, - totalItems: filteredData.length, - }} + + {/* Import Modal */} + setShowImportModal(false)} + onImport={handleImportCSV} /> - {/* Add/Edit/View Modal */} - {modalMode && ( -
-
-
- {/* Header */} -
-

- {modalMode === 'add' ? 'Add New HS Code' : modalMode === 'edit' ? 'Edit HS Code' : 'View HS Code'} -

- +
+ {/* Header */} +
+

HS Codes

+
+
+
+ Search +
+ setSearchTerm(e.target.value)} + placeholder="Search by code or name" + className="h-10 w-full rounded-md border border-[#C3C6CB] pl-9 pr-3 text-sm focus:outline-none focus:ring-1 focus:ring-[#92722A]" + />
+ + + +
+
- {/* Form */} -
- {/* HS Code */} -
-
- - -
+ {/* Table */} +
{ + if (colIndex === 6) { + return ( + + ); + } -
- - + if (colIndex === 7) { + return ( +
+ + +
+ ); + } + + return value; + }} + pagination={{ + currentPage, + onPageChange: setCurrentPage, + pageSize, + totalItems: filteredData.length, + }} + /> + + {/* Add/Edit/View Modal */} + {modalMode && ( +
+
+
+ {/* Header */} +
+

+ {modalMode === 'add' ? 'Add New HS Code' : modalMode === 'edit' ? 'Edit HS Code' : 'View HS Code'} +

+
- {/* Row 2: Unit + Status */} -
-
- - {isLoading ? ( -
- Loading units... -
- ) : error ? ( -
- {error} -
- ) : ( + {/* Form */} +
+
+
+ + +
+ +
+ + +
+
+ +
+
+ + {isLoading ? ( +
+ Loading units... +
+ ) : error ? ( +
+ {error} +
+ ) : ( + + )} +
+ +
+ - )} +
+
-
- - + Cancel + + +
+ )} +
+
+ )} + + {/* Delete Confirmation Modal */} + {showDeleteConfirm && ( +
+
+
+
+
+ Delete +
+

+ Delete HS Codes +

+

+ Are you sure you want to delete{' '} + + {deletingCode && `${deletingCode.code} - ${deletingCode.product}`} + + ? +

+

+ This action will permanently delete the data and cannot be undone. +

+
+
+ +
+ +
- - {/* Footer */} - {modalMode !== 'view' && ( -
- - -
- )}
-
- )} - - {/* Delete Confirmation Modal */} - {showDeleteConfirm && ( -
-
-
-
-
- Delete -
-

- Delete HS Codes -

-

- Are you sure you want to delete{' '} - - {deletingCode && `${deletingCode.code} - ${deletingCode.product}`} - - ? -

-

- This action will permanently delete the data and cannot be undone. -

-
-
- -
- - -
-
-
-
- )} + )}
); }; -export default IsicHsCodes; +export default IsicHsCodes; \ No newline at end of file diff --git a/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx b/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx index 11713bb..54d310b 100644 --- a/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx +++ b/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx @@ -3,8 +3,7 @@ import Table from '@/components/common/Table'; import StatusBadge from '@/components/common/StatusBadge'; import { TextField, SelectField } from '@/components/common/FormControls'; import { getUnits, createUnit, updateUnit, deleteUnit } from '@/services/configuration/unitService'; -import { toast } from 'react-toastify'; -import 'react-toastify/dist/ReactToastify.css'; + const downloadIconSrc = '/assets/images/DownloadSimple.svg'; const addIconSrc = '/assets/images/ic_baseline-plus.svg'; const pencilActiveSrc = '/assets/images/PencilSimple.svg'; @@ -16,6 +15,56 @@ const caretDownIconSrc = '/assets/images/caretdown-active.svg'; const rectangleIconSrc = '/assets/images/Rectangle.svg'; const checkboxIconSrc = '/assets/images/checkbox.svg'; +// Custom Toast Component +const CustomToast = ({ message, type, onClose }) => { + useEffect(() => { + const timer = setTimeout(() => { + onClose(); + }, 3000); + + return () => clearTimeout(timer); + }, [onClose]); + + const getToastStyles = () => { + switch (type) { + case 'success': + return 'bg-green-50 border-green-200 text-green-800'; + case 'error': + return 'bg-red-50 border-red-200 text-red-800'; + case 'warning': + return 'bg-yellow-50 border-yellow-200 text-yellow-800'; + default: + return 'bg-blue-50 border-blue-200 text-blue-800'; + } + }; + + const getIcon = () => { + switch (type) { + case 'success': + return '✅'; + case 'error': + return '❌'; + case 'warning': + return '⚠️'; + default: + return 'ℹ️'; + } + }; + + return ( +
+ {getIcon()} + {message} + +
+ ); +}; + const createEmptyUnitForm = () => ({ unitName: '', description: '', @@ -33,93 +82,80 @@ const UnitMaster = () => { const [form, setForm] = useState(createEmptyUnitForm()); const [isProductDropdownOpen, setIsProductDropdownOpen] = useState(false); const productDropdownRef = useRef(null); - const pageSize = 10; // Add this line + const pageSize = 10; const [currentUser, setCurrentUser] = useState(null); + const [toastData, setToastData] = useState(null); // Fetch units on component mount useEffect(() => { fetchUnits(); }, []); - // const fetchUnits = async () => { - // try { - // setLoading(true); - // const response = await getUnits(); - // setUnits(response.data || []); - // } catch (error) { - // console.error('Error fetching units:', error); - // } finally { - // setLoading(false); - // } - // }; - const fetchUnits = async () => { - try { - setLoading(true); - const response = await getUnits(); - const unitsData = response.data || []; + try { + setLoading(true); + const response = await getUnits(); + const unitsData = response.data || []; - // ✅ Sort by latest created_at date (newest first) - const sortedUnits = [...unitsData].sort( - (a, b) => new Date(b.created_at) - new Date(a.created_at) - ); + // ✅ Sort by latest created_at date (newest first) + const sortedUnits = [...unitsData].sort( + (a, b) => new Date(b.created_at) - new Date(a.created_at) + ); - setUnits(sortedUnits); - } catch (error) { - console.error('Error fetching units:', error); - } finally { - setLoading(false); - } -}; + setUnits(sortedUnits); + } catch (error) { + console.error('Error fetching units:', error); + } finally { + setLoading(false); + } + }; useEffect(() => { - const userProfile = sessionStorage.getItem('user_profile'); - - if (userProfile) { - setCurrentUser(JSON.parse(userProfile)); - } -}, []); - const headers = [ - 'Unit Name', - 'Description', - 'Mapped Products', - 'Created By', - 'Created On', - 'Last Updated', // ✅ New column - 'Status', - 'Actions', -]; + const userProfile = sessionStorage.getItem('user_profile'); + if (userProfile) { + setCurrentUser(JSON.parse(userProfile)); + } + }, []); + const headers = [ + 'Unit Name', + 'Description', + 'Mapped Products', + 'Created By', + 'Created On', + 'Last Updated', + 'Status', + 'Actions', + ]; const rows = useMemo(() => { - return units.map((item) => { - const createdDate = item.created_at - ? new Date(item.created_at).toLocaleDateString('en-GB') - : '-'; - const updatedDate = - item.updated_at && item.updated_at !== item.created_at - ? new Date(item.updated_at).toLocaleDateString('en-GB') + return units.map((item) => { + const createdDate = item.created_at + ? new Date(item.created_at).toLocaleDateString('en-GB') : '-'; - const currentUserName = currentUser?.name || ''; - - return [ - item.uom || item.unitName, // Unit Name - item.uom_short_name || item.description, // Description - item.mapped_products_count !== undefined - ? item.mapped_products_count - : (Array.isArray(item.productsMapped) ? item.productsMapped.length : 0), - item.created_by_name || currentUserName || '-', // Created By - createdDate, // Created On - updatedDate, // ✅ Last Update (moved here) - , // Status - 'actions', // Actions - ]; - }); -}, [units, currentUser]); + const updatedDate = + item.updated_at && item.updated_at !== item.created_at + ? new Date(item.updated_at).toLocaleDateString('en-GB') + : '-'; + const currentUserName = currentUser?.name || ''; + return [ + item.uom || item.unitName, + item.uom_short_name || item.description, + item.mapped_products_count !== undefined + ? item.mapped_products_count + : (Array.isArray(item.productsMapped) ? item.productsMapped.length : 0), + item.created_by_name || currentUserName || '-', + createdDate, + updatedDate, + , + 'actions', + ]; + }); + }, [units, currentUser]); const statusOptions = React.useMemo( () => [ @@ -166,23 +202,12 @@ const UnitMaster = () => { }); setModalMode('edit'); setIsProductDropdownOpen(false); -}; + }; const handleDelete = (index) => { setDeletingRow(index); }; - const confirmDelete = async () => { - if (deletingRow === null) return; - - try { - await deleteUnit(units[deletingRow].id); - setUnits(prev => prev.filter((_, index) => index !== deletingRow)); - setDeletingRow(null); - } catch (error) { - console.error('Error deleting unit:', error); - } - }; const closeModal = () => { setModalMode(null); setForm(createEmptyUnitForm()); @@ -206,7 +231,6 @@ const UnitMaster = () => { }); }; - React.useEffect(() => { const handleClickOutside = (event) => { if (!productDropdownRef.current) return; @@ -224,170 +248,124 @@ const UnitMaster = () => { }; }, [isProductDropdownOpen]); - const getToday = () => { - const now = new Date(); - const day = String(now.getDate()).padStart(2, '0'); - const month = String(now.getMonth() + 1).padStart(2, '0'); - const year = now.getFullYear(); - return `${day}/${month}/${year}`; - }; + const handleSave = async () => { + const selectedProducts = Array.isArray(form.productsMapped) ? form.productsMapped : []; -// const handleSaveForm = async () => { -// const selectedProducts = Array.isArray(form.productsMapped) ? form.productsMapped : []; -// if (!form.unitName || !form.description || !form.status) { -// return; -// } - -// const unitData = { -// uom: form.unitName, -// uom_short_name: form.description, -// is_active: form.status === 'Active', -// }; - -// try { -// setLoading(true); - -// if (modalMode === 'edit' && editingRow !== null) { -// const updatedUnitData = { -// ...unitData, -// updated_at: new Date().toISOString(), -// updated_by: currentUser?.id || null, -// }; - -// const updatedUnit = await updateUnit(units[editingRow].id, updatedUnitData); - -// setUnits(prev => -// prev.map((item, index) => -// index === editingRow -// ? { -// ...item, -// ...updatedUnit, -// updated_at: updatedUnitData.updated_at, -// created_at: item.created_at, -// created_by: item.created_by, -// created_by_name: item.created_by_name, -// } -// : item -// ) -// ); -// } else { -// const newUnit = await createUnit({ -// ...unitData, -// created_at: new Date().toISOString(), -// created_by: currentUser?.id || null, -// created_by_name: currentUser?.name || '', -// }); - -// setUnits(prev => [newUnit, ...prev]); -// } - -// await fetchUnits(); -// closeModal(); -// } catch (error) { -// console.error('Error saving unit:', error); -// } finally { -// setLoading(false); -// } -// }; - -const handleSave = async () => { - const selectedProducts = Array.isArray(form.productsMapped) ? form.productsMapped : []; - - // ✅ Validation checks - if (!form.unitName) { - toast.error('Please enter Unit Name.'); - return; - } - if (!form.description) { - toast.error('Please enter Description.'); - return; - } - if (!form.status) { - toast.error('Please select Status.'); - return; - } - - // ✅ Check for duplicate unit name - const nameExists = units.some( - unit => unit.uom?.toLowerCase().trim() === form.unitName.toLowerCase().trim() - ); - - // Only check duplicates when adding (not editing) - if (modalMode !== 'edit' && nameExists) { - toast.error('Unit name already exists!'); - return; - } - - const unitData = { - uom: form.unitName, - uom_short_name: form.description, - is_active: form.status === 'Active', - productsMapped: selectedProducts, - }; - - try { - setLoading(true); - - if (modalMode === 'edit' && editingRow !== null) { - const updatedUnitData = { - ...unitData, - updated_at: new Date().toISOString(), - updated_by: currentUser?.id || null, - }; - - const updatedUnit = await updateUnit(units[editingRow].id, updatedUnitData); - - setUnits(prev => - prev.map((item, index) => - index === editingRow - ? { - ...item, - ...updatedUnit, - updated_at: updatedUnitData.updated_at, - created_at: item.created_at, - created_by: item.created_by, - created_by_name: item.created_by_name, - productsMapped: selectedProducts, - } - : item - ) - ); - - toast.success('Unit updated successfully!'); - } else { - const newUnit = await createUnit({ - ...unitData, - created_at: new Date().toISOString(), - created_by: currentUser?.id || null, - created_by_name: currentUser?.name || '', + // ✅ Validation checks + if (!form.unitName) { + setToastData({ + message: 'Please enter Unit Name.', + type: 'error' }); - - setUnits(prev => [ - { - ...newUnit, - productsMapped: selectedProducts, - }, - ...prev, - ]); - - toast.success('New unit added successfully!'); + return; + } + if (!form.description) { + setToastData({ + message: 'Please enter Description.', + type: 'error' + }); + return; + } + if (!form.status) { + setToastData({ + message: 'Please select Status.', + type: 'error' + }); + return; } - await fetchUnits(); - closeModal(); - } catch (error) { - console.error('Error saving unit:', error); - toast.error('Something went wrong while saving the unit.'); - } finally { - setLoading(false); - } -}; + // ✅ Check for duplicate unit name + const nameExists = units.some( + unit => unit.uom?.toLowerCase().trim() === form.unitName.toLowerCase().trim() + ); + // Only check duplicates when adding (not editing) + if (modalMode !== 'edit' && nameExists) { + setToastData({ + message: 'Unit name already exists!', + type: 'error' + }); + return; + } + const unitData = { + uom: form.unitName, + uom_short_name: form.description, + is_active: form.status === 'Active', + productsMapped: selectedProducts, + }; + try { + setLoading(true); + if (modalMode === 'edit' && editingRow !== null) { + const updatedUnitData = { + ...unitData, + updated_at: new Date().toISOString(), + updated_by: currentUser?.id || null, + }; + await updateUnit(units[editingRow].id, updatedUnitData); + // ✅ Show success message for edit + setToastData({ + message: 'Unit updated successfully!', + type: 'success' + }); + } else { + // ✅ Create new record + await createUnit({ + ...unitData, + created_at: new Date().toISOString(), + created_by: currentUser?.id || null, + created_by_name: currentUser?.name || '', + }); + + // ✅ Show success message for add + setToastData({ + message: 'New unit added successfully!', + type: 'success' + }); + } + + await fetchUnits(); + closeModal(); + } catch (error) { + console.error('Error saving unit:', error); + setToastData({ + message: 'Something went wrong while saving the unit.', + type: 'error' + }); + } finally { + setLoading(false); + } + }; + + const handleDeleteConfirm = async () => { + if (deletingRow === null) return; + try { + const unitToDelete = units[deletingRow]; + await deleteUnit(unitToDelete.id); + setUnits(prev => prev.filter((_, index) => index !== deletingRow)); + + // ✅ Show success message for delete + setToastData({ + message: 'Unit deleted successfully!', + type: 'success' + }); + setDeletingRow(null); + } catch (error) { + console.error('Error deleting unit:', error); + setToastData({ + message: 'Failed to delete unit. Please try again.', + type: 'error' + }); + } + }; + + const closeDeleteConfirm = () => { + setDeletingRow(null); + }; const deletingUnit = React.useMemo(() => { if (deletingRow === null || deletingRow < 0 || deletingRow >= units.length) { @@ -396,90 +374,68 @@ const handleSave = async () => { return units[deletingRow]; }, [deletingRow, units]); - const closeDeleteConfirm = () => { - setDeletingRow(null); - }; - - - const handleDeleteConfirm = async () => { - if (deletingRow === null) return; - try { - await deleteUnit(units[deletingRow].id); - setUnits(prev => prev.filter((_, index) => index !== deletingRow)); - closeDeleteConfirm(); - } catch (error) { - console.error('Error deleting unit:', error); - } -}; - return (
-

Unit Master

+

Unit Master ( + + {units.length} {units.length === 1 ? 'unit': ''} + + )

- - - + return [ + item.uom || item.unitName || '', + item.uom_short_name || item.description || '', + mappedProducts, + item.created_by_name || currentUser?.name || '-', + createdDate, + updatedDate, + status, + ] + .map((value) => `"${String(value ?? '').replace(/"/g, '""')}"`) + .join(','); + }); + const csvContent = csvHeader + '\n' + csvRows.join('\n'); + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', 'unit-master.csv'); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + }} + className={`h-10 px-4 rounded-[6px] border text-sm inline-flex items-center gap-2 ${ + units.length + ? 'bg-[#F7F7F7] border-[#C3C6CB] text-[#232528]' + : 'bg-[#F3F4F6] border-[#E5E7EB] text-[#9CA3AF] cursor-not-allowed' + }`} + disabled={!units.length} + > + Export + Export CSV +
- {loading ? ( -
-
-
-) : ( -
{ - if (colIndex === headers.length - 1) { - return ( -
- - -
- ); - } - return value; - }} - pagination={{ - currentPage, - onPageChange: setCurrentPage, - pageSize, - totalItems: filteredRows.length, - }} - /> - )} + {loading ? ( +
+
+
+ ) : ( +
{ + if (colIndex === headers.length - 1) { + return ( +
+ + +
+ ); + } + return value; + }} + pagination={{ + currentPage, + onPageChange: setCurrentPage, + pageSize, + totalItems: filteredRows.length, + }} + /> + )} + + {/* Add/Edit Modal */} {modalMode && (
@@ -573,16 +531,16 @@ const handleSave = async () => {
- - Unit Name * - - } - value={form.unitName} - onChange={handleFormChange('unitName')} - placeholder="Enter Unit Name" -/> + + Unit Name * + + } + value={form.unitName} + onChange={handleFormChange('unitName')} + placeholder="Enter Unit Name" + /> { )}
- - Status * - - } - value={form.status} - onChange={handleFormChange('status')} - options={statusOptions} - placeholder="Select Status" -/> + + Status * + + } + value={form.status} + onChange={handleFormChange('status')} + options={statusOptions} + placeholder="Select Status" + />
@@ -658,27 +616,26 @@ const handleSave = async () => { > Cancel - - - +
)} + {/* Delete Confirmation Modal */} {deletingUnit && (
@@ -734,8 +691,22 @@ const handleSave = async () => {
)} + + {/* Custom Toast Notification */} + {/* Custom Toast Notification - CENTERED */} +{toastData && ( +
+
+ setToastData(null)} + /> +
+
+)} ); }; -export default UnitMaster; +export default UnitMaster; \ No newline at end of file diff --git a/ipi-survey-platform/src/pages/History/History.jsx b/ipi-survey-platform/src/pages/History/History.jsx index 3f20cfc..199f27a 100644 --- a/ipi-survey-platform/src/pages/History/History.jsx +++ b/ipi-survey-platform/src/pages/History/History.jsx @@ -314,6 +314,7 @@ const formatExportDateTime = (value) => { { value: 'approved', label: 'Approved' }, { value: 'rejected', label: 'Rejected' }, { value: 'submitted', label: 'Submitted' }, + { value: 'resubmitted', label: 'ReSubmitted' }, ]; const filteredRows = useMemo(() => { diff --git a/ipi-survey-platform/src/pages/Overview/Overview.jsx b/ipi-survey-platform/src/pages/Overview/Overview.jsx index dda524f..41c18bc 100644 --- a/ipi-survey-platform/src/pages/Overview/Overview.jsx +++ b/ipi-survey-platform/src/pages/Overview/Overview.jsx @@ -30,7 +30,7 @@ const Overview = () => { const [submissions, setSubmissions] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const establishmentId = 41; // You might want to get this from your auth context or props + const establishmentId = sessionStorage.getItem('establishment_id'); const [pagination, setPagination] = useState({ currentPage: 1, pageSize: 10, @@ -45,6 +45,7 @@ const Overview = () => { setLoading(true); // Use getSubmissionHistoryByEstablishment instead of getSubmissions const response = await getSubmissionHistoryByEstablishment(establishmentId); + console.log("response",response) setSubmissions(response.data || []); } catch (err) { setError('Failed to load submission history'); diff --git a/ipi-survey-platform/src/pages/SubmissionDetails/SubmissionDetails.jsx b/ipi-survey-platform/src/pages/SubmissionDetails/SubmissionDetails.jsx new file mode 100644 index 0000000..1bdbe79 --- /dev/null +++ b/ipi-survey-platform/src/pages/SubmissionDetails/SubmissionDetails.jsx @@ -0,0 +1,87 @@ +import React, { useState, useEffect } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; +import { fetchSubmissionDetail } from '@/services/submissions/submissionService'; +import { DetailedOverview } from '@/components/overview/DetailedOverview'; +import HeaderBar from '@/components/layout/HeaderBar'; + +const SubmissionDetails = () => { + const { submissionId } = useParams(); + const navigate = useNavigate(); + const [submission, setSubmission] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const loadSubmission = async () => { + try { + const response = await fetchSubmissionDetail(submissionId); + const data = response.data; + console.log("data",data) + setSubmission({ + id: submissionId, + survey_name: 'Industrial Production Survey', + year: data.year || '—', + quarter: data.quarter || '—', + status: data.status || '—', + submission_on: data.created_at || new Date().toISOString(), + products: data.products?.length || 0, + ...data + }); + } catch (error) { + console.error('Error loading submission:', error); + } finally { + setLoading(false); + } + }; + + if (submissionId) { + loadSubmission(); + } + }, [submissionId]); + + const handleBack = () => { + navigate('/dasboard'); + }; + + if (loading) { + return ( +
+ +
+
+
+
+
+
+ ); + } + + if (!submission) { + return ( +
+ +
+
+

Submission not found

+ +
+
+
+ ); + } + + return ( +
+ +
+ +
+
+ ); +}; + +export default SubmissionDetails; diff --git a/ipi-survey-platform/src/services/configuration/adminService.js b/ipi-survey-platform/src/services/configuration/adminService.js index 28ab2eb..97910fc 100644 --- a/ipi-survey-platform/src/services/configuration/adminService.js +++ b/ipi-survey-platform/src/services/configuration/adminService.js @@ -51,6 +51,56 @@ export const getAdminUserById = async (id) => { // throw error; // } // }; +export const changeAdminUserPassword = async (id, passwordData) => { + try { + if (!id) throw new Error("Invalid user ID"); + + // Debug: see what fields the API expects + console.log("🔍 Debug: Making test request to discover required fields"); + + const testData = { test_field: "test_value" }; + + try { + await putRequest(`/admin/${id}/change-password`, testData); + } catch (testError) { + console.log( + "🔍 Debug: Test request failed with:", + testError.response?.data || testError.message + ); + } + + // Common Laravel-style field names + const requestData = { + password: passwordData.newPassword, + password_confirmation: passwordData.confirmPassword, + new_password: passwordData.newPassword, + new_password_confirmation: passwordData.confirmPassword, + }; + + console.log("🔍 Debug: Final request data:", requestData); + + const response = await putRequest(`/admin/${id}/change-password`, requestData); + + return response.data; + } catch (error) { + console.error("Error changing admin user password:", error); + + if (error.response) { + console.error("🔍 Debug: Full error response:", { + status: error.response.status, + data: error.response.data, + headers: error.response.headers, + }); + + if (error.response.data.errors) { + console.error("🔍 Debug: Validation errors:", error.response.data.errors); + } + } + + throw error; + } +}; + export const deleteAdminUser = async (id) => { try { @@ -78,4 +128,8 @@ export const deleteAdminUser = async (id) => { console.error("Error deleting admin user:", error); throw error; } + + + + }; \ No newline at end of file