diff --git a/src/Components/Chart.js b/src/Components/Chart.js index ca3fc66..525606d 100644 --- a/src/Components/Chart.js +++ b/src/Components/Chart.js @@ -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, @@ -179,7 +180,10 @@ export default function Chart({ - + diff --git a/src/Components/Dashboard.js b/src/Components/Dashboard.js index fb491c2..d915b89 100644 --- a/src/Components/Dashboard.js +++ b/src/Components/Dashboard.js @@ -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: [ @@ -49,6 +50,9 @@ export default function Dashboard() { const [savedReports, setSavedReports] = React.useState( JSON.parse(localStorage.getItem("saved_reports")) ); + + const settings = useSettings(); + return ( {/* Chart */} ({ 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; @@ -39,16 +39,25 @@ 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) => { @@ -56,11 +65,19 @@ const OrgUnitFilter = (props) => { 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) => { @@ -71,7 +88,7 @@ const OrgUnitFilter = (props) => { return { ...node, children: updatedData.children }; } - if (!node.children) { + if (!node.children || node.hasChildren === false) { return node; } @@ -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) => { diff --git a/src/Components/OrgUnitFilterModal.js b/src/Components/OrgUnitFilterModal.js index 8cd4f45..f883277 100644 --- a/src/Components/OrgUnitFilterModal.js +++ b/src/Components/OrgUnitFilterModal.js @@ -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([]); @@ -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"); @@ -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); @@ -200,6 +226,7 @@ const OrgUnitFilterModal = ({ onConfirmed }) => { setSelectedOrgUnitLevel={setSelectedOrgUnitLevel} hideEmptyCharts={hideEmptyCharts} setHideEmptyCharts={setHideEmptyCharts} + settings={settings} /> ) : ( diff --git a/src/hooks/useSettings.js b/src/hooks/useSettings.js new file mode 100644 index 0000000..66d7ccf --- /dev/null +++ b/src/hooks/useSettings.js @@ -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;