comment chagnes

This commit is contained in:
2ManyProjects 2025-05-05 15:37:48 -05:00
parent 2f32cb7deb
commit 96334a595f
4 changed files with 4182 additions and 173 deletions

3982
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -23,12 +23,14 @@
"axios": "^1.6.2", "axios": "^1.6.2",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"papaparse": "^5.5.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-email-editor": "^1.7.11", "react-email-editor": "^1.7.11",
"react-redux": "^9.0.2", "react-redux": "^9.0.2",
"react-router-dom": "^6.20.1", "react-router-dom": "^6.20.1",
"recharts": "^2.10.3" "recharts": "^2.10.3",
"xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.2.37", "@types/react": "^18.2.37",

View file

@ -0,0 +1,106 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import apiClient from '@services/api';
import { useNotification } from './reduxHooks';
import { useNavigate, useParams, useLocation } from 'react-router-dom';
// Fetch categories
export const useCategories = () => {
return useQuery({
queryKey: ['categories'],
queryFn: async () => {
const response = await apiClient.get('/products/categories/all');
return response.data;
}
})
};
// Fetch all available tags
export const useTags = () => {
return useQuery({
queryKey: ['tags'],
queryFn: async () => {
const response = await apiClient.get('/products/tags/all');
return response.data;
}
})
};
// Fetch product data if editing
export const useProduct = (id, isNewProduct) => {
return useQuery({
queryKey: ['product', id],
queryFn: async () => {
const response = await apiClient.get(`/products/${id}`);
return response.data[0];
},
enabled: !isNewProduct
})
};
// Create product mutation
export const useCreateProduct = () => {
const notification = useNotification();
const queryClient = useQueryClient();
const navigate = useNavigate();
return useMutation({
mutationFn: async (productData) => {
return await apiClient.post('/admin/products', productData);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['admin-products'] });
notification.showNotification('Product created successfully', 'success');
// Redirect after a short delay
setTimeout(() => {
navigate('/admin/products');
}, 1500);
},
onError: (error) => {
notification.showNotification(
`Failed to create product: ${error.message}`,
'error'
);
}
})
};
// Update product mutation
export const useUpdateProduct = (id) => {
const notification = useNotification();
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ id, productData }) => {
return await apiClient.put(`/admin/products/${id}`, productData);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['admin-products'] });
queryClient.invalidateQueries({ queryKey: ['product', id] });
notification.showNotification('Product updated successfully', 'success');
},
onError: (error) => {
notification.showNotification(
`Failed to update product: ${error.message}`,
'error'
);
}
})
};
// Save stock notification settings
export const useSaveStockNotification = (id) => {
const notification = useNotification();
return useMutation({
mutationFn: async (notificationData) => {
return await apiClient.post(`/admin/products/${id}/stock-notification`, notificationData);
},
onSuccess: () => {
notification.showNotification('Stock notification settings saved!', 'success');
},
onError: (error) => {
notification.showNotification(
`Failed to save notification settings: ${error.message}`,
'error'
);
}
})
};

View file

@ -31,6 +31,7 @@ import NotificationsActiveIcon from '@mui/icons-material/NotificationsActive';
import ImageUploader from '@components/ImageUploader'; import ImageUploader from '@components/ImageUploader';
import apiClient from '@services/api'; import apiClient from '@services/api';
import { useAuth } from '@hooks/reduxHooks'; import { useAuth } from '@hooks/reduxHooks';
import { useCategories, useTags, useProduct, useCreateProduct, useUpdateProduct, useSaveStockNotification } from '@hooks/productAdminHooks';
const ProductEditPage = () => { const ProductEditPage = () => {
const { pathname } = useLocation(); const { pathname } = useLocation();
@ -75,106 +76,26 @@ const ProductEditPage = () => {
}); });
// Fetch categories // Fetch categories
const { data: categories, isLoading: categoriesLoading } = useQuery({ const { data: categories, isLoading: categoriesLoading } = useCategories();
queryKey: ['categories'],
queryFn: async () => {
const response = await apiClient.get('/products/categories/all');
return response.data;
}
});
// Fetch all available tags // Fetch all available tags
const { data: allTags, isLoading: tagsLoading } = useQuery({ const { data: allTags, isLoading: tagsLoading } = useTags();
queryKey: ['tags'],
queryFn: async () => {
const response = await apiClient.get('/products/tags/all');
return response.data;
}
});
// Fetch product data if editing // Fetch product data if editing
const { const {
data: product, data: product,
isLoading: productLoading, isLoading: productLoading,
error: productError error: productError
} = useQuery({ } = useProduct(id === 'new' ? null : id, isNewProduct);
queryKey: ['product', id],
queryFn: async () => {
const response = await apiClient.get(`/products/${id}`);
return response.data[0];
},
enabled: !isNewProduct
});
// Create product mutation // Create product mutation
const createProduct = useMutation({ const createProduct = useCreateProduct();
mutationFn: async (productData) => {
return await apiClient.post('/admin/products', productData);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['admin-products'] });
setNotification({
open: true,
message: 'Product created successfully!',
severity: 'success'
});
// Redirect after a short delay
setTimeout(() => {
navigate('/admin/products');
}, 1500);
},
onError: (error) => {
setNotification({
open: true,
message: `Failed to create product: ${error.message}`,
severity: 'error'
});
}
});
// Update product mutation // Update product mutation
const updateProduct = useMutation({ const updateProduct = useUpdateProduct(id === 'new' ? null : id);
mutationFn: async ({ id, productData }) => {
return await apiClient.put(`/admin/products/${id}`, productData);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['admin-products'] });
queryClient.invalidateQueries({ queryKey: ['product', id] });
setNotification({
open: true,
message: 'Product updated successfully!',
severity: 'success'
});
},
onError: (error) => {
setNotification({
open: true,
message: `Failed to update product: ${error.message}`,
severity: 'error'
});
}
});
// Save stock notification settings // Save stock notification settings
const saveStockNotification = useMutation({ const saveStockNotification = useSaveStockNotification(id === 'new' ? null : id);
mutationFn: async (notificationData) => {
return await apiClient.post(`/admin/products/${id}/stock-notification`, notificationData);
},
onSuccess: () => {
setNotification({
open: true,
message: 'Stock notification settings saved!',
severity: 'success'
});
},
onError: (error) => {
setNotification({
open: true,
message: `Failed to save notification settings: ${error.message}`,
severity: 'error'
});
}
});
// Handle form changes // Handle form changes
const handleChange = (e) => { const handleChange = (e) => {
@ -323,35 +244,34 @@ const ProductEditPage = () => {
}; };
// Add notification data if enabled // Add notification data if enabled
if (notificationEnabled && !isNewProduct) {
if (notificationEnabled) {
productData.stockNotification = { productData.stockNotification = {
enabled: true, enabled: true,
email: notificationEmail, email: notificationEmail,
threshold: stockThreshold threshold: stockThreshold
}; };
} }
if (isNewProduct) { if (isNewProduct) {
createProduct.mutate(productData); createProduct.mutate(productData);
} else { } else {
updateProduct.mutate({ id, productData }); updateProduct.mutate({ id, productData });
}
// Save notification settings separately // Save notification settings separately
if (notificationEnabled) { // if (notificationEnabled) {
saveStockNotification.mutate({ // saveStockNotification.mutate({
enabled: true, // enabled: true,
email: notificationEmail, // email: notificationEmail,
threshold: stockThreshold // threshold: stockThreshold
}); // });
} else { // } else {
// Disable notifications if checkbox is unchecked // // Disable notifications if checkbox is unchecked
saveStockNotification.mutate({ // saveStockNotification.mutate({
enabled: false, // enabled: false,
email: '', // email: '',
threshold: 0 // threshold: 0
}); // });
} // }
}
}; };
// Handle notification close // Handle notification close
@ -545,7 +465,7 @@ const ProductEditPage = () => {
</Grid> </Grid>
{/* Stock Notification Section */} {/* Stock Notification Section */}
{!isNewProduct && (
<> <>
<Grid item xs={12}> <Grid item xs={12}>
<Divider sx={{ my: 2 }} /> <Divider sx={{ my: 2 }} />
@ -616,7 +536,6 @@ const ProductEditPage = () => {
</Card> </Card>
</Grid> </Grid>
</> </>
)}
<Grid item xs={12}> <Grid item xs={12}>
<Divider sx={{ my: 2 }} /> <Divider sx={{ my: 2 }} />