({ coordinates, mapToken, existingFieldColors, onSave, onCancel }: NewFieldDialogProps)
| 76 | } |
| 77 | |
| 78 | const NewFieldDialog = ({ coordinates, mapToken, existingFieldColors, onSave, onCancel }: NewFieldDialogProps) => { |
| 79 | const [name, setName] = useState(""); |
| 80 | const [regionType, setRegionType] = useState<"rural" | "urban">("rural"); |
| 81 | const [detecting, setDetecting] = useState(true); |
| 82 | const [guessingCrop, setGuessingCrop] = useState(false); |
| 83 | const [crop, setCrop] = useState("Wheat"); |
| 84 | const [urbanLandUse, setUrbanLandUse] = useState("Residential"); |
| 85 | const [color, setColor] = useState(() => getNextAutoColor(existingFieldColors || [])); |
| 86 | const [group, setGroup] = useState(""); |
| 87 | const [location, setLocation] = useState(""); |
| 88 | const [cropSearch, setCropSearch] = useState(""); |
| 89 | const [showCropDropdown, setShowCropDropdown] = useState(false); |
| 90 | |
| 91 | const estimatedAcres = calculateAreaAcres(coordinates); |
| 92 | const estimatedHa = Math.round((estimatedAcres / 2.47105) * 10) / 10; |
| 93 | |
| 94 | // Auto-detect region type using GEE land use + reverse geocode (server-proxied) |
| 95 | useEffect(() => { |
| 96 | const detect = async () => { |
| 97 | setDetecting(true); |
| 98 | |
| 99 | const center = coordinates.reduce( |
| 100 | (acc, c) => [acc[0] + c[0] / coordinates.length, acc[1] + c[1] / coordinates.length], [0, 0] |
| 101 | ); |
| 102 | |
| 103 | // Parallel: reverse geocode (via edge proxy) + GEE land use |
| 104 | const geocodePromise = callBackend("mapbox-geocode", { mode: "reverse", lng: center[0], lat: center[1], limit: 1 }) |
| 105 | .then(r => r.data).catch(() => null); |
| 106 | |
| 107 | const geePromise = callBackend("gee-analytics", { polygon: coordinates, analyses: ["land_use"] }).then(r => r.data).catch(() => null); |
| 108 | |
| 109 | const [geoData, geeData] = await Promise.all([geocodePromise, geePromise]); |
| 110 | |
| 111 | // Set location from geocode |
| 112 | if (geoData?.features?.[0]) { |
| 113 | setLocation(geoData.features[0].place_name); |
| 114 | } |
| 115 | |
| 116 | // Determine region type from GEE land use data |
| 117 | let isUrban = false; |
| 118 | if (geeData?.land_use) { |
| 119 | const builtUp = geeData.land_use["Built-up"] || 0; |
| 120 | isUrban = builtUp >= 30; |
| 121 | } else if (geoData?.features?.[0]) { |
| 122 | // Fallback: check Mapbox place type |
| 123 | const placeType = geoData.features[0].place_type?.[0] || ""; |
| 124 | const placeName = (geoData.features[0].place_name || "").toLowerCase(); |
| 125 | isUrban = ["place", "locality", "neighborhood", "address"].includes(placeType) || |
| 126 | ["city", "town", "metro", "urban", "suburb", "downtown"].some(k => placeName.includes(k)); |
| 127 | } |
| 128 | |
| 129 | setRegionType(isUrban ? "urban" : "rural"); |
| 130 | setDetecting(false); |
| 131 | |
| 132 | if (!isUrban) { |
| 133 | setGuessingCrop(true); |
| 134 | try { |
| 135 | const cropNames = CROP_OPTIONS.map(c => c.name); |
nothing calls this directly
no test coverage detected