update some stuff
This commit is contained in:
@@ -8,7 +8,8 @@ import {
|
|||||||
TrashIcon,
|
TrashIcon,
|
||||||
ArrowLeftIcon,
|
ArrowLeftIcon,
|
||||||
MapPinIcon,
|
MapPinIcon,
|
||||||
SwatchIcon
|
SwatchIcon,
|
||||||
|
PencilIcon
|
||||||
} from '@heroicons/react/24/outline';
|
} from '@heroicons/react/24/outline';
|
||||||
import { propertiesAPI } from '../../services/api';
|
import { propertiesAPI } from '../../services/api';
|
||||||
import LoadingSpinner from '../../components/UI/LoadingSpinner';
|
import LoadingSpinner from '../../components/UI/LoadingSpinner';
|
||||||
@@ -54,10 +55,19 @@ function PolygonDrawer({ isDrawing, onPolygonComplete, currentColor }) {
|
|||||||
if (!isDrawing) return;
|
if (!isDrawing) return;
|
||||||
|
|
||||||
const newPoint = [e.latlng.lat, e.latlng.lng];
|
const newPoint = [e.latlng.lat, e.latlng.lng];
|
||||||
setCurrentPolygon(prev => [...prev, newPoint]);
|
console.log('Adding point:', newPoint);
|
||||||
|
setCurrentPolygon(prev => {
|
||||||
|
const newPoly = [...prev, newPoint];
|
||||||
|
console.log('Current polygon has', newPoly.length, 'points');
|
||||||
|
return newPoly;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
dblclick() {
|
dblclick(e) {
|
||||||
|
console.log('Double click detected, drawing:', isDrawing, 'points:', currentPolygon.length);
|
||||||
if (isDrawing && currentPolygon.length >= 3) {
|
if (isDrawing && currentPolygon.length >= 3) {
|
||||||
|
e.originalEvent.preventDefault();
|
||||||
|
e.originalEvent.stopPropagation();
|
||||||
|
console.log('Completing polygon with', currentPolygon.length, 'points');
|
||||||
onPolygonComplete(currentPolygon);
|
onPolygonComplete(currentPolygon);
|
||||||
setCurrentPolygon([]);
|
setCurrentPolygon([]);
|
||||||
}
|
}
|
||||||
@@ -89,11 +99,30 @@ const PropertyDetail = () => {
|
|||||||
const [showNameModal, setShowNameModal] = useState(false);
|
const [showNameModal, setShowNameModal] = useState(false);
|
||||||
const [pendingSection, setPendingSection] = useState(null);
|
const [pendingSection, setPendingSection] = useState(null);
|
||||||
const [sectionName, setSectionName] = useState('');
|
const [sectionName, setSectionName] = useState('');
|
||||||
|
const [editingSection, setEditingSection] = useState(null);
|
||||||
|
const [showEditModal, setShowEditModal] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchPropertyDetails();
|
fetchPropertyDetails();
|
||||||
}, [id]); // eslint-disable-line react-hooks/exhaustive-deps
|
}, [id]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
|
// Handle keyboard shortcuts for polygon drawing
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyPress = (e) => {
|
||||||
|
if (isDrawing && e.key === 'Enter') {
|
||||||
|
// Find the PolygonDrawer component and complete the polygon
|
||||||
|
console.log('Enter pressed during drawing mode');
|
||||||
|
}
|
||||||
|
if (e.key === 'Escape' && isDrawing) {
|
||||||
|
setIsDrawing(false);
|
||||||
|
toast.info('Drawing cancelled');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('keydown', handleKeyPress);
|
||||||
|
return () => document.removeEventListener('keydown', handleKeyPress);
|
||||||
|
}, [isDrawing]);
|
||||||
|
|
||||||
const fetchPropertyDetails = async () => {
|
const fetchPropertyDetails = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -110,12 +139,14 @@ const PropertyDetail = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handlePolygonComplete = (coordinates) => {
|
const handlePolygonComplete = (coordinates) => {
|
||||||
|
console.log('handlePolygonComplete called with', coordinates.length, 'coordinates');
|
||||||
if (coordinates.length < 3) {
|
if (coordinates.length < 3) {
|
||||||
toast.error('Polygon needs at least 3 points');
|
toast.error('Polygon needs at least 3 points');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const area = calculateAreaInSqFt([...coordinates, coordinates[0]]);
|
const area = calculateAreaInSqFt([...coordinates, coordinates[0]]);
|
||||||
|
console.log('Calculated area:', area);
|
||||||
setPendingSection({ coordinates, color: currentColor, area });
|
setPendingSection({ coordinates, color: currentColor, area });
|
||||||
setShowNameModal(true);
|
setShowNameModal(true);
|
||||||
setIsDrawing(false);
|
setIsDrawing(false);
|
||||||
@@ -153,6 +184,34 @@ const PropertyDetail = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const startEditSection = (section) => {
|
||||||
|
setEditingSection(section);
|
||||||
|
setSectionName(section.name);
|
||||||
|
setCurrentColor(section.color);
|
||||||
|
setShowEditModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveEditedSection = () => {
|
||||||
|
if (!sectionName.trim()) {
|
||||||
|
toast.error('Please enter a section name');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedSection = {
|
||||||
|
...editingSection,
|
||||||
|
name: sectionName,
|
||||||
|
color: currentColor
|
||||||
|
};
|
||||||
|
|
||||||
|
setLawnSections(prev => prev.map(s => s.id === editingSection.id ? updatedSection : s));
|
||||||
|
toast.success('Section updated!');
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
setSectionName('');
|
||||||
|
setEditingSection(null);
|
||||||
|
setShowEditModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
const getTotalArea = () => {
|
const getTotalArea = () => {
|
||||||
return lawnSections.reduce((total, section) => total + section.area, 0);
|
return lawnSections.reduce((total, section) => total + section.area, 0);
|
||||||
};
|
};
|
||||||
@@ -231,7 +290,8 @@ const PropertyDetail = () => {
|
|||||||
<div style={{ height: '600px', width: '100%' }}>
|
<div style={{ height: '600px', width: '100%' }}>
|
||||||
<MapContainer
|
<MapContainer
|
||||||
center={mapCenter}
|
center={mapCenter}
|
||||||
zoom={hasValidCoordinates ? 19 : 13}
|
zoom={hasValidCoordinates ? 21 : 13}
|
||||||
|
maxZoom={23}
|
||||||
style={{ height: '100%', width: '100%' }}
|
style={{ height: '100%', width: '100%' }}
|
||||||
>
|
>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
@@ -260,13 +320,21 @@ const PropertyDetail = () => {
|
|||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<strong>{section.name}</strong><br />
|
<strong>{section.name}</strong><br />
|
||||||
{section.area.toLocaleString()} sq ft<br />
|
{section.area.toLocaleString()} sq ft<br />
|
||||||
|
<div className="flex gap-2 mt-2">
|
||||||
|
<button
|
||||||
|
onClick={() => startEditSection(section)}
|
||||||
|
className="text-blue-600 text-sm"
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => deleteLawnSection(section.id)}
|
onClick={() => deleteLawnSection(section.id)}
|
||||||
className="text-red-600 text-sm mt-2"
|
className="text-red-600 text-sm"
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</Popup>
|
</Popup>
|
||||||
</Polygon>
|
</Polygon>
|
||||||
))}
|
))}
|
||||||
@@ -282,9 +350,15 @@ const PropertyDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isDrawing && (
|
{isDrawing && (
|
||||||
<div className="p-4 bg-blue-50">
|
<div className="p-4 bg-blue-50 border-t">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-3 h-3 bg-blue-600 rounded-full animate-pulse"></div>
|
||||||
<p className="text-sm text-blue-800">
|
<p className="text-sm text-blue-800">
|
||||||
<strong>Drawing Mode:</strong> Click to add points. Double-click to complete polygon.
|
<strong>Drawing Mode Active:</strong> Click to add points. Double-click to complete polygon.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-blue-600 mt-1">
|
||||||
|
Need at least 3 points to create a section. Press ESC to cancel.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -350,6 +424,13 @@ const PropertyDetail = () => {
|
|||||||
<p className="text-xs text-gray-600">{section.area.toLocaleString()} sq ft</p>
|
<p className="text-xs text-gray-600">{section.area.toLocaleString()} sq ft</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex gap-1">
|
||||||
|
<button
|
||||||
|
onClick={() => startEditSection(section)}
|
||||||
|
className="text-blue-600 hover:text-blue-800"
|
||||||
|
>
|
||||||
|
<PencilIcon className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => deleteLawnSection(section.id)}
|
onClick={() => deleteLawnSection(section.id)}
|
||||||
className="text-red-600 hover:text-red-800"
|
className="text-red-600 hover:text-red-800"
|
||||||
@@ -357,6 +438,7 @@ const PropertyDetail = () => {
|
|||||||
<TrashIcon className="h-4 w-4" />
|
<TrashIcon className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -366,8 +448,8 @@ const PropertyDetail = () => {
|
|||||||
|
|
||||||
{/* Name Modal */}
|
{/* Name Modal */}
|
||||||
{showNameModal && (
|
{showNameModal && (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center" style={{ zIndex: 9999 }}>
|
||||||
<div className="bg-white rounded-lg p-6 w-96">
|
<div className="bg-white rounded-lg p-6 w-96 shadow-2xl">
|
||||||
<h3 className="text-lg font-semibold mb-4">Name Your Lawn Section</h3>
|
<h3 className="text-lg font-semibold mb-4">Name Your Lawn Section</h3>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<input
|
<input
|
||||||
@@ -404,6 +486,68 @@ const PropertyDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Edit Modal */}
|
||||||
|
{showEditModal && (
|
||||||
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center" style={{ zIndex: 9999 }}>
|
||||||
|
<div className="bg-white rounded-lg p-6 w-96 shadow-2xl">
|
||||||
|
<h3 className="text-lg font-semibold mb-4">Edit Lawn Section</h3>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label className="label">Section Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input"
|
||||||
|
value={sectionName}
|
||||||
|
onChange={(e) => setSectionName(e.target.value)}
|
||||||
|
placeholder="e.g., Front Yard, Back Lawn"
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="label">Color</label>
|
||||||
|
<div className="grid grid-cols-3 gap-2">
|
||||||
|
{SECTION_COLORS.map((color) => (
|
||||||
|
<button
|
||||||
|
key={color.value}
|
||||||
|
onClick={() => setCurrentColor(color)}
|
||||||
|
className={`w-10 h-10 rounded border-2 ${
|
||||||
|
currentColor.value === color.value ? 'border-gray-900' : 'border-gray-300'
|
||||||
|
}`}
|
||||||
|
style={{ backgroundColor: color.value }}
|
||||||
|
title={color.name}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2 text-sm text-gray-600">
|
||||||
|
<div
|
||||||
|
className="w-4 h-4 rounded"
|
||||||
|
style={{ backgroundColor: currentColor.value }}
|
||||||
|
/>
|
||||||
|
<span>{editingSection?.area.toLocaleString()} sq ft</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-3 mt-6">
|
||||||
|
<button onClick={saveEditedSection} className="btn-primary flex-1">
|
||||||
|
Save Changes
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setShowEditModal(false);
|
||||||
|
setEditingSection(null);
|
||||||
|
setSectionName('');
|
||||||
|
}}
|
||||||
|
className="btn-secondary flex-1"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user