fix map and stuff

This commit is contained in:
Jake Kasper
2025-08-22 15:49:00 -04:00
parent 89ee666858
commit 8049a72505
3 changed files with 67 additions and 11 deletions

View File

@@ -95,10 +95,10 @@ const applicationPlanSchema = Joi.object({
lawnSectionId: Joi.number().integer().positive().required(), lawnSectionId: Joi.number().integer().positive().required(),
equipmentId: Joi.number().integer().positive().required(), equipmentId: Joi.number().integer().positive().required(),
plannedDate: Joi.date().required(), plannedDate: Joi.date().required(),
notes: Joi.string(), notes: Joi.string().allow('').optional(),
products: Joi.array().items(Joi.object({ products: Joi.array().items(Joi.object({
productId: Joi.number().integer().positive(), productId: Joi.number().integer().positive().optional(),
userProductId: Joi.number().integer().positive(), userProductId: Joi.number().integer().positive().optional(),
rateAmount: Joi.number().positive().required(), rateAmount: Joi.number().positive().required(),
rateUnit: Joi.string().max(50).required() rateUnit: Joi.string().max(50).required()
})).min(1).required() })).min(1).required()

View File

@@ -355,9 +355,27 @@ const PropertyMap = ({
{/* Existing sections */} {/* Existing sections */}
{sections.map((section) => { {sections.map((section) => {
if (!section.polygonData?.coordinates?.[0]) return null; console.log('Section:', section);
// Handle both string and object polygon data
let polygonData = section.polygonData;
if (typeof polygonData === 'string') {
try {
polygonData = JSON.parse(polygonData);
} catch (e) {
console.error('Failed to parse polygon data:', e);
return null;
}
}
if (!polygonData?.coordinates?.[0]) {
console.log('No coordinates found for section:', section.id);
return null;
}
const coordinates = polygonData.coordinates[0].map(([lng, lat]) => [lat, lng]);
console.log('Mapped coordinates:', coordinates);
const coordinates = section.polygonData.coordinates[0].map(([lng, lat]) => [lat, lng]);
const isInternallySelected = selectedSection?.id === section.id; const isInternallySelected = selectedSection?.id === section.id;
const isExternallySelected = selectedSections.includes(section.id); const isExternallySelected = selectedSections.includes(section.id);
const isSelected = isInternallySelected || isExternallySelected; const isSelected = isInternallySelected || isExternallySelected;

View File

@@ -6,7 +6,7 @@ import {
WrenchScrewdriverIcon, WrenchScrewdriverIcon,
CalculatorIcon CalculatorIcon
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
import { propertiesAPI, productsAPI, equipmentAPI, applicationsAPI } from '../../services/api'; import { propertiesAPI, productsAPI, equipmentAPI, applicationsAPI, nozzlesAPI } from '../../services/api';
import LoadingSpinner from '../../components/UI/LoadingSpinner'; import LoadingSpinner from '../../components/UI/LoadingSpinner';
import PropertyMap from '../../components/Maps/PropertyMap'; import PropertyMap from '../../components/Maps/PropertyMap';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
@@ -138,10 +138,12 @@ const Applications = () => {
lawnSectionId: parseInt(areaId), lawnSectionId: parseInt(areaId),
equipmentId: parseInt(planData.equipmentId), equipmentId: parseInt(planData.equipmentId),
plannedDate: new Date().toISOString().split('T')[0], // Default to today plannedDate: new Date().toISOString().split('T')[0], // Default to today
notes: planData.notes, notes: planData.notes || '', // Ensure notes is never null/undefined
products: [{ products: [{
productId: planData.selectedProduct?.isShared ? parseInt(planData.selectedProduct.id) : null, ...(planData.selectedProduct?.isShared
userProductId: !planData.selectedProduct?.isShared ? parseInt(planData.selectedProduct.id) : null, ? { productId: parseInt(planData.selectedProduct.id) }
: { userProductId: parseInt(planData.selectedProduct.id) }
),
rateAmount: parseFloat(planData.selectedProduct?.customRateAmount || planData.selectedProduct?.rateAmount || 1), rateAmount: parseFloat(planData.selectedProduct?.customRateAmount || planData.selectedProduct?.rateAmount || 1),
rateUnit: planData.selectedProduct?.customRateUnit || planData.selectedProduct?.rateUnit || 'per 1000sqft' rateUnit: planData.selectedProduct?.customRateUnit || planData.selectedProduct?.rateUnit || 'per 1000sqft'
}] }]
@@ -170,6 +172,7 @@ const ApplicationPlanModal = ({ onClose, onSubmit }) => {
const [properties, setProperties] = useState([]); const [properties, setProperties] = useState([]);
const [products, setProducts] = useState([]); const [products, setProducts] = useState([]);
const [equipment, setEquipment] = useState([]); const [equipment, setEquipment] = useState([]);
const [nozzles, setNozzles] = useState([]);
const [selectedPropertyDetails, setSelectedPropertyDetails] = useState(null); const [selectedPropertyDetails, setSelectedPropertyDetails] = useState(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [loadingProperty, setLoadingProperty] = useState(false); const [loadingProperty, setLoadingProperty] = useState(false);
@@ -181,6 +184,7 @@ const ApplicationPlanModal = ({ onClose, onSubmit }) => {
selectedProduct: null, selectedProduct: null,
applicationType: '', // 'liquid' or 'granular' applicationType: '', // 'liquid' or 'granular'
equipmentId: '', equipmentId: '',
nozzleId: '',
notes: '' notes: ''
}); });
@@ -191,15 +195,17 @@ const ApplicationPlanModal = ({ onClose, onSubmit }) => {
const fetchPlanningData = async () => { const fetchPlanningData = async () => {
try { try {
setLoading(true); setLoading(true);
const [propertiesResponse, productsResponse, equipmentResponse] = await Promise.all([ const [propertiesResponse, productsResponse, equipmentResponse, nozzlesResponse] = await Promise.all([
propertiesAPI.getAll(), propertiesAPI.getAll(),
productsAPI.getAll(), productsAPI.getAll(),
equipmentAPI.getAll() equipmentAPI.getAll(),
nozzlesAPI.getAll()
]); ]);
console.log('Properties response:', propertiesResponse.data); console.log('Properties response:', propertiesResponse.data);
console.log('Products response:', productsResponse.data); console.log('Products response:', productsResponse.data);
console.log('Equipment response:', equipmentResponse.data); console.log('Equipment response:', equipmentResponse.data);
console.log('Nozzles response:', nozzlesResponse.data);
setProperties(propertiesResponse.data.data.properties || []); setProperties(propertiesResponse.data.data.properties || []);
// Combine shared and user products with unique IDs // Combine shared and user products with unique IDs
@@ -218,6 +224,7 @@ const ApplicationPlanModal = ({ onClose, onSubmit }) => {
const allProducts = [...sharedProducts, ...userProducts]; const allProducts = [...sharedProducts, ...userProducts];
setProducts(allProducts); setProducts(allProducts);
setEquipment(equipmentResponse.data.data.equipment || []); setEquipment(equipmentResponse.data.data.equipment || []);
setNozzles(nozzlesResponse.data.data?.nozzles || nozzlesResponse.data || []);
} catch (error) { } catch (error) {
console.error('Failed to fetch planning data:', error); console.error('Failed to fetch planning data:', error);
toast.error('Failed to load planning data'); toast.error('Failed to load planning data');
@@ -335,6 +342,7 @@ const ApplicationPlanModal = ({ onClose, onSubmit }) => {
{selectedPropertyDetails.latitude && selectedPropertyDetails.longitude && ( {selectedPropertyDetails.latitude && selectedPropertyDetails.longitude && (
<div className="mb-4"> <div className="mb-4">
<div className="relative"> <div className="relative">
{console.log('Map sections data:', selectedPropertyDetails.sections)}
<PropertyMap <PropertyMap
center={[selectedPropertyDetails.latitude, selectedPropertyDetails.longitude]} center={[selectedPropertyDetails.latitude, selectedPropertyDetails.longitude]}
zoom={18} zoom={18}
@@ -474,6 +482,36 @@ const ApplicationPlanModal = ({ onClose, onSubmit }) => {
</div> </div>
)} )}
{/* Nozzle Selection for Liquid Applications */}
{planData.applicationType === 'liquid' && (
<div>
<label className="label flex items-center gap-2">
<BeakerIcon className="h-5 w-5" />
Nozzle Selection
</label>
<select
className="input"
value={planData.nozzleId}
onChange={(e) => setPlanData({ ...planData, nozzleId: e.target.value })}
>
<option value="">Select nozzle (optional)...</option>
{nozzles.map((nozzle) => (
<option key={nozzle.id} value={nozzle.id}>
{nozzle.customName || nozzle.typeName}
{nozzle.orificeSize && ` - ${nozzle.orificeSize}`}
{nozzle.flowRateGpm && ` (${nozzle.flowRateGpm} GPM)`}
{nozzle.sprayAngle && ` ${nozzle.sprayAngle}°`}
</option>
))}
</select>
{nozzles.length === 0 && (
<p className="text-sm text-orange-600 mt-1">
No nozzles found. Add nozzles in Equipment management first.
</p>
)}
</div>
)}
{/* Notes */} {/* Notes */}
<div> <div>
<label className="label">Notes</label> <label className="label">Notes</label>