diff --git a/src/admin/components/ResourceTabs/PolygonReviewTab/index.tsx b/src/admin/components/ResourceTabs/PolygonReviewTab/index.tsx index 54d5c06c5..ddbb419fe 100644 --- a/src/admin/components/ResourceTabs/PolygonReviewTab/index.tsx +++ b/src/admin/components/ResourceTabs/PolygonReviewTab/index.tsx @@ -8,7 +8,11 @@ import { VARIANT_FILE_INPUT_MODAL_ADD_IMAGES } from "@/components/elements/Input import { BBox } from "@/components/elements/Map-mapbox/GeoJSON"; import { useMap } from "@/components/elements/Map-mapbox/hooks/useMap"; import { MapContainer } from "@/components/elements/Map-mapbox/Map"; -import { addSourcesToLayers, mapPolygonData } from "@/components/elements/Map-mapbox/utils"; +import { + addSourcesToLayers, + downloadSiteGeoJsonPolygons, + mapPolygonData +} from "@/components/elements/Map-mapbox/utils"; import Menu from "@/components/elements/Menu/Menu"; import { MENU_PLACEMENT_RIGHT_BOTTOM, MENU_PLACEMENT_RIGHT_TOP } from "@/components/elements/Menu/MenuVariant"; import Table from "@/components/elements/Table/Table"; @@ -23,7 +27,6 @@ import { useModalContext } from "@/context/modal.provider"; import { SitePolygonDataProvider } from "@/context/sitePolygon.provider"; import { fetchDeleteV2TerrafundPolygonUuid, - fetchGetV2TerrafundGeojsonSite, fetchGetV2TerrafundPolygonBboxUuid, fetchPostV2TerrafundPolygon, fetchPostV2TerrafundSitePolygonUuidSiteUuid, @@ -203,19 +206,6 @@ const PolygonReviewTab: FC = props => { ); }; - const downloadSiteGeoJsonPolygons = async (siteUuid: string) => { - const polygonGeojson = await fetchGetV2TerrafundGeojsonSite({ - queryParams: { uuid: siteUuid } - }); - const blob = new Blob([JSON.stringify(polygonGeojson)], { type: "application/json" }); - const url = URL.createObjectURL(blob); - const link = document.createElement("a"); - link.href = url; - link.download = `SitePolygons.geojson`; - link.click(); - URL.revokeObjectURL(url); - }; - useEffect(() => { if (files && files.length > 0 && saveFlags) { uploadFiles(); diff --git a/src/components/elements/Map-mapbox/Map.tsx b/src/components/elements/Map-mapbox/Map.tsx index cb7c8049c..e2c411533 100644 --- a/src/components/elements/Map-mapbox/Map.tsx +++ b/src/components/elements/Map-mapbox/Map.tsx @@ -232,7 +232,7 @@ export const MapContainer = ({ }; return ( -
+
diff --git a/src/components/elements/Map-mapbox/hooks/useMap.ts b/src/components/elements/Map-mapbox/hooks/useMap.ts index e5b478618..45810cf70 100644 --- a/src/components/elements/Map-mapbox/hooks/useMap.ts +++ b/src/components/elements/Map-mapbox/hooks/useMap.ts @@ -46,6 +46,7 @@ export const useMap = (onSave?: (geojson: any, record: any) => void) => { const initMap = () => { if (map.current) return; + map.current = new mapboxgl.Map({ container: mapContainer.current as HTMLDivElement, style: MAP_STYLE, diff --git a/src/components/elements/Map-mapbox/utils.ts b/src/components/elements/Map-mapbox/utils.ts index d58b0f9c7..d98005b2b 100644 --- a/src/components/elements/Map-mapbox/utils.ts +++ b/src/components/elements/Map-mapbox/utils.ts @@ -4,6 +4,7 @@ import { createElement } from "react"; import { createRoot } from "react-dom/client"; import { layersList } from "@/constants/layers"; +import { fetchGetV2TerrafundGeojsonSite, fetchGetV2TypeEntity } from "@/generated/apiComponents"; import { SitePolygon, SitePolygonsDataResponse } from "@/generated/apiSchemas"; import { BBox, FeatureCollection } from "./GeoJSON"; @@ -275,3 +276,29 @@ export function mapPolygonData(sitePolygonData: SitePolygonsDataResponse | undef return acc; }, {}); } + +export function getPolygonsData(uuid: string, statusFilter: string, sortOrder: string, type: string, cb: Function) { + fetchGetV2TypeEntity({ + queryParams: { + uuid: uuid, + type: type, + status: statusFilter, + [`sort[${sortOrder}]`]: sortOrder === "created_at" ? "desc" : "asc" + } + }).then(result => { + cb(result); + }); +} + +export async function downloadSiteGeoJsonPolygons(siteUuid: string): Promise { + const polygonGeojson = await fetchGetV2TerrafundGeojsonSite({ + queryParams: { uuid: siteUuid } + }); + const blob = new Blob([JSON.stringify(polygonGeojson)], { type: "application/json" }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = `SitePolygons.geojson`; + link.click(); + URL.revokeObjectURL(url); +} diff --git a/src/components/elements/MapPolygonPanel/AttributeInformation.tsx b/src/components/elements/MapPolygonPanel/AttributeInformation.tsx new file mode 100644 index 000000000..bae22cb23 --- /dev/null +++ b/src/components/elements/MapPolygonPanel/AttributeInformation.tsx @@ -0,0 +1,156 @@ +import { useT } from "@transifex/react"; + +import Dropdown from "@/components/elements/Inputs/Dropdown/Dropdown"; +import Input from "@/components/elements/Inputs/Input/Input"; + +import Text from "../Text/Text"; + +const dropdownOptionsRestoration = [ + { + title: "Tree Planting", + value: 1 + }, + { + title: "Direct Seeding", + value: 2 + }, + { + title: "Assisted Natural Regeneration", + value: 3 + } +]; +const dropdownOptionsTarget = [ + { + title: "Agroforest", + value: 1 + }, + { + title: "Natural Forest", + value: 2 + }, + { + title: "Mangrove", + value: 3 + }, + { + title: "Peatland", + value: 4 + }, + { + title: "Riparian Area or Wetland", + value: 5 + }, + { + title: "Silvopasture", + value: 6 + }, + { + title: "Woodlot or Plantation", + value: 7 + }, + { + title: "Urban Forest", + value: 8 + } +]; + +const dropdownOptionsTree = [ + { + title: "Single Line", + value: 1 + }, + { + title: "Partial", + value: 2 + }, + { + title: "Full", + value: 3 + } +]; +const AttributeInformation = () => { + const t = useT(); + + return ( +
+ + + + {}} + className="bg-white" + /> + {}} + className="bg-white" + /> + {}} + className="bg-white" + /> + + +
+ ); +}; + +export default AttributeInformation; diff --git a/src/components/elements/MapPolygonPanel/ChecklistInformation.tsx b/src/components/elements/MapPolygonPanel/ChecklistInformation.tsx new file mode 100644 index 000000000..1948979c2 --- /dev/null +++ b/src/components/elements/MapPolygonPanel/ChecklistInformation.tsx @@ -0,0 +1,86 @@ +import { useT } from "@transifex/react"; + +import Icon, { IconNames } from "@/components/extensive/Icon/Icon"; +import ModalWithMap from "@/components/extensive/Modal/ModalWithMap"; +import { useModalContext } from "@/context/modal.provider"; + +import Button from "../Button/Button"; +import Text from "../Text/Text"; + +const ChecklistInformation = () => { + const { openModal, closeModal } = useModalContext(); + const t = useT(); + + const openFormModalHandlerRequestPolygonSupport = () => { + openModal( + + ); + }; + return ( +
+ 3 {t("out")} 14 + {t("Validation criteria are not met")} + +
+ + + {t("GeoJSON Format")} + + + + {t("WGS84 Projection")} + + + + {t("Earth Location")} + + + + {t("Country")} + + + + {t("Reasonable Size Self-Intersecting Topology")} + + + + {t("Overlapping Polygons")} + + + + {t("Spike")} + + + + {t("Polygon Integrity")} + + + + {t("GeoJSON Format")} + + + + {t("WGS84 Projection")} + + + + {t("Earth Location")} + + + + {t("Country")} + +
+
+ ); +}; + +export default ChecklistInformation; diff --git a/src/components/elements/MapPolygonPanel/MapEditPolygonPanel.tsx b/src/components/elements/MapPolygonPanel/MapEditPolygonPanel.tsx new file mode 100644 index 000000000..0729096ea --- /dev/null +++ b/src/components/elements/MapPolygonPanel/MapEditPolygonPanel.tsx @@ -0,0 +1,93 @@ +import { t } from "@transifex/native"; +import classNames from "classnames"; +import { Dispatch, SetStateAction } from "react"; +import { When } from "react-if"; + +import Icon, { IconNames } from "@/components/extensive/Icon/Icon"; + +import Button from "../Button/Button"; +import Text from "../Text/Text"; +import AttributeInformation from "./AttributeInformation"; +import ChecklistInformation from "./ChecklistInformation"; +import VersionInformation from "./VersionInformation"; + +export interface MapEditPolygonPanelProps { + setEditPolygon: Dispatch>; + tabEditPolygon: string; + setTabEditPolygon: Dispatch>; + setPreviewVersion: Dispatch>; +} + +const MapEditPolygonPanel = ({ + setEditPolygon, + tabEditPolygon, + setTabEditPolygon, + setPreviewVersion +}: MapEditPolygonPanelProps) => ( + <> +
+
+ + - + + + - + +
+ + +
+
+ + + +
+
+ {AttributeInformation} + {ChecklistInformation} + + + +
+ +); + +export default MapEditPolygonPanel; diff --git a/src/components/elements/MapPolygonPanel/MapPolygonCheckPanelItem.tsx b/src/components/elements/MapPolygonPanel/MapPolygonCheckPanelItem.tsx new file mode 100644 index 000000000..fefbb5c4e --- /dev/null +++ b/src/components/elements/MapPolygonPanel/MapPolygonCheckPanelItem.tsx @@ -0,0 +1,213 @@ +import { useT } from "@transifex/react"; +import classNames from "classnames"; +import { DetailedHTMLProps, Dispatch, HTMLAttributes, SetStateAction, useState } from "react"; +import { When } from "react-if"; + +import Text from "@/components/elements/Text/Text"; +import Icon, { IconNames } from "@/components/extensive/Icon/Icon"; +import ModalConfirm from "@/components/extensive/Modal/ModalConfirm"; +import ModalWithLogo from "@/components/extensive/Modal/ModalWithLogo"; +import ModalWithMap from "@/components/extensive/Modal/ModalWithMap"; +import { useModalContext } from "@/context/modal.provider"; + +import Button from "../Button/Button"; +import Menu from "../Menu/Menu"; +import { MENU_PLACEMENT_RIGHT_BOTTOM } from "../Menu/MenuVariant"; +import { StatusEnum } from "../Status/constants/statusMap"; + +export interface MapPolygonCheckPanelItemProps + extends DetailedHTMLProps, HTMLDivElement> { + uuid: string; + title: string; + isSelected?: boolean; + refContainer?: React.RefObject | null; + setEditPolygon?: Dispatch>; + status: string; + polygon?: string[]; +} + +const MapPolygonCheckPanelItem = ({ + title, + isSelected, + className, + refContainer, + setEditPolygon, + polygon, + status, + ...props +}: MapPolygonCheckPanelItemProps) => { + const { openModal, closeModal } = useModalContext(); + const [openCollapse, setOpenCollapse] = useState(true); + const t = useT(); + + const openFormModalHandlerRequestPolygonSupport = () => { + openModal( + + ); + }; + const openFormModalHandlerAddCommentary = () => { + openModal( + + ); + }; + const openFormModalHandlerConfirm = () => { + openModal( + {}} + /> + ); + }; + + const itemsPrimaryMenu = [ + { + id: "1", + render: () => ( + + +   {t("Edit Polygon")} + + ), + onClick: () => { + if (setEditPolygon) { + setEditPolygon(true); + } + } + }, + { + id: "2", + render: () => ( + + +   {t("Zoom to")} + + ) + }, + { + id: "3", + render: () => ( + + +   {t("Download")} + + ) + }, + { + id: "4", + render: () => ( + + ) + }, + { + id: "5", + render: () => ( + + ) + }, + { + id: "6", + render: () => ( + + ) + } + ]; + + const dynamicClasses = (status: string) => { + switch (status) { + case "Submitted": + return "bg-blue"; + case "Approved": + return "bg-green"; + case "Needs More Info": + return "bg-tertiary-600"; + case "Draft": + return "bg-pinkCustom"; + default: + return "bg-blue "; + } + }; + + return ( +
+
+
+
{" "} +
+ + {t(title)} + +
+
+ + + + + + + +
+
+ +
+ {polygon?.map((item, index) => ( +
+ + + {t(item)} + +
+ ))} +
+
+
+
+ ); +}; + +export default MapPolygonCheckPanelItem; diff --git a/src/components/elements/MapPolygonPanel/MapPolygonCkeckPanel.tsx b/src/components/elements/MapPolygonPanel/MapPolygonCkeckPanel.tsx new file mode 100644 index 000000000..745ae8fe6 --- /dev/null +++ b/src/components/elements/MapPolygonPanel/MapPolygonCkeckPanel.tsx @@ -0,0 +1,69 @@ +import { useT } from "@transifex/react"; +import { Dispatch, Fragment, SetStateAction, useRef } from "react"; + +import Icon, { IconNames } from "@/components/extensive/Icon/Icon"; +import List from "@/components/extensive/List/List"; +import { PolygonAvailableData } from "@/pages/site/[uuid]/components/MockedData"; + +import Text from "../Text/Text"; +import MapPolygonCheckPanelItem from "./MapPolygonCheckPanelItem"; +import { MapPolygonPanelItemProps } from "./MapPolygonPanelItem"; + +export interface MapPolygonCheckPanelProps { + emptyText?: string; + onLoadMore: () => void; + setEditPolygon: Dispatch>; + selected: MapPolygonPanelItemProps | undefined; +} + +const MapPlygonCheckPanel = ({ emptyText, onLoadMore, setEditPolygon, selected }: MapPolygonCheckPanelProps) => { + const t = useT(); + + const refContainer = useRef(null); + return ( + <> + + {t("Available polygons")} + +
+ {PolygonAvailableData.length === 0 && ( + + {t(emptyText) ?? t("No result")} + + )} +
{ + //@ts-ignore + const bottom = e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight; + if (bottom) onLoadMore(); + }} + > + ( + + )} + /> +
+
+ + +   {t("Add Polygon")} + + + ); +}; + +export default MapPlygonCheckPanel; diff --git a/src/components/elements/MapPolygonPanel/MapPolygonPanel.stories.tsx b/src/components/elements/MapPolygonPanel/MapPolygonPanel.stories.tsx new file mode 100644 index 000000000..f25a2ebaa --- /dev/null +++ b/src/components/elements/MapPolygonPanel/MapPolygonPanel.stories.tsx @@ -0,0 +1,98 @@ +import { Meta, StoryObj } from "@storybook/react"; +import { useState } from "react"; + +import Component from "./MapPolygonPanel"; + +const meta: Meta = { + title: "Components/Elements/MapPolygonPanel", + component: Component +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: args => { + const [query, setQuery] = useState(); + + return ( +
+
+ (query ? item.title.includes(query) : item))} + onSearch={setQuery} + /> +
+
+ ); + }, + args: { + title: "Project Sites", + onSelectItem: console.log, + stateViewPanel: false + } +}; + +export const OpenPolygonCheck: Story = { + render: args => { + const [query, setQuery] = useState(); + + return ( +
+
+ (query ? item.title.includes(query) : item))} + onSearch={setQuery} + /> +
+
+ ); + }, + args: { + title: "Project Sites", + onSelectItem: console.log, + stateViewPanel: true + } +}; + +const items = [ + { + uuid: "1", + title: "Puerto Princesa Subterranean River National Park Forest Corridor", + subtitle: "Created 03/12/21", + refContainer: null + }, + { + uuid: "2", + title: "A medium sized project site to see how it looks with 2 lines", + subtitle: "Created 03/12/21", + refContainer: null + }, + { + uuid: "3", + title: "A shorter project site", + subtitle: "Created 03/12/21", + refContainer: null + }, + { + uuid: "4", + title: + "Very long name A medium sized project site to see how it looks with 2 lines A medium sized project site to see how it looks with 2 lines A medium sized project site to see how it looks with 2 lines", + subtitle: "Created 03/12/21", + refContainer: null + }, + { + uuid: "5", + title: "A shorter project site", + subtitle: "Created 03/12/21", + refContainer: null + }, + { + uuid: "6", + title: "A shorter project site", + subtitle: "Created 03/12/21", + refContainer: null + } +]; diff --git a/src/components/elements/MapPolygonPanel/MapPolygonPanel.tsx b/src/components/elements/MapPolygonPanel/MapPolygonPanel.tsx new file mode 100644 index 000000000..f148ebe53 --- /dev/null +++ b/src/components/elements/MapPolygonPanel/MapPolygonPanel.tsx @@ -0,0 +1,101 @@ +import { useT } from "@transifex/react"; +import classNames from "classnames"; +import { DetailedHTMLProps, Dispatch, HTMLAttributes, SetStateAction, useState } from "react"; +import { Else, If, Then, When } from "react-if"; + +import { MapPolygonPanelItemProps } from "@/components/elements/MapPolygonPanel/MapPolygonPanelItem"; + +import Button from "../Button/Button"; +import MapEditPolygonPanel from "./MapEditPolygonPanel"; +import MapPlygonCheckPanel from "./MapPolygonCkeckPanel"; +import MapPlygonSitePanel from "./MapPolygonSitePanel"; + +export interface MapPolygonPanelProps extends DetailedHTMLProps, HTMLDivElement> { + title: string; + items: MapPolygonPanelItemProps[]; + onSelectItem: (item: MapPolygonPanelItemProps) => void; + onSearch: (query: string) => void; + onLoadMore: () => void; + emptyText?: string; + setStateViewPanel: Dispatch>; + stateViewPanel: boolean; + setEditPolygon: Dispatch>; + editPolygon: boolean; + tabEditPolygon: string; + setTabEditPolygon: Dispatch>; + setPreviewVersion: Dispatch>; +} + +const MapPolygonPanel = ({ + title, + items, + className, + onSelectItem, + onSearch, + onLoadMore, + emptyText, + setStateViewPanel, + stateViewPanel, + setEditPolygon, + editPolygon, + tabEditPolygon, + setTabEditPolygon, + setPreviewVersion, + ...props +}: MapPolygonPanelProps) => { + const t = useT(); + const [selected, setSelected] = useState(); + + return ( +
+ + + + + +
+ + +
+ + + + + + +
+
+
+ ); +}; + +export default MapPolygonPanel; diff --git a/src/components/elements/MapPolygonPanel/MapPolygonPanelItem.tsx b/src/components/elements/MapPolygonPanel/MapPolygonPanelItem.tsx new file mode 100644 index 000000000..bd0392b85 --- /dev/null +++ b/src/components/elements/MapPolygonPanel/MapPolygonPanelItem.tsx @@ -0,0 +1,172 @@ +import { useT } from "@transifex/react"; +import classNames from "classnames"; +import { DetailedHTMLProps, Dispatch, HTMLAttributes, SetStateAction } from "react"; + +import Text from "@/components/elements/Text/Text"; +import Icon, { IconNames } from "@/components/extensive/Icon/Icon"; +import ModalConfirm from "@/components/extensive/Modal/ModalConfirm"; +import ModalWithLogo from "@/components/extensive/Modal/ModalWithLogo"; +import ModalWithMap from "@/components/extensive/Modal/ModalWithMap"; +import { useModalContext } from "@/context/modal.provider"; + +import Button from "../Button/Button"; +import Menu from "../Menu/Menu"; +import { MENU_PLACEMENT_RIGHT_BOTTOM } from "../Menu/MenuVariant"; +import { StatusEnum } from "../Status/constants/statusMap"; + +export interface MapPolygonPanelItemProps extends DetailedHTMLProps, HTMLDivElement> { + uuid: string; + title: string; + subtitle: string; + isSelected?: boolean; + refContainer?: React.RefObject | null; + setEditPolygon?: Dispatch>; +} + +const MapPolygonPanelItem = ({ + title, + subtitle, + isSelected, + className, + refContainer, + setEditPolygon, + ...props +}: MapPolygonPanelItemProps) => { + const { openModal, closeModal } = useModalContext(); + const t = useT(); + + const openFormModalHandlerRequestPolygonSupport = () => { + openModal( + + ); + }; + const openFormModalHandlerAddCommentary = () => { + openModal( + + ); + }; + const openFormModalHandlerConfirm = () => { + openModal( + {}} + /> + ); + }; + + const itemsPrimaryMenu = [ + { + id: "1", + render: () => ( + + +   {t("Edit Polygon")} + + ), + onClick: () => { + if (setEditPolygon) { + setEditPolygon(true); + } + } + }, + { + id: "2", + render: () => ( + + +   {t("Zoom to")} + + ) + }, + { + id: "3", + render: () => ( + + +   {t("Download")} + + ) + }, + { + id: "4", + render: () => ( + + ) + }, + { + id: "5", + render: () => ( + + ) + }, + { + id: "6", + render: () => ( + + ) + } + ]; + + return ( +
+
+
+ +
+ + {t(title)} + + {t(subtitle)} +
+
+ + + +
+
+
+
+ ); +}; + +export default MapPolygonPanelItem; diff --git a/src/components/elements/MapPolygonPanel/MapPolygonSitePanel.tsx b/src/components/elements/MapPolygonPanel/MapPolygonSitePanel.tsx new file mode 100644 index 000000000..175a496c3 --- /dev/null +++ b/src/components/elements/MapPolygonPanel/MapPolygonSitePanel.tsx @@ -0,0 +1,187 @@ +import { useT } from "@transifex/react"; +import { Dispatch, Fragment, SetStateAction, useEffect, useRef, useState } from "react"; +import { When } from "react-if"; + +import Icon, { IconNames } from "@/components/extensive/Icon/Icon"; +import List from "@/components/extensive/List/List"; +import { PolygonData } from "@/pages/site/[uuid]/components/MockedData"; + +import Checkbox from "../Inputs/Checkbox/Checkbox"; +import { MenuItem } from "../MenuItem/MenuItem"; +import FilterSearchBox from "../TableFilters/Inputs/FilterSearchBox"; +import Text from "../Text/Text"; +import MapPolygonPanelItem, { MapPolygonPanelItemProps } from "./MapPolygonPanelItem"; + +export interface MapPolygonSitePanelProps { + emptyText?: string; + onLoadMore: () => void; + onSelectItem: (item: MapPolygonPanelItemProps) => void; + setEditPolygon: Dispatch>; + selected: MapPolygonPanelItemProps | undefined; + setSelected: Dispatch>; +} + +const MapPlygonSitePanel = ({ + emptyText, + onLoadMore, + onSelectItem, + setEditPolygon, + selected, + setSelected +}: MapPolygonSitePanelProps) => { + const t = useT(); + const menuRef = useRef(null); + const [openMenu, setOpenMenu] = useState(false); + const refContainer = useRef(null); + const [openSubMenu, setOpenSubMenu] = useState(false); + const checkboxRefs = useRef([]); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (menuRef.current && !menuRef.current?.contains(event.target as Node)) { + setOpenMenu(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }); + + useEffect(() => { + const handleChange = () => { + const checked = checkboxRefs.current.some(ref => ref.checked); + setOpenSubMenu(checked); + }; + + const checkbox = checkboxRefs.current; + + checkbox.forEach(ref => { + if (ref) { + ref.addEventListener("change", handleChange); + } + }); + + handleChange(); + + return () => { + checkbox.forEach(ref => { + if (ref) { + ref.removeEventListener("change", handleChange); + } + }); + }; + }, [openMenu]); + + return ( + <> + {}} /> +
+ + +   {t("new Polygon")} + +
+
+
setOpenMenu(!openMenu)}> + +
+ +
+ ref && checkboxRefs.current.push(ref as HTMLInputElement)} + name="" + label={t("Draft")} + className="flex w-full flex-row-reverse items-center justify-end gap-3" + textClassName="text-10-semibold" + /> + + ref && checkboxRefs.current.push(ref as HTMLInputElement)} + name="" + label={t("Submitted")} + className="flex w-full flex-row-reverse items-center justify-end gap-3" + textClassName="text-10-semibold" + /> + ref && checkboxRefs.current.push(ref as HTMLInputElement)} + name="" + label={t("Approved")} + className="flex w-full flex-row-reverse items-center justify-end gap-3" + textClassName="text-10-semibold" + /> + ref && checkboxRefs.current.push(ref as HTMLInputElement)} + name="" + label={t("Needs More Info")} + className="flex w-full flex-row-reverse items-center justify-end gap-3" + textClassName="text-10-semibold" + /> + +
+ { + setOpenSubMenu(false); + }} + > + { + setOpenSubMenu(false); + }} + > +
+
+
+
+
+
+ +
+
+
+ +
+ {PolygonData.length === 0 && ( + + {emptyText ?? t("No result")} + + )} +
{ + //@ts-ignore + const bottom = e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight; + if (bottom) onLoadMore(); + }} + > + ( + { + setSelected(item); + onSelectItem(item); + }} + isSelected={selected?.uuid === item.uuid} + refContainer={refContainer} + setEditPolygon={setEditPolygon} + /> + )} + /> +
+
+ + ); +}; + +export default MapPlygonSitePanel; diff --git a/src/components/elements/MapPolygonPanel/VersionInformation.tsx b/src/components/elements/MapPolygonPanel/VersionInformation.tsx new file mode 100644 index 000000000..5909948c6 --- /dev/null +++ b/src/components/elements/MapPolygonPanel/VersionInformation.tsx @@ -0,0 +1,92 @@ +import { useT } from "@transifex/react"; +import classNames from "classnames"; +import { Dispatch, SetStateAction } from "react"; + +import Icon, { IconNames } from "@/components/extensive/Icon/Icon"; +import ModalConfirm from "@/components/extensive/Modal/ModalConfirm"; +import { useModalContext } from "@/context/modal.provider"; + +import Menu from "../Menu/Menu"; +import { MENU_PLACEMENT_RIGHT_BOTTOM } from "../Menu/MenuVariant"; +import Text from "../Text/Text"; + +const VersionInformation = ({ setPreviewVersion }: { setPreviewVersion: Dispatch> }) => { + const { openModal, closeModal } = useModalContext(); + const t = useT(); + + const openFormModalHandlerConfirm = () => { + openModal( + {}} + /> + ); + }; + const itemsPrimaryMenu = [ + { + id: "1", + render: () => ( + + +   {t("Preview Version")} + + ), + onClick: () => setPreviewVersion(true) + }, + { + id: "2", + render: () => ( + + +   {t("Delete Version")} + + ), + onClick: () => openFormModalHandlerConfirm() + } + ]; + + return ( +
+
+ + {t("Version")} + + + {t("Date")} + + + {t("Current")} + +
+ +
+ + - + + + - + +
+ + + + +
+
+
+ ); +}; + +export default VersionInformation; diff --git a/src/components/elements/MapSidePanel/MapSidePanel.stories.tsx b/src/components/elements/MapSidePanel/MapSidePanel.stories.tsx index 3ca39db2e..6e17a26ad 100644 --- a/src/components/elements/MapSidePanel/MapSidePanel.stories.tsx +++ b/src/components/elements/MapSidePanel/MapSidePanel.stories.tsx @@ -36,7 +36,8 @@ const items = [ subtitle: "Created 03/12/21", status: "submitted", setClickedButton: console.log, - refContainer: null + refContainer: null, + type: "sites" }, { uuid: "2", @@ -44,7 +45,8 @@ const items = [ subtitle: "Created 03/12/21", status: "submitted", setClickedButton: console.log, - refContainer: null + refContainer: null, + type: "sites" }, { uuid: "3", @@ -52,7 +54,8 @@ const items = [ subtitle: "Created 03/12/21", status: "submitted", setClickedButton: console.log, - refContainer: null + refContainer: null, + type: "sites" }, { uuid: "4", @@ -61,7 +64,8 @@ const items = [ subtitle: "Created 03/12/21", status: "submitted", setClickedButton: console.log, - refContainer: null + refContainer: null, + type: "sites" }, { uuid: "5", @@ -69,7 +73,8 @@ const items = [ subtitle: "Created 03/12/21", status: "submitted", setClickedButton: console.log, - refContainer: null + refContainer: null, + type: "sites" }, { uuid: "6", @@ -77,6 +82,7 @@ const items = [ subtitle: "Created 03/12/21", status: "submitted", setClickedButton: console.log, - refContainer: null + refContainer: null, + type: "sites" } ]; diff --git a/src/components/elements/MapSidePanel/MapSidePanel.tsx b/src/components/elements/MapSidePanel/MapSidePanel.tsx index 163713de5..24e53b88c 100644 --- a/src/components/elements/MapSidePanel/MapSidePanel.tsx +++ b/src/components/elements/MapSidePanel/MapSidePanel.tsx @@ -25,6 +25,7 @@ export interface MapSidePanelProps extends DetailedHTMLProps void; setSortOrder: React.Dispatch>; + type: string; } const MapSidePanel = ({ @@ -38,6 +39,7 @@ const MapSidePanel = ({ checkedValues, onCheckboxChange, setSortOrder, + type, ...props }: MapSidePanelProps) => { const t = useT(); @@ -194,6 +196,7 @@ const MapSidePanel = ({ setClickedButton={setClickedButton} isSelected={selected?.uuid === item.uuid} refContainer={refContainer} + type={type} /> )} /> diff --git a/src/components/elements/MapSidePanel/MapSidePanelItem.tsx b/src/components/elements/MapSidePanel/MapSidePanelItem.tsx index 01096384d..e1b2d3b8a 100644 --- a/src/components/elements/MapSidePanel/MapSidePanelItem.tsx +++ b/src/components/elements/MapSidePanel/MapSidePanelItem.tsx @@ -18,6 +18,7 @@ export interface MapSidePanelItemProps extends DetailedHTMLProps>; refContainer: React.RefObject | null; + type: string; } const MapSidePanelItem = ({ @@ -29,21 +30,26 @@ const MapSidePanelItem = ({ setClickedButton, className, refContainer, + type, ...props }: MapSidePanelItemProps) => { let imageStatus = `IC_${status.toUpperCase().replace(/-/g, "_")}`; const t = useT(); const itemsPrimaryMenu = [ - { - id: "1", - render: () => ( - setClickedButton("site")}> - -   {t("View Site")} - - ) - }, + ...(type !== "sites" + ? [ + { + id: "1", + render: () => ( + setClickedButton("site")}> + +   {t("View Site")} + + ) + } + ] + : []), { id: "2", render: () => ( diff --git a/src/pages/project/[uuid]/components/ProjectArea.tsx b/src/pages/project/[uuid]/components/OverviewMapArea.tsx similarity index 71% rename from src/pages/project/[uuid]/components/ProjectArea.tsx rename to src/pages/project/[uuid]/components/OverviewMapArea.tsx index 2df4ef9e7..6b93b3f21 100644 --- a/src/pages/project/[uuid]/components/ProjectArea.tsx +++ b/src/pages/project/[uuid]/components/OverviewMapArea.tsx @@ -4,48 +4,39 @@ import { useEffect, useState } from "react"; import { BBox } from "@/components/elements/Map-mapbox/GeoJSON"; import { useMap } from "@/components/elements/Map-mapbox/hooks/useMap"; import { MapContainer } from "@/components/elements/Map-mapbox/Map"; +import { getPolygonsData } from "@/components/elements/Map-mapbox/utils"; import MapSidePanel from "@/components/elements/MapSidePanel/MapSidePanel"; import { APPROVED, DRAFT, NEEDS_MORE_INFORMATION, SUBMITTED } from "@/constants/statuses"; -import { fetchGetV2TypeEntity } from "@/generated/apiComponents"; import { SitePolygonsDataResponse } from "@/generated/apiSchemas"; import { useDate } from "@/hooks/useDate"; -interface ProjectAreaProps { - project: any; +interface EntityAreaProps { + entityModel: any; + type: string; } -const ProjectArea = ({ project }: ProjectAreaProps) => { +const OverviewMapArea = ({ entityModel, type }: EntityAreaProps) => { const t = useT(); const { format } = useDate(); const [polygonsData, setPolygonsData] = useState([]); const [polygonDataMap, setPolygonDataMap] = useState({}); - const [projectBbox, setProjectBbox] = useState(); + const [entityBbox, setEntityBbox] = useState(); const mapFunctions = useMap(); const [checkedValues, setCheckedValues] = useState([]); const [sortOrder, setSortOrder] = useState("created_at"); - - const getPolygonsData = (uuid: string, statusFilter: string, sortOrder: string) => { - fetchGetV2TypeEntity({ - queryParams: { - uuid: uuid, - type: "projects", - status: statusFilter, - [`sort[${sortOrder}]`]: sortOrder === "created_at" ? "desc" : "asc" - } - }).then(result => { - if (result.polygonsData) { - setPolygonsData(result.polygonsData); - setProjectBbox(result.bbox as BBox); - } - }); + const setResultValues = (result: any) => { + if (result.polygonsData) { + setPolygonsData(result.polygonsData); + setEntityBbox(result.bbox as BBox); + } }; useEffect(() => { - if (project?.uuid) { + if (entityModel?.uuid) { const statusFilter = checkedValues.join(","); - getPolygonsData(project.uuid, statusFilter, sortOrder); + getPolygonsData(entityModel.uuid, statusFilter, sortOrder, type, setResultValues); } - }, [project, checkedValues, sortOrder]); + }, [entityModel, checkedValues, sortOrder]); useEffect(() => { if (polygonsData?.length > 0) { @@ -76,9 +67,9 @@ const ProjectArea = ({ project }: ProjectAreaProps) => { }; return ( -
+ <> ({ ...item, @@ -92,19 +83,20 @@ const ProjectArea = ({ project }: ProjectAreaProps) => { checkedValues={checkedValues} onCheckboxChange={handleCheckboxChange} setSortOrder={setSortOrder} + type={type} /> -
+ ); }; -export default ProjectArea; +export default OverviewMapArea; diff --git a/src/pages/project/[uuid]/tabs/Overview.tsx b/src/pages/project/[uuid]/tabs/Overview.tsx index 8f92105ff..fc71ec08b 100644 --- a/src/pages/project/[uuid]/tabs/Overview.tsx +++ b/src/pages/project/[uuid]/tabs/Overview.tsx @@ -14,7 +14,7 @@ import PageCard from "@/components/extensive/PageElements/Card/PageCard"; import PageColumn from "@/components/extensive/PageElements/Column/PageColumn"; import PageRow from "@/components/extensive/PageElements/Row/PageRow"; import { useFramework } from "@/hooks/useFramework"; -import ProjectArea from "@/pages/project/[uuid]/components/ProjectArea"; +import OverviewMapArea from "@/pages/project/[uuid]/components/OverviewMapArea"; interface ProjectOverviewTabProps { project: any; @@ -97,7 +97,7 @@ const ProjectOverviewTab = ({ project }: ProjectOverviewTabProps) => { } > - + diff --git a/src/pages/site/[uuid]/components/MockedData.ts b/src/pages/site/[uuid]/components/MockedData.ts index 056628fbc..82f67b376 100644 --- a/src/pages/site/[uuid]/components/MockedData.ts +++ b/src/pages/site/[uuid]/components/MockedData.ts @@ -1,3 +1,6 @@ +import { MapPolygonCheckPanelItemProps } from "@/components/elements/MapPolygonPanel/MapPolygonCheckPanelItem"; +import { MapPolygonPanelItemProps } from "@/components/elements/MapPolygonPanel/MapPolygonPanelItem"; + export const uploadImageData = [ { id: "1", name: "Images5.png", status: "We are processing your image", isVerified: true }, { id: "2", name: "Images4.png", status: "We are processing your image", isVerified: true }, @@ -40,3 +43,60 @@ export const commentariesItems = [ status: "Draft" } ]; + +export const PolygonData: MapPolygonPanelItemProps[] = [ + { + uuid: "1", + title: "Polygon 1", + subtitle: "Created 15/12/2023" + }, + { + uuid: "2", + title: "Polygon 2", + subtitle: "Created 15/12/2023" + }, + { + uuid: "3", + title: "Polygon 3", + subtitle: "Created 15/12/2023" + }, + { + uuid: "4", + title: "Polygon 4", + subtitle: "Created 15/12/2023" + }, + { + uuid: "5", + title: "Polygon 5", + subtitle: "Created 15/12/2023" + } +]; + +export const PolygonAvailableData: MapPolygonCheckPanelItemProps[] = [ + { + uuid: "1", + title: "Durrell", + status: "Submitted" + }, + { + uuid: "2", + title: "Ecofix", + status: "Approved" + }, + { + uuid: "3", + title: "Env Coffee Forest Forum", + status: "Needs More Info", + polygon: ["Not WGS 84 projection", "Not WGS 84 projection", "Overlapping polygons identified"] + }, + { + uuid: "4", + title: "Env Found Afr Sl", + status: "Submitted" + }, + { + uuid: "5", + title: "Justdiggit", + status: "Draft" + } +]; diff --git a/src/pages/site/[uuid]/components/SiteArea.tsx b/src/pages/site/[uuid]/components/SiteArea.tsx new file mode 100644 index 000000000..1c464a242 --- /dev/null +++ b/src/pages/site/[uuid]/components/SiteArea.tsx @@ -0,0 +1,85 @@ +import { useT } from "@transifex/react"; +import { Dispatch, SetStateAction, useState } from "react"; +import { When } from "react-if"; + +import Button from "@/components/elements/Button/Button"; +import Text from "@/components/elements/Text/Text"; +import Icon, { IconNames } from "@/components/extensive/Icon/Icon"; +import OverviewMapArea from "@/pages/project/[uuid]/components/OverviewMapArea"; + +interface SiteAreaProps { + sites: any; + editPolygon: boolean; + setEditPolygon: Dispatch>; +} + +const SiteArea = ({ sites, editPolygon, setEditPolygon }: SiteAreaProps) => { + const t = useT(); + const [tabEditPolygon] = useState("Attributes"); + const [previewVersion, setPreviewVersion] = useState(false); + return ( +
+
+ +
+ +
+ +
+
+ +
+ + + {t("Preview Attributes")} + +
+ + {t("Polygon ID")} + + - +
+
+ + {t("Restoration Practice")} + + - +
+
+ + {t("Target Land Use System")} + + - +
+
+ + {t("Tree Distribution")} + + - +
+
+ + {t("Source")} + + - +
+
+
+
+ +
+ ); +}; + +export default SiteArea; diff --git a/src/pages/site/[uuid]/tabs/Overview.tsx b/src/pages/site/[uuid]/tabs/Overview.tsx index 5c76f3339..301317358 100644 --- a/src/pages/site/[uuid]/tabs/Overview.tsx +++ b/src/pages/site/[uuid]/tabs/Overview.tsx @@ -8,24 +8,18 @@ import Button from "@/components/elements/Button/Button"; import GoalProgressCard from "@/components/elements/Cards/GoalProgressCard/GoalProgressCard"; import ItemMonitoringCards from "@/components/elements/Cards/ItemMonitoringCard/ItemMonitoringCards"; import Dropdown from "@/components/elements/Inputs/Dropdown/Dropdown"; -import { VARIANT_FILE_INPUT_MODAL_ADD_IMAGES } from "@/components/elements/Inputs/FileInput/FileInputVariants"; -import Menu from "@/components/elements/Menu/Menu"; -import { MENU_PLACEMENT_BOTTOM_BOTTOM } from "@/components/elements/Menu/MenuVariant"; -import StepProgressbar from "@/components/elements/ProgressBar/StepProgressbar/StepProgressbar"; +import { downloadSiteGeoJsonPolygons } from "@/components/elements/Map-mapbox/utils"; import Text from "@/components/elements/Text/Text"; import Icon, { IconNames } from "@/components/extensive/Icon/Icon"; -import ModalAdd from "@/components/extensive/Modal/ModalAdd"; -import ModalConfirm from "@/components/extensive/Modal/ModalConfirm"; -import ModalSubmit from "@/components/extensive/Modal/ModalSubmit"; -import ModalWithMap from "@/components/extensive/Modal/ModalWithMap"; import PageBody from "@/components/extensive/PageElements/Body/PageBody"; import PageCard from "@/components/extensive/PageElements/Card/PageCard"; import PageColumn from "@/components/extensive/PageElements/Column/PageColumn"; import PageRow from "@/components/extensive/PageElements/Row/PageRow"; -import { useModalContext } from "@/context/modal.provider"; import { getEntityDetailPageLink } from "@/helpers/entity"; import { useFramework } from "@/hooks/useFramework"; +import SiteArea from "../components/SiteArea"; + interface SiteOverviewTabProps { site: any; } @@ -35,156 +29,6 @@ const SiteOverviewTab = ({ site }: SiteOverviewTabProps) => { const router = useRouter(); const { isPPC } = useFramework(site); const [editPolygon, setEditPolygon] = useState(false); - const { openModal, closeModal } = useModalContext(); - const openFormModalHandlerAddPolygon = () => { - openModal( - - {t("TerraMatch upload limits:")}  - {t("50 MB per upload")} -
- } - onClose={closeModal} - content="Start by adding polygons to your site." - primaryButtonText="Close" - primaryButtonProps={{ className: "px-8 py-3", variant: "primary", onClick: closeModal }} - > - ); - }; - const openFormModalHandlerUploadImages = () => { - openModal( - - Uploaded Files - - } - onClose={closeModal} - content="Start by adding images for processing." - primaryButtonText="Save" - primaryButtonProps={{ className: "px-8 py-3", variant: "primary", onClick: closeModal }} - > - ); - }; - - const openFormModalHandlerSubmitReviewConfirm = () => { - openModal( - - Are your sure you want to submit your polygons for the site Iseme.? - - } - onClose={closeModal} - onConfirm={() => { - closeModal; - }} - /> - ); - }; - - const openFormModalHandlerRequestPolygonSupport = () => { - openModal( - - ); - }; - - const openFormModalHandlerSubmitPolygon = () => { - openModal( - { - closeModal(); - openFormModalHandlerSubmitReviewConfirm(); - } - }} - secondaryButtonText="Cancel" - secondaryButtonProps={{ className: "px-8 py-3", variant: "white-page-admin", onClick: closeModal }} - site={site} - > - ); - }; - - const polygonStatusLabels = [ - { id: "1", label: "Site Approved" }, - { id: "2", label: "Polygons Submitted" }, - { id: "3", label: "Polygons Approved" }, - { id: "4", label: "Monitoring Begins" } - ]; - - const itemsPrimaryMenu = [ - { - id: "1", - render: () => ( - - Create Polygons - - ), - onClick: () => { - console.log("Create Polygons", editPolygon); - setEditPolygon(true); - } - }, - { - id: "2", - render: () => ( - - Upload Data - - ), - onClick: () => openFormModalHandlerAddPolygon() - }, - { - id: "3", - render: () => ( - - Upload Images - - ), - onClick: () => openFormModalHandlerUploadImages() - } - ]; - const itemsSubmitPolygon = [ - { - id: "1", - render: () => ( - - Request Support - - ), - onClick: () => openFormModalHandlerRequestPolygonSupport() - }, - { - id: "2", - render: () => ( - - Submit for Review - - ), - onClick: () => openFormModalHandlerSubmitPolygon() - } - ]; return ( @@ -242,31 +86,20 @@ const SiteOverviewTab = ({ site }: SiteOverviewTabProps) => { modified in QGIS or ArcGIS and imported again; or fed through the mobile application.
- - - - - - -
- -
- - Polygon Status - - -
+