From 047f0b9fe46c32af2730bddf53b8b19fc813620e Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Tue, 8 Aug 2023 13:18:01 +0800 Subject: [PATCH 1/8] [#155] Install Dexie --- frontend/package.json | 2 ++ frontend/yarn.lock | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/frontend/package.json b/frontend/package.json index 22a60b1a..68b70b65 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,8 @@ "axios": "^1.2.0", "d3-geo": "^3.0.1", "d3-scale": "^4.0.2", + "dexie": "^3.2.4", + "dexie-react-hooks": "^1.1.6", "echarts": "^5.4.2", "echarts-for-react": "^3.0.2", "leaflet": "^1.9.3", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 3daf5f8d..4b2ce58c 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -3992,6 +3992,16 @@ detect-port-alt@^1.1.6: address "^1.0.1" debug "^2.6.0" +dexie-react-hooks@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/dexie-react-hooks/-/dexie-react-hooks-1.1.6.tgz#42d4d4d314b049c23e2abe20c8543107ed85b0bd" + integrity sha512-xSblWtmPwhafWNWMECsW7zMMmBu8goH3QqTxEfwBNoNG1mgsM0oFclippev7ss9HhKICqBwTjgqpscci5Ed4mA== + +dexie@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/dexie/-/dexie-3.2.4.tgz#b22a9729be1102acb2eee16102ea6e2bc76454cf" + integrity sha512-VKoTQRSv7+RnffpOJ3Dh6ozknBqzWw/F3iqMdsZg958R0AS8AnY9x9d1lbwENr0gzeGJHXKcGhAMRaqys6SxqA== + didyoumean@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" From 03a93f6c23e4f9599ab2efcef1ccaab0834b0354 Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Tue, 8 Aug 2023 13:18:41 +0800 Subject: [PATCH 2/8] [#155] Create db.js --- frontend/src/lib/db.js | 48 +++++++++++++++++++++++++++++++++++++++ frontend/src/lib/index.js | 1 + 2 files changed, 49 insertions(+) create mode 100644 frontend/src/lib/db.js diff --git a/frontend/src/lib/db.js b/frontend/src/lib/db.js new file mode 100644 index 00000000..bcecfcd1 --- /dev/null +++ b/frontend/src/lib/db.js @@ -0,0 +1,48 @@ +import Dexie from "dexie"; + +const dbName = "siwins"; +const db = new Dexie(dbName); + +db.version(1).stores({ + sync: "++id, cursor", + sources: "++endpoint, data", +}); + +const checkDB = () => + Dexie.exists(dbName) + .then((exists) => { + if (exists) { + console.info("Database exists"); + } else { + console.info("Database doesn't exist"); + } + }) + .catch((e) => { + console.error( + "Oops, an error occurred when trying to check database existance" + ); + console.error(e); + }); + +const getSource = async (endpoint) => { + const res = await db.sources.get({ endpoint }); + return { + ...res, + data: JSON.parse(res.data), + }; +}; + +const saveSource = ({ endpoint, data }) => { + return db.sources.put({ endpoint, data: JSON.stringify(data) }); +}; + +const ds = { + checkDB, + getSource, + saveSource, + truncateSources: () => db.sources.clear(), + getCursor: async () => await db.sync.get({ id: 1 }), + saveCursor: ({ cursor }) => db.sync.put({ id: 1, cursor }), +}; + +export default ds; diff --git a/frontend/src/lib/index.js b/frontend/src/lib/index.js index 78703cad..e9afe176 100644 --- a/frontend/src/lib/index.js +++ b/frontend/src/lib/index.js @@ -1 +1,2 @@ export { default as api } from "./api"; +export { default as ds } from "./db"; From 2048e34429365c0fef4a8ef75d56ac404d287d7b Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Tue, 8 Aug 2023 13:18:59 +0800 Subject: [PATCH 3/8] [#155] Initiall indexed db implementation --- frontend/src/App.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/frontend/src/App.js b/frontend/src/App.js index bb4b8712..4f8f85d4 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -4,21 +4,33 @@ import { Routes, Route, useLocation } from "react-router-dom"; import { Layout } from "./components"; import { Home, DashboardView, ErrorPage } from "./pages"; import { UIState } from "./state/ui"; -import { api } from "./lib"; +import { api, ds } from "./lib"; const App = () => { const location = useLocation(); useEffect(() => { + // #TODO:: Fetch cursor here (Replace with correct value) + ds.saveCursor({ cursor: 456 }); + // const url = `chart/number_of_school`; - api - .get(url) - .then((res) => { + ds.getSource(url).then((cachedData) => { + if (!cachedData?.endpoint) { + api + .get(url) + .then((res) => { + ds.saveSource({ endpoint: url, data: res.data }); + UIState.update((s) => { + s.schoolTotal = res?.data?.total; + }); + }) + .catch((e) => console.error(e)); + } else { UIState.update((s) => { - s.schoolTotal = res?.data?.total; + s.schoolTotal = cachedData.data.total; }); - }) - .catch((e) => console.error(e)); + } + }); }, []); return ( From d0e022807d2d4eac549086338662f5cb232fc0ca Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Tue, 8 Aug 2023 13:33:20 +0800 Subject: [PATCH 4/8] [#155] Implement indexed DB on home page --- frontend/src/App.js | 1 + frontend/src/pages/home/Home.jsx | 29 ++++++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/frontend/src/App.js b/frontend/src/App.js index 4f8f85d4..eef69ad2 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -14,6 +14,7 @@ const App = () => { ds.saveCursor({ cursor: 456 }); // const url = `chart/number_of_school`; + // check indexed DB first ds.getSource(url).then((cachedData) => { if (!cachedData?.endpoint) { api diff --git a/frontend/src/pages/home/Home.jsx b/frontend/src/pages/home/Home.jsx index 7e05d37d..153ebf8b 100644 --- a/frontend/src/pages/home/Home.jsx +++ b/frontend/src/pages/home/Home.jsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import "./style.scss"; import { Col, Row, Button, Image, Card, Statistic } from "antd"; import { ArrowDownOutlined } from "@ant-design/icons"; -import { api } from "../../lib"; +import { api, ds } from "../../lib"; import { Chart } from "../../components"; import CountUp from "react-countup"; import { UIState } from "../../state/ui"; @@ -21,20 +21,31 @@ const Home = () => { const [chartList, setChartList] = useState([]); useEffect(() => { + const dsKey = "home/chart/bar?name=overview-charts"; setLoading(true); + const chartList = chartConfig.find( (item) => item.component === "OVERVIEW-CHARTS" )?.chartList; setChartList(chartList); - const apiCall = chartList?.map((chart) => { - const url = `chart/bar?name=${chart?.path}`; - return api.get(url); - }); - sequentialPromise(apiCall).then((res) => { - const dataTemp = res.map((r) => r.data).flat(); - setData(dataTemp); - setLoading(false); + // check indexed DB first + ds.getSource(dsKey).then((cachedData) => { + if (!cachedData?.endpoint) { + const apiCall = chartList?.map((chart) => { + const url = `chart/bar?name=${chart?.path}`; + return api.get(url); + }); + sequentialPromise(apiCall).then((res) => { + const dataTemp = res.map((r) => r.data).flat(); + ds.saveSource({ endpoint: dsKey, data: dataTemp }); + setData(dataTemp); + setLoading(false); + }); + } else { + setData(cachedData.data); + setLoading(false); + } }); }, []); From 7f981631a28c1ebfc75e61e32aab82b23b2ee591 Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Tue, 8 Aug 2023 14:57:52 +0800 Subject: [PATCH 5/8] [#155] Save dropdown resources to indexed DB --- frontend/src/App.js | 40 ++++++----- frontend/src/lib/db.js | 5 +- frontend/src/pages/dashboard/index.jsx | 97 ++++++++++++++++++++------ frontend/src/pages/home/Home.jsx | 4 +- 4 files changed, 103 insertions(+), 43 deletions(-) diff --git a/frontend/src/App.js b/frontend/src/App.js index eef69ad2..c1649c89 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -13,25 +13,29 @@ const App = () => { // #TODO:: Fetch cursor here (Replace with correct value) ds.saveCursor({ cursor: 456 }); // - const url = `chart/number_of_school`; + const url = `/chart/number_of_school`; // check indexed DB first - ds.getSource(url).then((cachedData) => { - if (!cachedData?.endpoint) { - api - .get(url) - .then((res) => { - ds.saveSource({ endpoint: url, data: res.data }); - UIState.update((s) => { - s.schoolTotal = res?.data?.total; - }); - }) - .catch((e) => console.error(e)); - } else { - UIState.update((s) => { - s.schoolTotal = cachedData.data.total; - }); - } - }); + ds.getSource(url) + .then((cachedData) => { + if (!cachedData) { + api + .get(url) + .then((res) => { + ds.saveSource({ endpoint: url, data: res.data }); + UIState.update((s) => { + s.schoolTotal = res?.data?.total; + }); + }) + .catch((e) => console.error(e)); + } else { + UIState.update((s) => { + s.schoolTotal = cachedData.data.total; + }); + } + }) + .catch((e) => { + console.error("[Failed fetch indexed DB sources table]", e); + }); }, []); return ( diff --git a/frontend/src/lib/db.js b/frontend/src/lib/db.js index bcecfcd1..ab183892 100644 --- a/frontend/src/lib/db.js +++ b/frontend/src/lib/db.js @@ -5,7 +5,7 @@ const db = new Dexie(dbName); db.version(1).stores({ sync: "++id, cursor", - sources: "++endpoint, data", + sources: "++endpoint, data", // store resources of dropdown data }); const checkDB = () => @@ -26,6 +26,9 @@ const checkDB = () => const getSource = async (endpoint) => { const res = await db.sources.get({ endpoint }); + if (!res) { + return null; + } return { ...res, data: JSON.parse(res.data), diff --git a/frontend/src/pages/dashboard/index.jsx b/frontend/src/pages/dashboard/index.jsx index 2b81e53b..19de1ae8 100644 --- a/frontend/src/pages/dashboard/index.jsx +++ b/frontend/src/pages/dashboard/index.jsx @@ -17,7 +17,7 @@ import Maps from "./Maps"; import Dashboard from "./Dashboard"; import ManageData from "./ManageData"; import { UIState } from "../../state/ui"; -import { api } from "../../lib"; +import { api, ds } from "../../lib"; const menuItems = [ { label: "Maps", link: "/dashboard/maps", icon: , key: "1" }, @@ -30,37 +30,90 @@ const menuItems = [ }, ]; +const dropdownResourceURL = [ + "/question?attribute=indicator", + "/question?attribute=advance_filter", + "/question?attribute=generic_bar_chart", + "/cascade/school_information?level=province", + "/cascade/school_information?level=school_type", +]; + const DashboardView = () => { const location = useLocation(); const navigate = useNavigate(); const [collapsed, setCollapsed] = useState(true); + const [fetchFromAPI, setFetchFromAPI] = useState(false); const { schoolTotal } = UIState.useState((s) => s); useEffect(() => { - Promise.all([ - api.get("/question?attribute=indicator"), - api.get("/question?attribute=advance_filter"), - api.get("/question?attribute=generic_bar_chart"), - api.get("/cascade/school_information?level=province"), - api.get("/cascade/school_information?level=school_type"), - ]).then((res) => { - const [ - indicatorQuestions, - advanceFilterQuestions, - generic_bar_chart, - province, - school_type, - ] = res; - UIState.update((s) => { - s.indicatorQuestions = indicatorQuestions?.data; - s.advanceFilterQuestions = advanceFilterQuestions?.data; - s.barChartQuestions = generic_bar_chart?.data; - s.provinceValues = province?.data; - s.schoolTypeValues = school_type?.data; - }); + // ** fetch dropdown sources from indexed DB first + const dsApiCalls = dropdownResourceURL.map((url) => ds.getSource(url)); + Promise.all(dsApiCalls).then((cachedData) => { + const nullInsideRes = cachedData.filter((x) => !x)?.length; + if (nullInsideRes) { + setFetchFromAPI(true); + } else { + const [ + indicatorQuestions, + advanceFilterQuestions, + generic_bar_chart, + province, + school_type, + ] = cachedData; + UIState.update((s) => { + s.indicatorQuestions = indicatorQuestions?.data; + s.advanceFilterQuestions = advanceFilterQuestions?.data; + s.barChartQuestions = generic_bar_chart?.data; + s.provinceValues = province?.data; + s.schoolTypeValues = school_type?.data; + }); + } }); }, []); + useEffect(() => { + // ** fetch from API if indexed DB not defined + if (fetchFromAPI) { + const apiCalls = dropdownResourceURL.map((url) => api.get(url)); + Promise.all(apiCalls).then((res) => { + const [ + indicatorQuestions, + advanceFilterQuestions, + generic_bar_chart, + province, + school_type, + ] = res; + // save to indexed DB + ds.saveSource({ + endpoint: indicatorQuestions.config.url, + data: indicatorQuestions.data, + }); + ds.saveSource({ + endpoint: advanceFilterQuestions.config.url, + data: advanceFilterQuestions.data, + }); + ds.saveSource({ + endpoint: generic_bar_chart.config.url, + data: generic_bar_chart.data, + }); + ds.saveSource({ endpoint: province.config.url, data: province.data }); + ds.saveSource({ + endpoint: school_type.config.url, + data: school_type.data, + }); + // + UIState.update((s) => { + s.indicatorQuestions = indicatorQuestions?.data; + s.advanceFilterQuestions = advanceFilterQuestions?.data; + s.barChartQuestions = generic_bar_chart?.data; + s.provinceValues = province?.data; + s.schoolTypeValues = school_type?.data; + }); + setFetchFromAPI(false); + }); + } + }, [fetchFromAPI]); + const handleOnClickMenu = ({ key }) => { const link = menuItems.find((x) => x.key === key)?.link; navigate(link); diff --git a/frontend/src/pages/home/Home.jsx b/frontend/src/pages/home/Home.jsx index 153ebf8b..82d24963 100644 --- a/frontend/src/pages/home/Home.jsx +++ b/frontend/src/pages/home/Home.jsx @@ -21,7 +21,7 @@ const Home = () => { const [chartList, setChartList] = useState([]); useEffect(() => { - const dsKey = "home/chart/bar?name=overview-charts"; + const dsKey = "/home/chart/bar?name=overview-charts"; setLoading(true); const chartList = chartConfig.find( @@ -31,7 +31,7 @@ const Home = () => { // check indexed DB first ds.getSource(dsKey).then((cachedData) => { - if (!cachedData?.endpoint) { + if (!cachedData) { const apiCall = chartList?.map((chart) => { const url = `chart/bar?name=${chart?.path}`; return api.get(url); From 8d59a2a3b7e20a073b2048a6bdc02aa584fa6d02 Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Tue, 8 Aug 2023 15:10:00 +0800 Subject: [PATCH 6/8] [#155] Add more maps & dashboards table to indexed DB --- frontend/src/lib/db.js | 44 +++++++++++++++++++++- frontend/src/pages/dashboard/Dashboard.jsx | 19 +++++----- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/frontend/src/lib/db.js b/frontend/src/lib/db.js index ab183892..df0d2e70 100644 --- a/frontend/src/lib/db.js +++ b/frontend/src/lib/db.js @@ -6,6 +6,8 @@ const db = new Dexie(dbName); db.version(1).stores({ sync: "++id, cursor", sources: "++endpoint, data", // store resources of dropdown data + maps: "++endpoint, data", // store data of maps page + dashboards: "++endpoint, data", // store data of dashboard page }); const checkDB = () => @@ -24,6 +26,12 @@ const checkDB = () => console.error(e); }); +const truncateTables = () => { + db.sources.clear(); + db.maps.clear(); + db.dashboards.clear(); +}; + const getSource = async (endpoint) => { const res = await db.sources.get({ endpoint }); if (!res) { @@ -39,11 +47,45 @@ const saveSource = ({ endpoint, data }) => { return db.sources.put({ endpoint, data: JSON.stringify(data) }); }; +const getMap = async (endpoint) => { + const res = await db.maps.get({ endpoint }); + if (!res) { + return null; + } + return { + ...res, + data: JSON.parse(res.data), + }; +}; + +const saveMap = ({ endpoint, data }) => { + return db.maps.put({ endpoint, data: JSON.stringify(data) }); +}; + +const getDashboard = async (endpoint) => { + const res = await db.dashboards.get({ endpoint }); + if (!res) { + return null; + } + return { + ...res, + data: JSON.parse(res.data), + }; +}; + +const saveDashboard = ({ endpoint, data }) => { + return db.dashboards.put({ endpoint, data: JSON.stringify(data) }); +}; + const ds = { checkDB, + truncateTables, getSource, saveSource, - truncateSources: () => db.sources.clear(), + getMap, + saveMap, + getDashboard, + saveDashboard, getCursor: async () => await db.sync.get({ id: 1 }), saveCursor: ({ cursor }) => db.sync.put({ id: 1, cursor }), }; diff --git a/frontend/src/pages/dashboard/Dashboard.jsx b/frontend/src/pages/dashboard/Dashboard.jsx index b37d4372..0edcbc55 100644 --- a/frontend/src/pages/dashboard/Dashboard.jsx +++ b/frontend/src/pages/dashboard/Dashboard.jsx @@ -79,16 +79,17 @@ const Dashboard = () => { useEffect(() => { if (!selectedIndicator) { setBarChartData([]); + } else { + let url = `chart/generic-bar/${selectedIndicator}`; + url = generateAdvanceFilterURL(advanceSearchValue, url); + url = generateFilterURL(provinceFilterValue, url); + api + .get(url) + .then((res) => { + setBarChartData(res.data); + }) + .catch((e) => console.error(e)); } - let url = `chart/generic-bar/${selectedIndicator}`; - url = generateAdvanceFilterURL(advanceSearchValue, url); - url = generateFilterURL(provinceFilterValue, url); - api - .get(url) - .then((res) => { - setBarChartData(res.data); - }) - .catch((e) => console.error(e)); }, [selectedIndicator, advanceSearchValue, provinceFilterValue]); const renderColumn = (cfg, index) => { From 6cf11c92b464b43d2132d60403e49783ebc9b57d Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Tue, 8 Aug 2023 15:40:32 +0800 Subject: [PATCH 7/8] [#155] Implement indexed DB on dashboard page --- frontend/src/pages/dashboard/Dashboard.jsx | 67 ++++++++++++++-------- frontend/src/pages/home/Home.jsx | 2 +- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/frontend/src/pages/dashboard/Dashboard.jsx b/frontend/src/pages/dashboard/Dashboard.jsx index 0edcbc55..c1009448 100644 --- a/frontend/src/pages/dashboard/Dashboard.jsx +++ b/frontend/src/pages/dashboard/Dashboard.jsx @@ -1,16 +1,12 @@ /* eslint-disable react-hooks/exhaustive-deps */ import React, { useEffect, useState } from "react"; import { Row, Col, Select, Breadcrumb } from "antd"; -import { api } from "../../lib"; +import { api, ds } from "../../lib"; import { UIState } from "../../state/ui"; import ChartVisual from "./components/ChartVisual"; import { Chart } from "../../components"; import AdvanceFilter from "../../components/filter"; -import { - generateAdvanceFilterURL, - generateFilterURL, - sequentialPromise, -} from "../../util/utils"; +import { generateAdvanceFilterURL, generateFilterURL } from "../../util/utils"; import { Link } from "react-router-dom"; import { orderBy } from "lodash"; @@ -63,15 +59,30 @@ const Dashboard = () => { ?.chartList ); setPageLoading(true); - const apiCall = chartList?.map((chart) => { - let url = `chart/jmp-data/${chart?.path}`; + chartList?.forEach((chart) => { + let url = `/chart/jmp-data/${chart?.path}`; url = generateAdvanceFilterURL(advanceSearchValue, url); url = generateFilterURL(provinceFilterValue, url); - return api.get(url); - }); - sequentialPromise(apiCall).then((res) => { - setData(res); - setPageLoading(false); + // ** fetch data from indexed DB first + ds.getDashboard(url) + .then(async (cachedData) => { + if (!cachedData) { + await api + .get(url) + .then((res) => { + ds.saveDashboard({ endpoint: url, data: res }); + setData((prevData) => [...prevData, res]); + }) + .catch((e) => { + console.error("[Error fetch JMP chart data]", e); + }); + } else { + setData((prevData) => [...prevData, cachedData.data]); + } + }) + .finally(() => { + setPageLoading(false); + }); }); }, [advanceSearchValue, provinceFilterValue]); @@ -79,17 +90,27 @@ const Dashboard = () => { useEffect(() => { if (!selectedIndicator) { setBarChartData([]); - } else { - let url = `chart/generic-bar/${selectedIndicator}`; - url = generateAdvanceFilterURL(advanceSearchValue, url); - url = generateFilterURL(provinceFilterValue, url); - api - .get(url) - .then((res) => { - setBarChartData(res.data); - }) - .catch((e) => console.error(e)); + return; } + let url = `/chart/generic-bar/${selectedIndicator}`; + url = generateAdvanceFilterURL(advanceSearchValue, url); + url = generateFilterURL(provinceFilterValue, url); + // ** fetch data from indexed DB first + ds.getDashboard(url).then((cachedData) => { + if (!cachedData) { + api + .get(url) + .then((res) => { + ds.saveDashboard({ endpoint: url, data: res.data }); + setBarChartData(res.data); + }) + .catch((e) => + console.error("[Error fetch Generic Bar chart data]", e) + ); + } else { + setBarChartData(cachedData.data); + } + }); }, [selectedIndicator, advanceSearchValue, provinceFilterValue]); const renderColumn = (cfg, index) => { diff --git a/frontend/src/pages/home/Home.jsx b/frontend/src/pages/home/Home.jsx index 82d24963..d1c72ca1 100644 --- a/frontend/src/pages/home/Home.jsx +++ b/frontend/src/pages/home/Home.jsx @@ -29,7 +29,7 @@ const Home = () => { )?.chartList; setChartList(chartList); - // check indexed DB first + // ** fetch data from indexed DB first ds.getSource(dsKey).then((cachedData) => { if (!cachedData) { const apiCall = chartList?.map((chart) => { From 46b031f00acbaf0d796c850268e275724b9c7686 Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Tue, 8 Aug 2023 15:49:39 +0800 Subject: [PATCH 8/8] [#155] Implement indexed DB to JMP history --- .../dashboard/components/ChartVisual.jsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/dashboard/components/ChartVisual.jsx b/frontend/src/pages/dashboard/components/ChartVisual.jsx index 7785ad7c..571b167e 100644 --- a/frontend/src/pages/dashboard/components/ChartVisual.jsx +++ b/frontend/src/pages/dashboard/components/ChartVisual.jsx @@ -3,7 +3,7 @@ import { Row, Col, Card, Switch, Space, Popover } from "antd"; import { Chart } from "../../../components"; import { get } from "lodash"; import { InfoCircleOutlined } from "@ant-design/icons"; -import { api } from "../../../lib"; +import { api, ds } from "../../../lib"; import { generateAdvanceFilterURL, generateFilterURL, @@ -32,9 +32,20 @@ const ChartVisual = ({ chartConfig, loading }) => { (async () => { const queryUrlPrefix = url.includes("?") ? "&" : "?"; url = `${url}${queryUrlPrefix}history=${showHistory}`; - const res = await api.get(url); - setLoading(false); - setHistoryData(res?.data?.data); + ds.getDashboard(url) + .then(async (cachedData) => { + if (!cachedData) { + await api.get(url).then((res) => { + ds.saveDashboard({ endpoint: url, data: res.data }); + setHistoryData(res?.data?.data); + }); + } else { + setHistoryData(cachedData.data.data); + } + }) + .finally(() => { + setLoading(false); + }); })(); } }, [showHistory, path, setLoading, advanceSearchValue, provinceFilterValue]);