seed stuff

This commit is contained in:
Jake Kasper
2025-09-03 10:56:17 -04:00
parent 6dbdba0e38
commit e7cbaf844f
8 changed files with 271 additions and 28 deletions

View File

@@ -19,6 +19,52 @@ import LoadingSpinner from '../../components/UI/LoadingSpinner';
import toast from 'react-hot-toast';
import 'leaflet/dist/leaflet.css';
// Simple tag input and suggestion components for grass types
const COOL_SEASON_GRASSES = [
'Turf Type Tall Fescue',
'Kentucky Bluegrass',
'Perennial Ryegrass',
'Fine Fescue',
'Creeping Red Fescue',
'Chewings Fescue',
'Hard Fescue',
'Annual Ryegrass'
];
const TagInput = ({ value = [], onChange }) => {
const [input, setInput] = useState('');
const add = (val) => { const v = val.trim(); if (!v) return; if (!value.includes(v)) onChange([...(value||[]), v]); setInput(''); };
return (
<div className="border rounded p-2">
<div className="flex flex-wrap gap-2 mb-2">
{(value||[]).map((t) => (
<span key={t} className="px-2 py-1 bg-gray-100 rounded text-xs flex items-center gap-1">
{t}
<button className="text-gray-500 hover:text-gray-700" onClick={() => onChange((value||[]).filter(x=>x!==t))}>×</button>
</span>
))}
</div>
<input
className="w-full border-0 focus:outline-none text-sm"
placeholder="Type and press Enter to add"
value={input}
onChange={(e)=> setInput(e.target.value)}
onKeyDown={(e)=> { if (e.key==='Enter'){ e.preventDefault(); add(input); } }}
/>
</div>
);
};
const SuggestionChips = ({ onPick }) => (
<div className="flex flex-wrap gap-2 mt-2">
{COOL_SEASON_GRASSES.map(g => (
<button key={g} type="button" onClick={()=> onPick(g)} className="px-2 py-1 bg-blue-50 hover:bg-blue-100 text-blue-700 rounded text-xs">
{g}
</button>
))}
</div>
);
// Fix for default markers
delete Icon.Default.prototype._getIconUrl;
Icon.Default.mergeOptions({
@@ -302,8 +348,10 @@ const PropertyDetail = () => {
const [showNameModal, setShowNameModal] = useState(false);
const [pendingSection, setPendingSection] = useState(null);
const [sectionName, setSectionName] = useState('');
const [sectionGrassTypes, setSectionGrassTypes] = useState([]);
const [editingSection, setEditingSection] = useState(null);
const [showEditModal, setShowEditModal] = useState(false);
const [editGrassTypes, setEditGrassTypes] = useState([]);
// Recent history state for this property
const [completedApplications, setCompletedApplications] = useState([]);
@@ -376,7 +424,9 @@ const PropertyDetail = () => {
name: section.name,
coordinates: section.polygonData?.coordinates?.[0] || [],
color: section.polygonData?.color || SECTION_COLORS[0],
area: section.area
area: section.area,
grassType: section.grassType || '',
grassTypes: section.grassTypes || null
}));
setLawnSections(convertedSections);
}
@@ -417,7 +467,8 @@ const PropertyDetail = () => {
coordinates: [pendingSection.coordinates],
color: pendingSection.color
},
grassType: null,
grassType: sectionGrassTypes.join(', '),
grassTypes: sectionGrassTypes,
soilType: null
};
@@ -438,6 +489,7 @@ const PropertyDetail = () => {
// Reset and cycle color
setSectionName('');
setSectionGrassTypes([]);
setPendingSection(null);
setShowNameModal(false);
const nextIndex = (SECTION_COLORS.findIndex(c => c.value === currentColor.value) + 1) % SECTION_COLORS.length;
@@ -466,6 +518,8 @@ const PropertyDetail = () => {
setSectionName(section.name);
setCurrentColor(section.color);
setShowEditModal(true);
const existing = (section.grassType || '').split(',').map(s=>s.trim()).filter(Boolean);
setEditGrassTypes(existing);
};
const saveEditedSection = async () => {
@@ -482,7 +536,8 @@ const PropertyDetail = () => {
coordinates: [editingSection.coordinates],
color: currentColor
},
grassType: null,
grassType: editGrassTypes.join(', '),
grassTypes: editGrassTypes,
soilType: null
};
@@ -501,6 +556,7 @@ const PropertyDetail = () => {
setSectionName('');
setEditingSection(null);
setShowEditModal(false);
setEditGrassTypes([]);
} catch (error) {
console.error('Failed to update section:', error);
toast.error('Failed to update section. Please try again.');
@@ -516,7 +572,8 @@ const PropertyDetail = () => {
coordinates: [updatedSection.coordinates],
color: updatedSection.color
},
grassType: null,
grassType: (updatedSection.grassType || ''),
grassTypes: (updatedSection.grassTypes || null),
soilType: null
};
@@ -767,6 +824,13 @@ const PropertyDetail = () => {
<div>
<p className="font-medium text-sm">{section.name}</p>
<p className="text-xs text-gray-600">{section.area.toLocaleString()} sq ft</p>
{(section.grassTypes && section.grassTypes.length > 0) || section.grassType ? (
<div className="flex flex-wrap gap-1 mt-1">
{((section.grassTypes && section.grassTypes.length > 0) ? section.grassTypes : (section.grassType||'').split(',').map(s=>s.trim()).filter(Boolean)).map((g, idx)=>(
<span key={idx} className="px-1.5 py-0.5 bg-green-100 text-green-800 rounded text-[10px]">{g}</span>
))}
</div>
) : null}
</div>
</div>
<div className="flex gap-1">
@@ -901,6 +965,14 @@ const PropertyDetail = () => {
/>
<span>{pendingSection?.area.toLocaleString()} sq ft</span>
</div>
<div>
<label className="label">Grass Types</label>
<TagInput
value={sectionGrassTypes}
onChange={setSectionGrassTypes}
/>
<SuggestionChips onPick={(v)=> setSectionGrassTypes(prev=> prev.includes(v)? prev : [...prev, v])} />
</div>
</div>
<div className="flex gap-3 mt-6">
<button onClick={saveLawnSection} className="btn-primary flex-1">
@@ -911,6 +983,7 @@ const PropertyDetail = () => {
setShowNameModal(false);
setPendingSection(null);
setSectionName('');
setSectionGrassTypes([]);
}}
className="btn-secondary flex-1"
>
@@ -955,6 +1028,14 @@ const PropertyDetail = () => {
))}
</div>
</div>
<div>
<label className="label">Grass Types</label>
<TagInput
value={editGrassTypes}
onChange={setEditGrassTypes}
/>
<SuggestionChips onPick={(v)=> setEditGrassTypes(prev=> prev.includes(v)? prev : [...prev, v])} />
</div>
<div className="flex items-center gap-2 text-sm text-gray-600">
<div
@@ -973,6 +1054,7 @@ const PropertyDetail = () => {
setShowEditModal(false);
setEditingSection(null);
setSectionName('');
setEditGrassTypes([]);
}}
className="btn-secondary flex-1"
>