diff --git a/Harvest.Web/ClientApp/package-lock.json b/Harvest.Web/ClientApp/package-lock.json index df6ab852a..30ee69c84 100644 --- a/Harvest.Web/ClientApp/package-lock.json +++ b/Harvest.Web/ClientApp/package-lock.json @@ -8,6 +8,7 @@ "name": "harvest.web", "version": "0.1.0", "dependencies": { + "@arcgis/core": "^4.28.1", "@azure/storage-blob": "^12.14.0", "@david.kucsai/react-pdf-table": "^0.4.1", "@fortawesome/fontawesome-svg-core": "^1.2.36", @@ -106,6 +107,21 @@ "ajv": ">=8" } }, + "node_modules/@arcgis/core": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@arcgis/core/-/core-4.28.1.tgz", + "integrity": "sha512-Pcaa0cZz/vwtFcGwUkZSfH92KHRb3vNJDz7czPjRnq19RncitxgIw0McPkgrJzDUeqGuAqpJR8qR+ERmM/ON8Q==", + "dependencies": { + "@esri/arcgis-html-sanitizer": "~3.0.1", + "@esri/calcite-colors": "~6.1.0", + "@esri/calcite-components": "^1.9.2", + "@popperjs/core": "~2.11.8", + "@zip.js/zip.js": "~2.7.29", + "focus-trap": "~7.5.3", + "luxon": "~3.4.3", + "sortablejs": "~1.15.0" + } + }, "node_modules/@azure/abort-controller": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.4.tgz", @@ -2683,6 +2699,67 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@esri/arcgis-html-sanitizer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@esri/arcgis-html-sanitizer/-/arcgis-html-sanitizer-3.0.1.tgz", + "integrity": "sha512-cwZJwsYCJZwtBQU2AmaiIVFg5nZcVwInPYja1/OgC9iKYO+ytZRoc5h+0S9/ygbFNoS8Nd0RX9A85stLX/BgiA==", + "dependencies": { + "xss": "1.0.13" + } + }, + "node_modules/@esri/calcite-colors": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@esri/calcite-colors/-/calcite-colors-6.1.0.tgz", + "integrity": "sha512-wHQYWFtDa6c328EraXEVZvgOiaQyYr0yuaaZ0G3cB4C3lSkWefW34L/e5TLAhtuG3zJ/wR6pl5X1YYNfBc0/4Q==" + }, + "node_modules/@esri/calcite-components": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@esri/calcite-components/-/calcite-components-1.9.2.tgz", + "integrity": "sha512-nzur8vPVRsju+9ELFjun/HKLMQxrQiDLOhmDtfkizmK9SxZ29djI5UPuyAQim2PsgHslo2dHP0FVdaPyOK5vLA==", + "dependencies": { + "@floating-ui/dom": "1.5.3", + "@stencil/core": "2.22.3", + "@types/color": "3.0.4", + "color": "4.2.3", + "composed-offset-position": "0.0.4", + "dayjs": "1.11.10", + "focus-trap": "7.5.2", + "form-request-submit-polyfill": "2.0.0", + "lodash-es": "4.17.21", + "sortablejs": "1.15.0", + "timezone-groups": "0.8.0" + } + }, + "node_modules/@esri/calcite-components/node_modules/focus-trap": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.2.tgz", + "integrity": "sha512-p6vGNNWLDGwJCiEjkSK6oERj/hEyI9ITsSwIUICBoKLlWiTWXJRfQibCwcoi50rTZdbi87qDtUlMCmQwsGSgPw==", + "dependencies": { + "tabbable": "^6.2.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", + "integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==", + "dependencies": { + "@floating-ui/utils": "^0.1.3" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "dependencies": { + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", + "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" + }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "0.2.36", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", @@ -3398,9 +3475,9 @@ } }, "node_modules/@popperjs/core": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.1.tgz", - "integrity": "sha512-HnUhk1Sy9IuKrxEMdIRCxpIqPw6BFsbYSEUO9p/hNw5sMld/+3OLMWQP80F8/db9qsv3qUjs7ZR5bS/R+iinXw==", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -3719,6 +3796,18 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@stencil/core": { + "version": "2.22.3", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.22.3.tgz", + "integrity": "sha512-kmVA0M/HojwsfkeHsifvHVIYe4l5tin7J5+DLgtl8h6WWfiMClND5K3ifCXXI2ETDNKiEk21p6jql3Fx9o2rng==", + "bin": { + "stencil": "bin/stencil" + }, + "engines": { + "node": ">=12.10.0", + "npm": ">=6.0.0" + } + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -4105,6 +4194,27 @@ "@types/node": "*" } }, + "node_modules/@types/color": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.4.tgz", + "integrity": "sha512-OpisS4bqJJwbkkQRrMvURf3DOxBoAg9mysHYI7WgrWpSYHqHGKYBULHdz4ih77SILcLDo/zyHGFyfIl9yb8NZQ==", + "dependencies": { + "@types/color-convert": "*" + } + }, + "node_modules/@types/color-convert": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-2.0.2.tgz", + "integrity": "sha512-KGRIgCxwcgazts4MXRCikPbIMzBpjfdgEZSy8TRHU/gtg+f9sOfHdtK8unPfxIoBtyd2aTTwINVLSNENlC8U8A==", + "dependencies": { + "@types/color-name": "*" + } + }, + "node_modules/@types/color-name": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.2.tgz", + "integrity": "sha512-JWO/ZyxTKk0bLuOhAavGjnwLR73rUE7qzACnU7gMeyA/gdrSHm2xJwqNPipw2MtaZUaqQ2UG/q7pP6AQiZ8mqw==" + }, "node_modules/@types/connect": { "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", @@ -4942,6 +5052,16 @@ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, + "node_modules/@zip.js/zip.js": { + "version": "2.7.30", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.30.tgz", + "integrity": "sha512-nhMvQCj+TF1ATBqYzFds7v+yxPBhdDYHh8J341KtC1D2UrVBUIYcYK4Jy1/GiTsxOXEiKOXSUxvPG/XR+7jMqw==", + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=16.5.0" + } + }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -6206,6 +6326,18 @@ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -6278,6 +6410,11 @@ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, + "node_modules/composed-offset-position": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/composed-offset-position/-/composed-offset-position-0.0.4.tgz", + "integrity": "sha512-vMlvu1RuNegVE0YsCDSV/X4X10j56mq7PCIyOKK74FxkXzGLwhOUmdkJLSdOBOMwWycobGUMgft2lp+YgTe8hw==" + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -6769,6 +6906,11 @@ "node": ">=4" } }, + "node_modules/cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" + }, "node_modules/cssfontparser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz", @@ -6940,6 +7082,11 @@ "url": "https://opencollective.com/date-fns" } }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "node_modules/debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", @@ -8635,6 +8782,14 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, + "node_modules/focus-trap": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", + "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", + "dependencies": { + "tabbable": "^6.2.0" + } + }, "node_modules/follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -8806,6 +8961,11 @@ "node": ">= 6" } }, + "node_modules/form-request-submit-polyfill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/form-request-submit-polyfill/-/form-request-submit-polyfill-2.0.0.tgz", + "integrity": "sha512-p0+M92y2gFnP0AuuL8VJ0GYVzAT0bYp3GsSkmPFhvUopdnfDLP/9xplQTBBc4w8qOjKRzdK7GaFcdL9IhlXdTQ==" + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -11809,6 +11969,14 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.3.tgz", + "integrity": "sha512-tFWBiv3h7z+T/tDaoxA8rqTxy1CHV6gHS//QdaH4pulbq/JuBSGgQspQQqcgnwdAx6pNI7cmvz5Sv/addzHmUg==", + "engines": { + "node": ">=12" + } + }, "node_modules/lz-string": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", @@ -15718,6 +15886,11 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sortablejs": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz", + "integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==" + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -16263,6 +16436,11 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, "node_modules/tailwindcss": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", @@ -16488,6 +16666,14 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, + "node_modules/timezone-groups": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/timezone-groups/-/timezone-groups-0.8.0.tgz", + "integrity": "sha512-t7E/9sPfCU0m0ZbS7Cqw52D6CB/UyeaiIBmyJCokI1SyOyOgA/ESiQ/fbreeFaUG9QSenGlZSSk/7rEbkipbOA==", + "bin": { + "timezone-groups": "dist/cli.cjs" + } + }, "node_modules/tiny-inflate": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", @@ -17864,6 +18050,26 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, + "node_modules/xss": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.13.tgz", + "integrity": "sha512-clu7dxTm1e8Mo5fz3n/oW3UCXBfV89xZ72jM8yzo1vR/pIS0w3sgB3XV2H8Vm6zfGnHL0FzvLJPJEBhd86/z4Q==", + "dependencies": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + }, + "bin": { + "xss": "bin/xss" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/xss/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/Harvest.Web/ClientApp/package.json b/Harvest.Web/ClientApp/package.json index 0444ac77c..6519ff71f 100644 --- a/Harvest.Web/ClientApp/package.json +++ b/Harvest.Web/ClientApp/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@arcgis/core": "^4.28.1", "@azure/storage-blob": "^12.14.0", "@david.kucsai/react-pdf-table": "^0.4.1", "@fortawesome/fontawesome-svg-core": "^1.2.36", diff --git a/Harvest.Web/ClientApp/src/App.tsx b/Harvest.Web/ClientApp/src/App.tsx index 7bf35b57d..d1bd53899 100644 --- a/Harvest.Web/ClientApp/src/App.tsx +++ b/Harvest.Web/ClientApp/src/App.tsx @@ -40,7 +40,10 @@ function App() { -
+
{/* Match any server-side routes and send empty content to let MVC return the view details */} { - return ( -
- - - - - - - - A pretty CSS3 popup.
Easily customizable. -
-
-
-
- ); -}; diff --git a/Harvest.Web/ClientApp/src/Maps/MapEditReactState.tsx b/Harvest.Web/ClientApp/src/Maps/MapEditReactState.tsx new file mode 100644 index 000000000..d3672c3f6 --- /dev/null +++ b/Harvest.Web/ClientApp/src/Maps/MapEditReactState.tsx @@ -0,0 +1,196 @@ +import React, { useEffect, useRef, useState } from "react"; +import { Button, Input } from "reactstrap"; + +import WebMap from "@arcgis/core/WebMap"; +import MapView from "@arcgis/core/views/MapView"; +import FeatureLayer from "@arcgis/core/layers/FeatureLayer"; +import BasemapToggle from "@arcgis/core/widgets/BasemapToggle"; +import Graphic from "@arcgis/core/Graphic"; +import Polygon from "@arcgis/core/geometry/Polygon"; +import Layer from "@arcgis/core/layers/Layer"; + +// the attributes of the feature layer () +interface FieldAttributes { + name: string; // field name, not display name (i messed up the casing but you can't rename the field name) + Crop: string; + Details: string; +} + +// from: https://github.com/Esri/jsapi-resources/blob/main/esm-samples/jsapi-react/src/App.jsx +export const MapEditReactState = () => { + console.log("Map.tsx"); + const mapDiv = useRef(null); + const [features, setFeatures] = useState([]); + const [viewState, setViewState] = useState(); + + const [newAttributes, setNewAttributes] = useState({ + name: "New Attributes Test", + Crop: "Potato", + Details: "Created from React", + }); + + const addFeature = () => { + if (!viewState) { + console.error("View state not set"); + return; + } + const layer: Layer = viewState.map.findLayerById("fieldsLayer"); + if (!layer) { + console.error("Feature layer not found"); + return; + } + // have to be sure that the layer is a feature layer to do this + const featureLayer = layer as FeatureLayer; + + const newFeature = new Graphic({ + geometry: new Polygon({ + rings: [ + [ + [-121.80148, 38.568554], + [-121.80148, 38.568554 - 0.01], + [-121.80148 + 0.01, 38.568554 - 0.01], + [-121.80148 + 0.01, 38.568554], + [-121.80148, 38.568554], + ], + ], + spatialReference: { wkid: 4326 }, + }), + attributes: newAttributes, + }); + + featureLayer + .applyEdits({ addFeatures: [newFeature] }) + .then((res) => { + if (res.addFeatureResults.length > 0) { + console.log("Feature added successfully"); + newFeature.attributes.objectId = res.addFeatureResults[0].objectId; + setFeatures([...features, newFeature]); + } + }) + .catch((error) => { + console.error("Error adding feature: ", error); + }); + }; + + const deleteFeature = () => { + if (!viewState) { + console.error("View state not set"); + return; + } + const layer: Layer = viewState.map.findLayerById("fieldsLayer"); + if (!layer) { + console.error("Feature layer not found"); + return; + } + // have to be sure that the layer is a feature layer to do this + const featureLayer = layer as FeatureLayer; + + // just delete the most recent feature for now + const feature = features[features.length - 1]; + + featureLayer + .applyEdits({ deleteFeatures: [feature] }) + .then((res) => { + if (res.deleteFeatureResults.length > 0) { + console.log("Feature deleted successfully"); + const featureIndex = features.findIndex( + (f) => f.getObjectId() === feature.getObjectId() + ); + setFeatures(features.splice(featureIndex, 1)); + } + }) + .catch((error) => { + console.error("Error deleting feature: ", error); + }); + }; + + useEffect(() => { + if (mapDiv.current) { + /** + * Initialize application + */ + // https://developers.arcgis.com/javascript/latest/api-reference/esri-WebMap.html + const webmap = new WebMap({ + portalItem: { + // the unique id for your map + // you can access this by going to arcgis -> content -> my content -> map and copy the id on the right (or from url) + id: "8e276b0a2b6b49b88a1ea459d91f5fd3", + }, + basemap: "hybrid", // https://developers.arcgis.com/rest/basemap-styles/#arcgis-styles + }); + + const view = new MapView({ + container: mapDiv.current, + map: webmap, + center: [-121.748, 38.54], + zoom: 13, + }); + setViewState(view); + + const featureLayer = new FeatureLayer({ + id: "fieldsLayer", + portalItem: { + // the feature layer is not added to the map in ArcGIS Online, but is overlayed here + id: "66289d4facfb4932a5b1d91db8792c4f", + }, + }); + webmap.add(featureLayer); + + // https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-BasemapToggle.html + let basemapToggle = new BasemapToggle({ + view: view, + nextBasemap: "osm", // Allows for toggling to the openstreetmaps (non-satellite ) + }); + view.ui.add(basemapToggle, "top-left"); + + // once the whole webmap is loaded + webmap.when(() => { + if (webmap.allLayers && webmap.allLayers.length) { + console.log("All layers: ", webmap.allLayers.length); + } else { + console.log("No layers in this webmap"); + } + if (webmap.allTables && webmap.allTables.length) { + console.log("All tables: ", webmap.allTables.length); + } else { + console.log("No tables in this webmap"); + } + }); + + featureLayer + .queryFeatures() + .then((featureSet) => { + console.log("features", featureSet.features); + // then we can do things like: + // console.log( + // "featureSet.features[0].attributes: ", + // featureSet.features[0].attributes + // ); + // and get: + // { + // Crop: "Cabbage", + // Details: null, + // OBJECTID: 2, + // Shape__Area: 176921.81640625, + // Shape__Length: 1728.6668691192322, + // name: "cabbages" + // } + + // and also: + setFeatures(featureSet.features); + }) + .catch((error) => { + console.error("Error querying features: ", error); + }); + } + }, [mapDiv, newAttributes]); + + return ( + <> + + + +
+ + ); +}; diff --git a/Harvest.Web/ClientApp/src/Maps/MapEditWidget.tsx b/Harvest.Web/ClientApp/src/Maps/MapEditWidget.tsx new file mode 100644 index 000000000..edc3e821e --- /dev/null +++ b/Harvest.Web/ClientApp/src/Maps/MapEditWidget.tsx @@ -0,0 +1,159 @@ +import React, { useEffect, useRef, useState } from "react"; +import WebMap from "@arcgis/core/WebMap"; +import MapView from "@arcgis/core/views/MapView"; +import Editor from "@arcgis/core/widgets/Editor"; +import FieldElement from "@arcgis/core/form/elements/FieldElement.js"; +import FormTemplate from "@arcgis/core/form/FormTemplate.js"; +import CodedValueDomain from "@arcgis/core/layers/support/CodedValueDomain.js"; + +import FeatureLayer from "@arcgis/core/layers/FeatureLayer"; +import BasemapToggle from "@arcgis/core/widgets/BasemapToggle"; + +// from: https://github.com/Esri/jsapi-resources/blob/main/esm-samples/jsapi-react/src/App.jsx +export const MapEditWidget = () => { + console.log("Map.tsx"); + const mapDiv = useRef(null); + + useEffect(() => { + if (mapDiv.current) { + /** + * Initialize application + */ + // https://developers.arcgis.com/javascript/latest/api-reference/esri-WebMap.html + const webmap = new WebMap({ + portalItem: { + // the unique id for your map + // you can access this by going to arcgis -> content -> my content -> map and copy the id on the right (or from url) + id: "8e276b0a2b6b49b88a1ea459d91f5fd3", + }, + basemap: "hybrid", // https://developers.arcgis.com/rest/basemap-styles/#arcgis-styles + }); + + const view = new MapView({ + container: mapDiv.current, + map: webmap, + center: [-121.748, 38.54], + zoom: 13, + }); + + const featureLayer = new FeatureLayer({ + portalItem: { + // the feature layer is not added to the map in ArcGIS Online, but is overlayed here + id: "66289d4facfb4932a5b1d91db8792c4f", + }, + }); + webmap.add(featureLayer); + + // https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-BasemapToggle.html + let basemapToggle = new BasemapToggle({ + view: view, + nextBasemap: "osm", // Allows for toggling to the openstreetmaps (non-satellite ) + }); + view.ui.add(basemapToggle, "top-left"); + + view.when(() => { + // when our view is loaded + const editor = new Editor({ + view: view, + label: "LABEL", + layerInfos: [ + { + layer: featureLayer, + formTemplate: new FormTemplate({ + description: "", + // expressionInfos: [], + // preserveFieldValuesWhenHidden: false, + title: "Edit Field Details", + elements: [ + // was having trouble with the autocast so this is my workaround + new FieldElement({ + fieldName: "name", // fieldName has to match the case of the field name in the feature layer + label: "Name", // you can check that at Content -> select feature layer -> Data (at top) -> fields + }), + new FieldElement({ + fieldName: "Crop", + label: "Crop", + domain: new CodedValueDomain({ + // dropdown list of available crop values + // https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-support-CodedValueDomain.html + // doesnt look like this supports dynamic values, but it's currently a dropdown anyways + codedValues: [ + { + name: "Potato", + code: "potato", + }, + { + name: "Tomato", + code: "Tomato", + }, + { + name: "Corn", + code: "Corn", + }, + { + name: "Celery", + code: "Celery", + }, + { + name: "Carrot", + code: "Carrot", + }, + ], + }), + }), + new FieldElement({ + fieldName: "Details", + label: "Details", + }), + ], + }), + }, + ], + }); + + // Add the widget to the view + view.ui.add(editor, "top-right"); + }); + + // once the whole webmap is loaded + webmap.when(() => { + if (webmap.allLayers && webmap.allLayers.length) { + console.log("All layers: ", webmap.allLayers.length); + } else { + console.log("No layers in this webmap"); + } + if (webmap.allTables && webmap.allTables.length) { + console.log("All tables: ", webmap.allTables.length); + } else { + console.log("No tables in this webmap"); + } + + // query the features once webmap is loaded + featureLayer + .queryFeatures() + .then((featureSet) => { + console.log("features", featureSet.features); + // then we can do things like: + console.log( + "featureSet.features[0].attributes: ", + featureSet.features[0].attributes + ); + // and get: + // { + // Crop: "Cabbage", + // Details: null, + // OBJECTID: 2, + // Shape__Area: 176921.81640625, + // Shape__Length: 1728.6668691192322, + // name: "cabbages" + // } + }) + .catch((error) => { + console.error("Error querying features: ", error); + }); + }); + } + }, [mapDiv]); + + return
; +}; diff --git a/Harvest.Web/ClientApp/src/Maps/MapSketchWidget.tsx b/Harvest.Web/ClientApp/src/Maps/MapSketchWidget.tsx new file mode 100644 index 000000000..0230709f2 --- /dev/null +++ b/Harvest.Web/ClientApp/src/Maps/MapSketchWidget.tsx @@ -0,0 +1,90 @@ +import React, { useEffect, useRef, useState } from "react"; +import WebMap from "@arcgis/core/WebMap"; +import MapView from "@arcgis/core/views/MapView"; +import FeatureLayer from "@arcgis/core/layers/FeatureLayer"; +import BasemapToggle from "@arcgis/core/widgets/BasemapToggle"; +import Sketch from "@arcgis/core/widgets/Sketch"; +import GraphicsLayer from "@arcgis/core/layers/GraphicsLayer"; +import Graphic from "@arcgis/core/Graphic.js"; +import Collection from "@arcgis/core/core/Collection.js"; +import SketchViewModel from "@arcgis/core/widgets/Sketch/SketchViewModel.js"; +import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js"; + +// here i'm trying to use the sketch widget to draw a polygon on the map +// and then use the reactiveUtils.watch to watch the graphicsLayer.graphics +// doesn't work yet (and i think we'd use the Edit Widget instead) +// but i'm interested in the reactiveUtils.watch function + +// from: https://github.com/Esri/jsapi-resources/blob/main/esm-samples/jsapi-react/src/App.jsx +export const MapSketchWidget = () => { + console.log("Map.tsx"); + const mapDiv = useRef(null); + + const [graphics, setGraphics] = useState>( + new Collection() + ); + + useEffect(() => { + if (mapDiv.current) { + /** + * Initialize application + */ + + // https://developers.arcgis.com/javascript/latest/api-reference/esri-WebMap.html + const webmap = new WebMap({ + portalItem: { + // the unique id for your map + // you can access this by going to arcgis -> content -> my content -> map and copy the id on the right (or from url) + id: "8e276b0a2b6b49b88a1ea459d91f5fd3", + }, + basemap: "hybrid", // https://developers.arcgis.com/rest/basemap-styles/#arcgis-styles + }); + + const view = new MapView({ + container: mapDiv.current, + map: webmap, + center: [-121.748, 38.54], + zoom: 13, + }); + + const featureLayer = new FeatureLayer({ + portalItem: { + id: "66289d4facfb4932a5b1d91db8792c4f", + }, + }); + webmap.add(featureLayer); + + // https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-BasemapToggle.html + let basemapToggle = new BasemapToggle({ + view: view, // The view that provides access to the map's "streets-vector" basemap + nextBasemap: "osm", // Allows for toggling to the "osm" basemap + }); + view.ui.add(basemapToggle, "top-left"); + + const graphicsLayer = new GraphicsLayer(); + const sketch = new Sketch({ + layer: graphicsLayer, + view: view, + // graphic will be selected as soon as it is created + creationMode: "update", + }); + view.ui.add(sketch, "top-right"); + + // https://developers.arcgis.com/javascript/latest/api-reference/esri-core-reactiveUtils.html#watch + const handle = reactiveUtils.watch( + // getValue function + () => graphicsLayer.graphics, + // Callback function + (newValue, oldValue) => { + console.log("New value: ", newValue, "Old value: ", oldValue); + }, + // Optional parameters + { + // initial: true, + } + ); + } + }, [mapDiv]); + + return
; +}; diff --git a/Harvest.Web/ClientApp/src/Quotes/QuoteContainer.tsx b/Harvest.Web/ClientApp/src/Quotes/QuoteContainer.tsx index 82fca4f2c..0702eab67 100644 --- a/Harvest.Web/ClientApp/src/Quotes/QuoteContainer.tsx +++ b/Harvest.Web/ClientApp/src/Quotes/QuoteContainer.tsx @@ -16,6 +16,8 @@ import { ProjectDetail } from "./ProjectDetail"; import { ProjectHeader } from "../Shared/ProjectHeader"; import { ActivitiesContainer } from "./ActivitiesContainer"; import { QuoteTotals } from "./QuoteTotals"; +import { MapEditWidget } from "../Maps/MapEditWidget"; +import { MapSketchWidget } from "../Maps/MapSketchWidget"; import { authenticatedFetch } from "../Util/Api"; import { usePromiseNotification } from "../Util/Notifications"; @@ -26,6 +28,7 @@ import { useIsMounted } from "../Shared/UseIsMounted"; import { ShowFor } from "../Shared/ShowFor"; import { validatorOptions } from "../constants"; import { Button } from "reactstrap"; +import { MapEditReactState } from "../Maps/MapEditReactState"; export const QuoteContainer = () => { const history = useHistory(); @@ -236,14 +239,14 @@ export const QuoteContainer = () => { // TODO: we might want to move this all into a separate component if (editFields) { return ( -
-
+
+
-
+
@@ -274,14 +277,17 @@ export const QuoteContainer = () => {
-
- + {/* */} + {/* */} + {/* setQuote({ ...quote, fields })} - > + > */} +
); diff --git a/Harvest.Web/ClientApp/src/sass/harvest.scss b/Harvest.Web/ClientApp/src/sass/harvest.scss index 2f66f692c..c30316780 100644 --- a/Harvest.Web/ClientApp/src/sass/harvest.scss +++ b/Harvest.Web/ClientApp/src/sass/harvest.scss @@ -2,7 +2,28 @@ @charset "utf-8"; @import "import", "typography", "layout", "regions", "components"; +@import "https://js.arcgis.com/4.28/@arcgis/core/assets/esri/themes/light/main.css"; -.searchPersonsClearButton > button{ - margin: -1.3em .5em 0em 0em; +.searchPersonsClearButton > button { + margin: -1.3em 0.5em 0em 0em; +} + +// map css +.mapDiv { + padding: 0; + margin: 0; + height: 100%; + width: 100%; + // position: absolute; +} + +// add to all parent divs of the map +.fullHeightWidth { + height: 100%; + width: 100%; +} + +// highest level div +#root { + height: 100%; }