diff --git a/frontend/src/pages/Nozzles/Nozzles.js b/frontend/src/pages/Nozzles/Nozzles.js new file mode 100644 index 0000000..507f749 --- /dev/null +++ b/frontend/src/pages/Nozzles/Nozzles.js @@ -0,0 +1,515 @@ +import React, { useState, useEffect } from 'react'; +import { + PlusIcon, + MagnifyingGlassIcon, + TrashIcon, + PencilIcon, + BeakerIcon +} from '@heroicons/react/24/outline'; +import { nozzlesAPI } from '../../services/api'; +import LoadingSpinner from '../../components/UI/LoadingSpinner'; +import toast from 'react-hot-toast'; + +const Nozzles = () => { + const [userNozzles, setUserNozzles] = useState([]); + const [nozzleTypes, setNozzleTypes] = useState([]); + const [loading, setLoading] = useState(true); + const [showCreateForm, setShowCreateForm] = useState(false); + const [showEditForm, setShowEditForm] = useState(false); + const [editingNozzle, setEditingNozzle] = useState(null); + const [searchTerm, setSearchTerm] = useState(''); + const [selectedManufacturer, setSelectedManufacturer] = useState(''); + const [selectedDropletSize, setSelectedDropletSize] = useState(''); + + useEffect(() => { + fetchData(); + }, []); + + const fetchData = async () => { + try { + setLoading(true); + const [userNozzlesResponse, nozzleTypesResponse] = await Promise.all([ + nozzlesAPI.getUserNozzles(), + nozzlesAPI.getNozzleTypes() + ]); + + setUserNozzles(userNozzlesResponse.data.data.userNozzles || []); + setNozzleTypes(nozzleTypesResponse.data.data.nozzleTypes || []); + } catch (error) { + console.error('Failed to fetch nozzles:', error); + toast.error('Failed to load nozzles'); + setUserNozzles([]); + setNozzleTypes([]); + } finally { + setLoading(false); + } + }; + + const handleCreateNozzle = async (nozzleData) => { + try { + await nozzlesAPI.create(nozzleData); + toast.success('Nozzle added successfully!'); + setShowCreateForm(false); + fetchData(); + } catch (error) { + console.error('Failed to create nozzle:', error); + toast.error('Failed to add nozzle'); + } + }; + + const handleEditNozzle = (nozzle) => { + setEditingNozzle(nozzle); + setShowEditForm(true); + }; + + const handleUpdateNozzle = async (nozzleData) => { + try { + await nozzlesAPI.update(editingNozzle.id, nozzleData); + toast.success('Nozzle updated successfully!'); + setShowEditForm(false); + setEditingNozzle(null); + fetchData(); + } catch (error) { + console.error('Failed to update nozzle:', error); + toast.error('Failed to update nozzle'); + } + }; + + const handleDeleteNozzle = async (nozzleId) => { + if (window.confirm('Are you sure you want to delete this nozzle?')) { + try { + await nozzlesAPI.delete(nozzleId); + toast.success('Nozzle deleted successfully'); + fetchData(); + } catch (error) { + console.error('Failed to delete nozzle:', error); + toast.error('Failed to delete nozzle'); + } + } + }; + + // Filter nozzles based on search and filters + const filteredNozzles = userNozzles.filter(nozzle => { + const matchesSearch = searchTerm === '' || + nozzle.nozzleType?.name?.toLowerCase().includes(searchTerm.toLowerCase()) || + nozzle.customName?.toLowerCase().includes(searchTerm.toLowerCase()) || + nozzle.nozzleType?.manufacturer?.toLowerCase().includes(searchTerm.toLowerCase()); + + const matchesManufacturer = selectedManufacturer === '' || + nozzle.nozzleType?.manufacturer === selectedManufacturer; + + const matchesDropletSize = selectedDropletSize === '' || + nozzle.nozzleType?.dropletSize === selectedDropletSize; + + return matchesSearch && matchesManufacturer && matchesDropletSize; + }); + + // Get unique manufacturers and droplet sizes for filters + const manufacturers = [...new Set(nozzleTypes.map(type => type.manufacturer).filter(Boolean))]; + const dropletSizes = [...new Set(nozzleTypes.map(type => type.dropletSize).filter(Boolean))]; + + const NozzleCard = ({ nozzle }) => ( +
{nozzle.nozzleType.manufacturer} {nozzle.nozzleType.model}
+ )} ++ Notes: {nozzle.notes} +
+ )} + + {nozzle.purchaseDate && ( ++ Purchased: {new Date(nozzle.purchaseDate).toLocaleDateString()} +
+ )} +Manage your spray nozzle inventory with specifications
++ {searchTerm || selectedManufacturer || selectedDropletSize + ? 'Try adjusting your search or filters' + : 'Start building your nozzle inventory' + } +
+ {!searchTerm && !selectedManufacturer && !selectedDropletSize && ( + + )} +