Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
Table, TableBody, TableCell, TableContainer, TableHead, TableHeader, TableRow,
TableExpandHeader, TableExpandRow, TableExpandedRow, TableToolbar, TableToolbarSearch,
Button, Column, Grid,
DefinitionTooltip
DefinitionTooltip,
Checkbox,
Tooltip
} from "@carbon/react";
import { CropGrowth, DiamondOutline, Layers, Search } from "@carbon/icons-react";
import { NOT_APPLICABLE, PLACE_HOLDER } from "@/constants";
Expand All @@ -24,9 +26,18 @@ import "./styles.scss";

type OpeningForestCoverProps = {
openingId: number;
availableForestCoverIds: string[];
setAvailableForestCoverIds: React.Dispatch<React.SetStateAction<string[]>>;
selectedForestCoverIds: string[];
setSelectedForestCoverIds: React.Dispatch<React.SetStateAction<string[]>>;
};

const OpeningForestCover = ({ openingId }: OpeningForestCoverProps) => {
const OpeningForestCover = ({
openingId,
availableForestCoverIds,
setAvailableForestCoverIds,
selectedForestCoverIds,
setSelectedForestCoverIds }: OpeningForestCoverProps) => {
const [expandedRows, setExpandedRows] = useState<number[]>([]);
const [searchInput, setSearchInput] = useState<string>("");
const [searchTerm, setSearchTerm] = useState<string | undefined>();
Expand Down Expand Up @@ -78,6 +89,12 @@ const OpeningForestCover = ({ openingId }: OpeningForestCoverProps) => {
);
};

const allAvailableIds = forestCoverSearchQuery.data
?.map(row => `${row.coverId}-${row.polygonId}`)
.filter(id => availableForestCoverIds.includes(id)) ?? [];
const allSelected = allAvailableIds.length > 0 && allAvailableIds.every(id => selectedForestCoverIds.includes(id));
const someSelected = allAvailableIds.some(id => selectedForestCoverIds.includes(id));

// Render cell content similar to ActivityAccordion
const renderCellContent = (
row: OpeningForestCoverDto,
Expand Down Expand Up @@ -255,10 +272,35 @@ const OpeningForestCover = ({ openingId }: OpeningForestCoverProps) => {
rowCount={10}
/>
) : (
<Table className="default-zebra-table forest-cover-table" aria-label="Forest cover table">
<Table className="default-zebra-table forest-cover-table" aria-label="Forest cover table" >
<TableHead>
<TableRow>
<TableRow >
<TableExpandHeader />
<TableHeader key="map-header">
<div className="map-header-checkbox-container">
<Tooltip
label={allSelected ? "Unselect all" : "Select all"}
align="right"
className="forest-cover-map-tooltip"
>
<span>
<Checkbox
id="forest-cover-select-all"
checked={allSelected}
indeterminate={!allSelected && someSelected}
disabled={allAvailableIds.length === 0}
labelText=""
onChange={(_, { checked }) => {
setSelectedForestCoverIds(checked ? allAvailableIds : []);
}}
/>

</span>
</Tooltip>
<span>Map</span>
</div>

</TableHeader>
{ForestCoverTableHeaders.map((header) => (
<TableHeader key={String(header.key)}>{header.header}</TableHeader>
))}
Expand All @@ -268,13 +310,44 @@ const OpeningForestCover = ({ openingId }: OpeningForestCoverProps) => {
{forestCoverSearchQuery.data?.map((row, idx) => {
const isExpanded = expandedRows.includes(row.coverId);
return (
<React.Fragment key={row.coverId + idx}>
<React.Fragment key={`${row.coverId}-${row.polygonId}-${idx}`}>
<TableExpandRow
className="opening-forest-cover-table-row"
aria-label={`Expand row for Polygon ID ${row.coverId}`}
isExpanded={isExpanded}
onExpand={() => handleRowExpand(row.coverId)}
>
<TableCell key={"map-select" + row.coverId + row.polygonId}>
<Tooltip
label={
!availableForestCoverIds.includes(`${row.coverId}-${row.polygonId}`)
? "Polygon is not available"
: "Show on map"
}
align={
!availableForestCoverIds.includes(`${row.coverId}-${row.polygonId}`)
? "right"
: "top"
}
>
<span>
<Checkbox
id={`forest-cover-map-checkbox-${row.coverId}-${idx}`}
key={`forest-cover-map-checkbox-${row.coverId}-${idx}`}
checked={selectedForestCoverIds.includes(`${row.coverId}-${row.polygonId}`)}
disabled={!availableForestCoverIds.includes(`${row.coverId}-${row.polygonId}`)}
labelText=""
onChange={(_, { checked }) => {
setSelectedForestCoverIds(ids =>
checked
? [...ids, `${row.coverId}-${row.polygonId}`]
: ids.filter(id => id !== `${row.coverId}-${row.polygonId}`)
);
}}
/>
</span>
</Tooltip>
</TableCell>
{ForestCoverTableHeaders.map((header) => (
<TableCell key={String(header.key)} className="default-table-cell">
{renderCellContent(row, header.key)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@
.forest-cover-expanded-row[data-child-row="true"] td .child-table td {
padding-inline: 1rem;
}

.map-header-checkbox-container {
align-items: center;
display: flex;
flex-direction: row;
}
}
}

Expand Down
22 changes: 22 additions & 0 deletions frontend/src/components/OpeningDetails/OpeningSummary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,21 @@ type OpeningSummaryProps = {
tombstoneObj?: OpeningDetailsTombstoneDto;
isLoading?: boolean;
currentTab: number;
availableForestCoverIds: string[];
setAvailableForestCoverIds: React.Dispatch<React.SetStateAction<string[]>>;
selectedForestCoverIds: string[];
setSelectedForestCoverIds: React.Dispatch<React.SetStateAction<string[]>>;
};

const OpeningSummary = ({
openingId,
tombstoneObj,
isLoading,
currentTab,
availableForestCoverIds,
setAvailableForestCoverIds,
selectedForestCoverIds,
setSelectedForestCoverIds
}: OpeningSummaryProps) => {
const [showMap, setShowMap] = useState<boolean>(true);

Expand Down Expand Up @@ -54,6 +62,15 @@ const OpeningSummary = ({
}
};

const isForestCoverMap = (mapKindTypes: MapKindType[]): boolean => {
return (
mapKindTypes.length === 3 &&
mapKindTypes.includes(mapKinds[4]?.code as MapKindType) &&
mapKindTypes.includes(mapKinds[5]?.code as MapKindType) &&
mapKindTypes.includes(mapKinds[6]?.code as MapKindType)
);
};

return (
<Grid className="default-grid opening-summary-grid">
<Column className="card-title-col" sm={4} md={8} lg={16}>
Expand Down Expand Up @@ -271,6 +288,11 @@ const OpeningSummary = ({
layerFilter={true}
kind={mapKind(currentTab)}
isDetailsPage={true}
isForestCoverMap={isForestCoverMap(mapKind(currentTab))}
availableForestCoverIds={availableForestCoverIds}
setAvailableForestCoverIds={setAvailableForestCoverIds}
selectedForestCoverIds={selectedForestCoverIds}
setSelectedForestCoverIds={setSelectedForestCoverIds}
/>
</Column>
) : null}
Expand Down
48 changes: 47 additions & 1 deletion frontend/src/components/OpeningsMap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ interface MapProps {
layerFilter?: boolean;
kind?: MapKindType[];
isDetailsPage?: boolean;
isForestCoverMap?: boolean;
availableForestCoverIds?: string[];
setAvailableForestCoverIds?: React.Dispatch<React.SetStateAction<string[]>>;
selectedForestCoverIds?: string[];
setSelectedForestCoverIds?: React.Dispatch<React.SetStateAction<string[]>>;
}

const OpeningsMap: React.FC<MapProps> = ({
Expand All @@ -37,6 +42,11 @@ const OpeningsMap: React.FC<MapProps> = ({
layerFilter = false,
kind = ["WHSE_FOREST_VEGETATION.RSLT_OPENING_SVW"],
isDetailsPage = false,
isForestCoverMap = false,
availableForestCoverIds,
setAvailableForestCoverIds,
selectedForestCoverIds,
setSelectedForestCoverIds
}) => {
const [selectedOpeningIds, setSelectedOpeningIds] = useState<number[]>([]);
const [openings, setOpenings] = useState<FeatureCollection[]>([]);
Expand All @@ -55,6 +65,22 @@ const OpeningsMap: React.FC<MapProps> = ({
const polygonClickedRef = useRef(false);
const kindKey = Array.isArray(kind) ? kind.join(",") : String(kind);

const polygonsToRender = React.useMemo(() => {
if (!isForestCoverMap) return openings;
const selectedSet = new Set(selectedForestCoverIds);
return openings.map(fc => ({
...fc,
features: fc.features.filter(
feature =>
feature.properties?.FOREST_COVER_ID &&
feature.properties?.SILV_POLYGON_NUMBER &&
selectedSet.has(
`${feature.properties.FOREST_COVER_ID}-${feature.properties.SILV_POLYGON_NUMBER}`
)
),
}));
}, [isForestCoverMap, selectedForestCoverIds, openings]);

/**
* This function is used to fetch the map queries based on the selected opening IDs
* and the kind of map data.
Expand Down Expand Up @@ -116,6 +142,19 @@ const OpeningsMap: React.FC<MapProps> = ({
const allSuccess = mapQueries.every((query) => query.status === "success");
if (allSuccess) {
setOpenings(mapQueries.map((query) => query.data as FeatureCollection));

if (isForestCoverMap) {
const forestCoverIds: string[] = mapQueries
.flatMap((query) => (query.data as FeatureCollection).features)
.filter((feature) => feature.properties!.FOREST_COVER_ID && feature.properties!.SILV_POLYGON_NUMBER)
.map(
(feature) =>
`${feature.properties!.FOREST_COVER_ID}-${feature.properties!.SILV_POLYGON_NUMBER}`
);
if (setAvailableForestCoverIds) {
setAvailableForestCoverIds(forestCoverIds);
}
}
} else {
// Check if there are any errors and extract their IDs
const errorIds = mapQueries
Expand Down Expand Up @@ -193,6 +232,12 @@ const OpeningsMap: React.FC<MapProps> = ({
<div className="opening-map-container" style={{ height: `${mapSize}px`, width: "100%" }}>
{/* Popup info in top left */}

{isForestCoverMap && selectedForestCoverIds?.length === 0 && (
<div className="opening-map-empty-message">
No forest cover polygon is selected and displayed. Select from the table to show on map.
</div>
)}

{selectedFeature || hoveredFeature ? (
<div className={`map-popup-top-left${selectedFeature &&
(hoveredFeature && selectedFeature.id === hoveredFeature.id) ||
Expand Down Expand Up @@ -254,7 +299,8 @@ const OpeningsMap: React.FC<MapProps> = ({

{/* Display Opening polygons, if any */}
<OpeningsMapEntry
polygons={openings}
key={selectedForestCoverIds?.join(",")}
polygons={polygonsToRender}
hoveredFeature={hoveredFeature}
setHoveredFeature={setHoveredFeature}
selectedFeature={selectedFeature}
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/components/OpeningsMap/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@

.opening-map-container {
position: relative;

.opening-map-empty-message {
position: absolute;
top: 1.5rem;
left: 50%;
transform: translateX(-50%);
background: colors.$white;
color: colors.$gray-100;
padding: 0.5rem;
border-radius: 0.25rem;
z-index: 1000;
pointer-events: none;
}
}

.map-popup-top-left {
Expand Down
33 changes: 33 additions & 0 deletions frontend/src/components/OpeningsMapEntryPopup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ const OpeningsMapEntryPopup: React.FC<OpeningsMapEntryPopupProps> = ({
return <h4 className="map-popup-header">Opening ID: {openingId}</h4>;
case "WHSE_FOREST_VEGETATION.RSLT_STANDARDS_UNIT_SVW":
return <h4 className="map-popup-header">Standard Unit: {data["standardUnitId"]}</h4>;
case "WHSE_FOREST_VEGETATION.RSLT_FOREST_COVER_INV_SVW":
case "WHSE_FOREST_VEGETATION.RSLT_FOREST_COVER_RESERVE_SVW":
case "WHSE_FOREST_VEGETATION.RSLT_FOREST_COVER_SILV_SVW":
return <h4 className="map-popup-header">Polygon ID: {data["polygon"]}</h4>;
default:
return <h4 className="map-popup-header">Opening ID: {openingId}</h4>;
}
Expand All @@ -47,6 +51,35 @@ const OpeningsMapEntryPopup: React.FC<OpeningsMapEntryPopupProps> = ({
<span>Net area (ha): <span className="popup-value">{data["netArea"]}</span></span>
</>
);

case "WHSE_FOREST_VEGETATION.RSLT_FOREST_COVER_INV_SVW":
return (
<>
<span>Opening ID: <span className="popup-value">{openingId}</span></span>
<span>Forest Cover ID: <span className="popup-value">{data["forestCoverId"]}</span></span>
<span>Reference Year: <span className="popup-value">{data["referenceYear"]}</span></span>
<span>Net Area (ha): <span className="popup-value">{data["netArea"]}</span></span>
<span>Polygon Area (ha): <span className="popup-value">{data["polygonArea"]}</span></span>
</>
);
case "WHSE_FOREST_VEGETATION.RSLT_FOREST_COVER_RESERVE_SVW":
return (
<>
<span>Opening ID: <span className="popup-value">{openingId}</span></span>
<span>Polygon Area (ha): <span className="popup-value">{data["polygonArea"]}</span></span>
</>
);
case "WHSE_FOREST_VEGETATION.RSLT_FOREST_COVER_SILV_SVW":
return (
<>
<span>Opening ID: <span className="popup-value">{openingId}</span></span>
<span>Forest Cover ID: <span className="popup-value">{data["forestCoverId"]}</span></span>
<span>Reference Year: <span className="popup-value">{data["referenceYear"]}</span></span>
<span>Net Area (ha): <span className="popup-value">{data["netArea"]}</span></span>
<span>Non Mapped Area (ha): <span className="popup-value">{data["nonMappedArea"]}</span></span>
<span>Polygon Area (ha): <span className="popup-value">{data["polygonArea"]}</span></span>
</>
);
default:
return (
<>
Expand Down
15 changes: 14 additions & 1 deletion frontend/src/screens/Openings/OpeningDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ const OpeningDetails = () => {
const openingId = param.openingId;
const isIdirUser = auth.user?.idpProvider === 'IDIR';

const [availableForestCoverIds, setAvailableForestCoverIds] = useState<string[]>([]);
const [selectedForestCoverIds, setSelectedForestCoverIds] = useState<string[]>([]);

useEffect(() => {
document.title = `Opening ${openingId} - Silva`;
return () => {
Expand Down Expand Up @@ -147,6 +150,10 @@ const OpeningDetails = () => {
tombstoneObj={openingDetailsTombstoneQuery.data?.tombstone}
isLoading={openingDetailsTombstoneQuery.isLoading}
currentTab={activeTab}
availableForestCoverIds={availableForestCoverIds}
setAvailableForestCoverIds={setAvailableForestCoverIds}
selectedForestCoverIds={selectedForestCoverIds}
setSelectedForestCoverIds={setSelectedForestCoverIds}
/>
</Column>

Expand Down Expand Up @@ -216,7 +223,13 @@ const OpeningDetails = () => {
<TabPanel className="tab-content full-width-col">
{isActive(4) ? (
<Suspense fallback={<TextAreaSkeleton />}>
<OpeningForestCover openingId={Number(openingId)} />
<OpeningForestCover
openingId={Number(openingId)}
availableForestCoverIds={availableForestCoverIds}
setAvailableForestCoverIds={setAvailableForestCoverIds}
selectedForestCoverIds={selectedForestCoverIds}
setSelectedForestCoverIds={setSelectedForestCoverIds}
/>
</Suspense>
) : null}
</TabPanel>
Expand Down
Loading
Loading