fix map and stuff
This commit is contained in:
@@ -95,10 +95,10 @@ const applicationPlanSchema = Joi.object({
|
||||
lawnSectionId: Joi.number().integer().positive().required(),
|
||||
equipmentId: Joi.number().integer().positive().required(),
|
||||
plannedDate: Joi.date().required(),
|
||||
notes: Joi.string(),
|
||||
notes: Joi.string().allow('').optional(),
|
||||
products: Joi.array().items(Joi.object({
|
||||
productId: Joi.number().integer().positive(),
|
||||
userProductId: Joi.number().integer().positive(),
|
||||
productId: Joi.number().integer().positive().optional(),
|
||||
userProductId: Joi.number().integer().positive().optional(),
|
||||
rateAmount: Joi.number().positive().required(),
|
||||
rateUnit: Joi.string().max(50).required()
|
||||
})).min(1).required()
|
||||
|
||||
@@ -355,9 +355,27 @@ const PropertyMap = ({
|
||||
|
||||
{/* Existing sections */}
|
||||
{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 isExternallySelected = selectedSections.includes(section.id);
|
||||
const isSelected = isInternallySelected || isExternallySelected;
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
WrenchScrewdriverIcon,
|
||||
CalculatorIcon
|
||||
} 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 PropertyMap from '../../components/Maps/PropertyMap';
|
||||
import toast from 'react-hot-toast';
|
||||
@@ -138,10 +138,12 @@ const Applications = () => {
|
||||
lawnSectionId: parseInt(areaId),
|
||||
equipmentId: parseInt(planData.equipmentId),
|
||||
plannedDate: new Date().toISOString().split('T')[0], // Default to today
|
||||
notes: planData.notes,
|
||||
notes: planData.notes || '', // Ensure notes is never null/undefined
|
||||
products: [{
|
||||
productId: planData.selectedProduct?.isShared ? parseInt(planData.selectedProduct.id) : null,
|
||||
userProductId: !planData.selectedProduct?.isShared ? parseInt(planData.selectedProduct.id) : null,
|
||||
...(planData.selectedProduct?.isShared
|
||||
? { productId: parseInt(planData.selectedProduct.id) }
|
||||
: { userProductId: parseInt(planData.selectedProduct.id) }
|
||||
),
|
||||
rateAmount: parseFloat(planData.selectedProduct?.customRateAmount || planData.selectedProduct?.rateAmount || 1),
|
||||
rateUnit: planData.selectedProduct?.customRateUnit || planData.selectedProduct?.rateUnit || 'per 1000sqft'
|
||||
}]
|
||||
@@ -170,6 +172,7 @@ const ApplicationPlanModal = ({ onClose, onSubmit }) => {
|
||||
const [properties, setProperties] = useState([]);
|
||||
const [products, setProducts] = useState([]);
|
||||
const [equipment, setEquipment] = useState([]);
|
||||
const [nozzles, setNozzles] = useState([]);
|
||||
const [selectedPropertyDetails, setSelectedPropertyDetails] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loadingProperty, setLoadingProperty] = useState(false);
|
||||
@@ -181,6 +184,7 @@ const ApplicationPlanModal = ({ onClose, onSubmit }) => {
|
||||
selectedProduct: null,
|
||||
applicationType: '', // 'liquid' or 'granular'
|
||||
equipmentId: '',
|
||||
nozzleId: '',
|
||||
notes: ''
|
||||
});
|
||||
|
||||
@@ -191,15 +195,17 @@ const ApplicationPlanModal = ({ onClose, onSubmit }) => {
|
||||
const fetchPlanningData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const [propertiesResponse, productsResponse, equipmentResponse] = await Promise.all([
|
||||
const [propertiesResponse, productsResponse, equipmentResponse, nozzlesResponse] = await Promise.all([
|
||||
propertiesAPI.getAll(),
|
||||
productsAPI.getAll(),
|
||||
equipmentAPI.getAll()
|
||||
equipmentAPI.getAll(),
|
||||
nozzlesAPI.getAll()
|
||||
]);
|
||||
|
||||
console.log('Properties response:', propertiesResponse.data);
|
||||
console.log('Products response:', productsResponse.data);
|
||||
console.log('Equipment response:', equipmentResponse.data);
|
||||
console.log('Nozzles response:', nozzlesResponse.data);
|
||||
|
||||
setProperties(propertiesResponse.data.data.properties || []);
|
||||
// Combine shared and user products with unique IDs
|
||||
@@ -218,6 +224,7 @@ const ApplicationPlanModal = ({ onClose, onSubmit }) => {
|
||||
const allProducts = [...sharedProducts, ...userProducts];
|
||||
setProducts(allProducts);
|
||||
setEquipment(equipmentResponse.data.data.equipment || []);
|
||||
setNozzles(nozzlesResponse.data.data?.nozzles || nozzlesResponse.data || []);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch planning data:', error);
|
||||
toast.error('Failed to load planning data');
|
||||
@@ -335,6 +342,7 @@ const ApplicationPlanModal = ({ onClose, onSubmit }) => {
|
||||
{selectedPropertyDetails.latitude && selectedPropertyDetails.longitude && (
|
||||
<div className="mb-4">
|
||||
<div className="relative">
|
||||
{console.log('Map sections data:', selectedPropertyDetails.sections)}
|
||||
<PropertyMap
|
||||
center={[selectedPropertyDetails.latitude, selectedPropertyDetails.longitude]}
|
||||
zoom={18}
|
||||
@@ -474,6 +482,36 @@ const ApplicationPlanModal = ({ onClose, onSubmit }) => {
|
||||
</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 */}
|
||||
<div>
|
||||
<label className="label">Notes</label>
|
||||
|
||||
Reference in New Issue
Block a user