Skip to content

Commit

Permalink
feat: #121 limit data level to a specific level
Browse files Browse the repository at this point in the history
  • Loading branch information
redet-G committed Nov 26, 2024
1 parent 3f3e22c commit a8713bd
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 27 deletions.
6 changes: 5 additions & 1 deletion src/Components/Chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const url =
"api/dashboards.json?paging=false&fields=id,name,favorite,displayDescription,dashboardItems[id,resources[id, name],type,shape,x,y,width,height,text,visualization[id,displayName],map[id,displayName],eventReport[id,displayName],eventChart[id,displayName]]";

export default function Chart({
settings,
savedReports,
setSavedReports,
selectedSavedChart,
Expand Down Expand Up @@ -179,7 +180,10 @@ export default function Chart({
</Select>
</FormControl>

<OrgUnitFilterModal onConfirmed={handelFilterSelect} />
<OrgUnitFilterModal
settings={settings}
onConfirmed={handelFilterSelect}
/>
</Paper>
</Grid>
<Grid item xs={12} md={12} lg={12}>
Expand Down
5 changes: 5 additions & 0 deletions src/Components/Dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ReactGA from "react-ga4";
// import MapChart from "./MapChart";
import Footer from "./Footer";
import NavBar from "./AppBar";
import useSettings from "../hooks/useSettings";

const chartData = {
headers: [
Expand Down Expand Up @@ -49,6 +50,9 @@ export default function Dashboard() {
const [savedReports, setSavedReports] = React.useState(
JSON.parse(localStorage.getItem("saved_reports"))
);

const settings = useSettings();

return (
<Box sx={{ display: "flex" }}>
<NavBar
Expand All @@ -73,6 +77,7 @@ export default function Dashboard() {
<Grid container spacing={3}>
{/* Chart */}
<Chart
settings={settings}
savedReports={savedReports}
setSavedReports={setSavedReports}
selectedSavedChart={selectedSavedChart}
Expand Down
66 changes: 49 additions & 17 deletions src/Components/OrgUnitFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const CustomTreeItem = styled(TreeItem)(({ theme }) => ({

const OrgUnitFilter = (props) => {
const [expanded, setExpanded] = useState([]);
const { selected, setSelected } = props;
const { selected, setSelected, settings } = props;
const [data, setData] = useState({ ...props.data });
const apiBase = process.env.REACT_APP_BASE_URI;

Expand All @@ -39,28 +39,45 @@ const OrgUnitFilter = (props) => {
return dataCache[nodeId];
}

console.log("fetching...");

const url = `${apiBase}api/organisationUnits/${nodeId}?fields=displayName,path,id,children%5Bid%2Cpath%2CdisplayName%5D`;
const url = `${apiBase}api/organisationUnits/${nodeId}?fields=displayName,level,path,id,children%5Bid%2Cpath%2CdisplayName%5D ${
settings?.orgUnitLimit?.level
? `&filter=children.level:lt:${settings?.orgUnitLimit?.level}&filter=level:lt:${settings?.orgUnitLimit?.level}`
: ""
}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error("Failed to fetch data");
}
const fetchedData = await response.json();
dataCache[nodeId] = fetchedData; // Cache the fetched data
return fetchedData;
const dataLoaded = await response.text();

if ("" === dataLoaded) {
dataCache[nodeId] = {};
return dataCache[nodeId];
} else {
const fetchedData = JSON.parse(dataLoaded);
dataCache[nodeId] = fetchedData; // Cache the fetched data
return fetchedData;
}
};

const handleToggle = async (event, nodeIds) => {
setExpanded(nodeIds);

const nodeId = nodeIds;
const fetchedData = await fetchData(nodeId);
const updatedDataChildren = await hasChildren(fetchedData.children);
const updatedData = { ...fetchedData, children: updatedDataChildren };
const paths = updatedData.path.split("/").slice(2);
const newData = updateData(paths, updatedData);
setData(newData);

if (
fetchedData &&
fetchedData?.children &&
fetchedData?.children?.length > 0
) {
const updatedDataChildren = await hasChildren(fetchedData.children);
const updatedData = { ...fetchedData, children: updatedDataChildren };
const paths = updatedData?.path?.split("/")?.slice(2);
const newData = updateData(paths, updatedData);

setData(newData);
}
};

const updateData = (path, updatedData) => {
Expand All @@ -71,7 +88,7 @@ const OrgUnitFilter = (props) => {
return { ...node, children: updatedData.children };
}

if (!node.children) {
if (!node.children || node.hasChildren === false) {
return node;
}

Expand All @@ -90,16 +107,31 @@ const OrgUnitFilter = (props) => {
};

const hasChildren = async (nodes) => {
const updatedNodes = [];

for (const node of nodes) {
const urlChildren = `${apiBase}api/organisationUnits/${node.id}?fields=path,children%3A%3Asize`;
const urlChildren = `${apiBase}api/organisationUnits/${node.id}?fields=path,level,children%3A%3Asize`;
const response = await fetch(urlChildren);

if (!response.ok) {
throw new Error("Failed to fetch data");
}
const data = await response.json();
node.hasChildren = parseInt(data.children) > 0;

const dataLoaded = await response.text();

const updatedNode = { ...node };
if ("" === dataLoaded) {
updatedNode.hasChildren = false;
} else {
const data = JSON.parse(dataLoaded);
updatedNode.hasChildren =
parseInt(data.children) > 0 &&
data.level < settings?.orgUnitLimit?.level - 1;
}

updatedNodes.push(updatedNode);
}
return nodes;
return updatedNodes;
};

const handleSelect = (event, nodeId) => {
Expand Down
45 changes: 36 additions & 9 deletions src/Components/OrgUnitFilterModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import FilterListIcon from "@mui/icons-material/FilterList";
import IconButton from "@mui/material/IconButton";
import Badge from "@mui/material/Badge";
import Tooltip from "@mui/material/Tooltip";
import useSetting from "../hooks/useSettings";

const OrgUnitFilterModal = ({ onConfirmed }) => {
const OrgUnitFilterModal = ({ onConfirmed, settings }) => {
const [open, setOpen] = useState(false);
const [data, setData] = useState(null);
const [orgUnitGroups, setOrgUnitGroups] = useState([]);
Expand All @@ -25,7 +26,11 @@ const OrgUnitFilterModal = ({ onConfirmed }) => {
const [hideEmptyCharts, setHideEmptyCharts] = useState(false);

const fetchData = async () => {
const url = `${apiBase}api/organisationUnits/b3aCK1PTn5S?fields=displayName, path, id, children%5Bid%2Cpath%2CdisplayName%5D`;
const url = `${apiBase}api/organisationUnits/b3aCK1PTn5S?fields=displayName, path, id, children%5Bid%2Cpath%2CdisplayName%5D ${
settings?.orgUnitLimit?.level
? `&filter=children.level:lt:${settings?.orgUnitLimit?.level}&filter=level:lt:${settings?.orgUnitLimit?.level}`
: ""
}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error("Failed to fetch data");
Expand All @@ -47,36 +52,57 @@ const OrgUnitFilterModal = ({ onConfirmed }) => {
};

const fetchOrgUnitLevels = async () => {
const url = `${apiBase}api/organisationUnitLevels?paging=false`;
console.log("settings loaded: ", settings);
const url = `${apiBase}api/organisationUnitLevels?paging=false${
settings?.orgUnitLimit?.level
? `&filter=level:lt:${settings?.orgUnitLimit?.level}`
: ""
}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error("Failed to fetch data");
}

const data = await response.json();

setOrgUnitLevels(data.organisationUnitLevels);
};

const hasChildren = async (nodes) => {
console.log("nodes:", nodes);
for (const node of nodes) {
const urlChildren = `${apiBase}api/organisationUnits/${node.id}?fields=path,children%3A%3Asize`;
const urlChildren = `${apiBase}api/organisationUnits/${
node.id
}?fields=path,children%3A%3Asize${
settings?.orgUnitLimit?.level
? `&filter=children.level:lt:${settings?.orgUnitLimit?.level}&filter=level:lt:${settings?.orgUnitLimit?.level}`
: ""
}`;

const response = await fetch(urlChildren);
if (!response.ok) {
throw new Error("Failed to fetch data");
}
const data = await response.json();
if (parseInt(data.children) > 0) {
node.hasChildren = true;

let dataLoaded = await response.text();

if ("" === dataLoaded) {
node.hasChildren = false;
} else {
const data = JSON.parse(dataLoaded);
if (parseInt(data.children) > 0) {
node.hasChildren = true;
}
}
}
return nodes;
};

useEffect(() => {
if (settings === null) return;
fetchData();
fetchOrgUnitGroups();
fetchOrgUnitLevels();
}, []);
}, [settings]);

const handleClickOpen = () => {
setOpen(true);
Expand Down Expand Up @@ -200,6 +226,7 @@ const OrgUnitFilterModal = ({ onConfirmed }) => {
setSelectedOrgUnitLevel={setSelectedOrgUnitLevel}
hideEmptyCharts={hideEmptyCharts}
setHideEmptyCharts={setHideEmptyCharts}
settings={settings}
/>
) : (
<CircularProgress size={24} />
Expand Down
29 changes: 29 additions & 0 deletions src/hooks/useSettings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// a hook to load settings from server dhis2 object store

import { useEffect, useState } from "react";

const apiBase = process.env.REACT_APP_BASE_URI;
const useSettings = () => {
const [settings, setSettings] = useState(null);

useEffect(() => {
const fetchSettings = async () => {
try {
const response = await fetch(
apiBase + "api/dataStore/public-dashboard/settings"
);
const data = await response.json();
setSettings(data);
} catch (error) {
setSettings({});
console.error("Error fetching settings:", error);
}
};

fetchSettings();
}, []);

return settings;
};

export default useSettings;

0 comments on commit a8713bd

Please sign in to comment.