diff --git a/control/content/app.js b/control/content/app.js index f0ba68b..db4f203 100644 --- a/control/content/app.js +++ b/control/content/app.js @@ -1,6 +1,12 @@ // The big object that contains all the metrics let metrics = {}; +// Object containes a specific client history +let histories = {}; + +// Client profile (client query) +let clientProfile = ""; + // We used nodeSelector to determine where are we inside the big object let nodeSelector = "metrics"; @@ -39,9 +45,15 @@ buildfire.messaging.sendMessageToWidget({ cmd: "refresh", }); +// Initialize clinet history +Histories.getHistories(clientProfile).then((result) => { + histories = result; +}); + // To get all metrics and start rendering Metrics.getMetrics().then((result) => { metrics = result; + initMaterialComponents(); // To prevent Functional Tests from Applying these lines where it will cause some errors renderInit(); @@ -240,19 +252,25 @@ const createMetric = () => { // Metric fields validation if (inputValidation()) { // Empty the form fields after submitting - metricFields.createdBy = `${currentUser.firstName} ${currentUser.lastName}`; - metricFields.lastUpdatedBy = `${currentUser.firstName} ${currentUser.lastName}`; + metricFields.createdBy = + currentUser && currentUser.username ? currentUser.username : null; + metricFields.lastUpdatedBy = + currentUser && currentUser.username ? currentUser.username : null; metricFields.order = metricsList.childNodes.length; + let newMetric = new Metric(metricFields); + // Save metric Metrics.insert( { nodeSelector, metricsId: metrics.id, }, - new Metric(metricFields) + newMetric ).then((result) => { + // Assign the metrics value to metrics metrics = result; + renderInit(); goToMetricspage(); }); @@ -263,7 +281,8 @@ const updateMetrics = (item) => { // Metric fields validation if (inputValidation()) { let updateObj = {}; - updateObj.lastUpdatedBy = `${currentUser.firstName} ${currentUser.lastName}`; + updateObj.lastUpdatedBy = + currentUser && currentUser.username ? currentUser.username : null; for (let prop in metricFields) { // To determine which fileds are needed to be updated @@ -315,6 +334,7 @@ const updateMetricDB = (updateObj, itemId) => { itemId ).then((result) => { metrics = result; + renderInit(); goToMetricspage(); }); @@ -376,36 +396,44 @@ const inputValidation = () => { // To initialize and prepare metrics to be rendered const renderInit = () => { metricsContainer = metricsList; - // Extract the desired metrics (children) from the big object using nodeSelector - let readyMetrics = helpers.nodeSplitter(nodeSelector, metrics); - let metricsChildren = readyMetrics.metricsChildren; - - wysiwygSetContent(readyMetrics.description); - - let currentMetricList = []; - // Prepare metrics to be rendered (Object to Array) - for (let metricId in metricsChildren) { - metricsChildren[metricId].id = metricId; - let newMetric = new Metric(metricsChildren[metricId]); - Metric.getHistoryValue(newMetric); - currentMetricList.push(newMetric); - } - // To show messages while metrics being rendered or if there is no metrics at all - let spinner = document.getElementById("spinner"); - if (currentMetricList.length === 0) { - spinner.innerHTML = "No metrics have been added yet."; - spinner.classList.remove("loaded"); - metricsContainer.innerHTML = ""; - } else { - spinner.innerHTML = ""; - spinner.classList.add("loaded"); - metricsContainer.innerHTML = ""; - } + // Filter Metrics before rendering + helpers + .filterCustomerMetrics(metrics, clientProfile) + .then((filteredMetrics) => { + metrics.metrics = filteredMetrics; + + // Extract the desired metrics (children) from the big object using nodeSelector + let readyMetrics = helpers.nodeSplitter(nodeSelector, metrics); + let metricsChildren = readyMetrics.metricsChildren; + + wysiwygSetContent(readyMetrics.description); + + let currentMetricList = []; + // Prepare metrics to be rendered (Object to Array) + for (let metricId in metricsChildren) { + metricsChildren[metricId].id = metricId; + let newMetric = new Metric(metricsChildren[metricId]); + Metric.getHistoryValue(newMetric); + currentMetricList.push(newMetric); + } - currentMetricList = helpers.sortMetrics(currentMetricList, metricsSortBy); + // To show messages while metrics being rendered or if there is no metrics at all + let spinner = document.getElementById("spinner"); + if (currentMetricList.length === 0) { + spinner.innerHTML = "No metrics have been added yet."; + spinner.classList.remove("loaded"); + metricsContainer.innerHTML = ""; + } else { + spinner.innerHTML = ""; + spinner.classList.add("loaded"); + metricsContainer.innerHTML = ""; + } - render(currentMetricList); + currentMetricList = helpers.sortMetrics(currentMetricList, metricsSortBy); + + render(currentMetricList); + }); }; // To render metrics @@ -479,14 +507,16 @@ const deleteItem = (item, index, callback) => { if (e) console.error(e); if (data && data.selectedButton.key == "y") { sortableList.items.splice(index, 1); - Metrics.delete({ nodeSelector, metricsId: metrics.id }, item.id) - .then((result) => { + // Delete the item from the client history db + Metrics.delete({ nodeSelector, metricsId: metrics.id }, item.id).then( + (result) => { + // Assign the metrics value to metrics metrics = result; + callback(metrics); - }) - .finally(() => { renderInit(); - }); + } + ); } } ); @@ -555,21 +585,29 @@ const pushBreadcrumb = (breadcrumb, data) => { // To synchronize with the widget buildfire.messaging.onReceivedMessage = (message) => { - // If message has title then it is a push breacrumb - if (message.title) { - nodeSelector = message.nodeSelector; - pushBreadcrumb(message.title, { nodeSelector }); - // If it is not then it is a backward breadcrumb - } else { - if (nodeSelector !== message.nodeSelector) { + if (breadcrumbsHistory && breadcrumbsHistory.length) { + // If message has title then it is a push breacrumb + if (message.title) { nodeSelector = message.nodeSelector; - bread.removeChild(bread.lastChild); - bread.removeChild(bread.lastChild); - breadcrumbsHistory.pop(); + pushBreadcrumb(message.title, { nodeSelector }); + // If it is not then it is a backward breadcrumb + } else { + if ( + nodeSelector && + nodeSelector !== message.nodeSelector && + bread.children.length > 2 + ) { + nodeSelector = message.nodeSelector; + bread.removeChild(bread.lastChild); + bread.removeChild(bread.lastChild); + breadcrumbsHistory.pop(); + } else { + return; + } } + renderInit(); + goToMetricspage(); } - renderInit(); - goToMetricspage(); }; // To handle users' choices for sorting @@ -585,10 +623,6 @@ const onSortByChange = () => { ).then((result) => { metrics = result; metricsSortBy = sortBy; - // if (metricsSortBy === "highest" || metricsSortBy === "lowest") { - // // Disable manual sorting - // sortableList.sortableList.option("disabled", true); - // } renderInit(); }); }; @@ -600,5 +634,12 @@ const updateDescription = (description) => { "description" ).then((result) => { metrics = result; + + // Filter Metrics on data change + helpers + .filterCustomerMetrics(metrics, clientProfile) + .then((filteredMetrics) => { + metrics.metrics = filteredMetrics; + }); }); }; diff --git a/control/content/index.html b/control/content/index.html index 54b1070..858387f 100644 --- a/control/content/index.html +++ b/control/content/index.html @@ -284,6 +284,8 @@

Metrics

+ + diff --git a/control/content/js/classes/histories.js b/control/content/js/classes/histories.js new file mode 100644 index 0000000..e02fa04 --- /dev/null +++ b/control/content/js/classes/histories.js @@ -0,0 +1,82 @@ +class Histories { + constructor() {} + + // Get the big Object (history object) + static getHistories(clientProfile) { + return new Promise((resolve, reject) => { + buildfire.publicData.get( + `history${clientProfile}`, + (err, result) => { + if (err) reject(err); + else { + // Check if there is already objects in the database + if (!result.data.metrics) { + // If there is no object, then create the parent object + buildfire.publicData.save( + { metrics: {} }, + `history${clientProfile}`, + (err, result) => { + if (err) reject(err); + else { + this.getHistories().then((result) => { + resolve(result); + }); + } + } + ); + } else { + result.data.id = result.id; + resolve(result.data); + } + } + } + ); + }); + } + + // // Add new history in the big object (Control Panel Only) + // static insert({ clientProfile, nodeSelector, historyId }, history) { + // return new Promise((resolve, reject) => { + // if (!nodeSelector) reject("nodeSelector not provided"); + // if (!historyId) reject("historyId not provided"); + + // buildfire.publicData.update( + // historyId, + // { $set: { [`${nodeSelector}.${history.id}`]: history } }, + // `history${clientProfile}`, + // (err, result) => { + // if (err) reject(err); + // else { + // result.data.id = historyId; + // resolve(result.data); + // } + // } + // ); + // }); + // } + + // // To delete any history (Control Panel Only) + // static delete({ clientProfile, nodeSelector, historyId }, id) { + // return new Promise((resolve, reject) => { + // if (!nodeSelector) reject("nodeSelector not provided"); + // if (!historyId) reject("historyId not provided"); + + // buildfire.publicData.update( + // historyId, + // { + // $unset: { + // [`${nodeSelector}.${id}`]: "", + // }, + // }, + // `history${clientProfile}`, + // (err, result) => { + // if (err) reject(err); + // else { + // result.data.id = historyId; + // resolve(result.data); + // } + // } + // ); + // }); + // } +} diff --git a/control/content/js/classes/history.js b/control/content/js/classes/history.js new file mode 100644 index 0000000..29b7841 --- /dev/null +++ b/control/content/js/classes/history.js @@ -0,0 +1,6 @@ +class ClientHistory { + constructor(data = {}) { + this.id = data.id || ""; + this.history = data.history || []; + } +} diff --git a/control/content/js/classes/metric.js b/control/content/js/classes/metric.js index a51c641..523da88 100644 --- a/control/content/js/classes/metric.js +++ b/control/content/js/classes/metric.js @@ -1,6 +1,6 @@ class Metric { constructor(data = {}) { - this.id = data.id || ""; + this.id = data.id || helpers.uuidv4(); this.title = data.title || ""; this.icon = data.icon || ""; this.min = data.min || 0; @@ -43,7 +43,7 @@ class Metric { return avg; } } - //TODO: check if it affect the avg claculations + // TODO: check if it affect the avg claculations return 0; } } diff --git a/control/content/js/classes/metrics.js b/control/content/js/classes/metrics.js index d6771f5..6b9c318 100644 --- a/control/content/js/classes/metrics.js +++ b/control/content/js/classes/metrics.js @@ -4,13 +4,13 @@ class Metrics { // Get the big Object (metrics object) static getMetrics() { return new Promise((resolve, reject) => { - buildfire.publicData.get("metrics", (err, result) => { + buildfire.datastore.get("metrics", (err, result) => { if (err) reject(err); else { // Check if there is already objects in the database if (!result.data.metrics) { // If there is no object, then create the parent object - buildfire.publicData.save( + buildfire.datastore.save( { metrics: {}, sortBy: "manual" }, "metrics", (err, result) => { @@ -33,7 +33,7 @@ class Metrics { // Add new metrics in the big object (Control Panel Only) static insert({ nodeSelector, metricsId }, metric) { - metric.id = helpers.uuidv4(); + // metric.id = helpers.uuidv4(); metric.createdOn = new Date(); metric.lastUpdatedOn = new Date(); @@ -41,7 +41,7 @@ class Metrics { if (!nodeSelector) reject("nodeSelector not provided"); if (!metricsId) reject("metricsId not provided"); - buildfire.publicData.update( + buildfire.datastore.update( metricsId, { $set: { [`${nodeSelector}.${metric.id}`]: metric } }, "metrics", @@ -75,7 +75,7 @@ class Metrics { _set[`${nodeSelector}.${id}.lastUpdatedOn`] = new Date(); _set[`${nodeSelector}.${id}.lastUpdatedBy`] = data.lastUpdatedBy; - buildfire.publicData.update( + buildfire.datastore.update( metricsId, { $set: _set }, "metrics", @@ -96,7 +96,7 @@ class Metrics { if (!nodeSelector) reject("nodeSelector not provided"); if (!metricsId) reject("metricsId not provided"); - buildfire.publicData.update( + buildfire.datastore.update( metricsId, { $unset: { @@ -127,7 +127,7 @@ class Metrics { _set[`${nodeSelector}.${id}.order`] = orderObj[id]; } - buildfire.publicData.update( + buildfire.datastore.update( metricsId, { $set: _set }, "metrics", @@ -163,7 +163,7 @@ class Metrics { : (_set[`${selector}.description`] = value); } - buildfire.publicData.update( + buildfire.datastore.update( metricsId, { $set: _set }, "metrics", diff --git a/control/content/js/helper.js b/control/content/js/helper.js index 2aa2f72..ae8ec6e 100644 --- a/control/content/js/helper.js +++ b/control/content/js/helper.js @@ -111,4 +111,40 @@ const helpers = { elem.style.display = displayType || "block"; }); }, + // Filter Metrics based on the provided customer + filterCustomerMetrics(metrics, clientProfile) { + return new Promise((resolve, reject) => { + // Get client history data; + Histories.getHistories(clientProfile).then((result) => { + histories = result; + // Add the history data to each metric + this.addHistoryToMetrics(metrics, histories); + + resolve(metrics.metrics); + }); + }); + }, + // Loop recursively on the metrics object and add the history value from histories object + addHistoryToMetrics(metrics, histories) { + if (Object.keys(metrics.metrics).length > 0) { + for (var key in metrics.metrics) { + if ( + histories.metrics[key] && + metrics.metrics[key].type === "parent" && + Object.keys(metrics.metrics[key].metrics).length > 0 + ) { + this.addHistoryToMetrics( + metrics.metrics[key], + histories.metrics[key] + ); + } else { + if (histories.metrics[key] && histories.metrics[key].history) { + metrics.metrics[key].history = histories.metrics[key].history; + } else { + metrics.metrics[key].history = []; + } + } + } + } + }, }; diff --git a/control/content/style.css b/control/content/style.css index 13c7f8e..719e458 100644 --- a/control/content/style.css +++ b/control/content/style.css @@ -81,6 +81,14 @@ input:disabled + span { padding: 5px 15px; } +#bread { + margin-top: 20px; +} + +#bread span:nth-child(even) { + padding: 0 4px; +} + #bread .crumb { cursor: pointer; color: #00a1f3; @@ -167,6 +175,7 @@ input:disabled + span { } .screens .screen.layouticon a, .screens .layoutgrid .layouticon { + text-align: center; height: 124px; line-height: 121px; } @@ -392,7 +401,7 @@ select { .metric-actions { display: flex; - align-items: center; + align-items: right; } .bottom-actions { display: flex; diff --git a/control/settings/app.js b/control/settings/app.js index aaad2dd..d296fc7 100644 --- a/control/settings/app.js +++ b/control/settings/app.js @@ -62,7 +62,9 @@ const deleteTag = (index, name) => { const updateSettings = () => { return new Promise((resolve, reject) => { - Settings.save(`${currentUser.firstName} ${currentUser.lastName}`) + Settings.save( + currentUser && currentUser.username ? currentUser.username : null + ) .then(() => { resolve(); }) diff --git a/control/settings/index.html b/control/settings/index.html index 08be185..b2b07bb 100644 --- a/control/settings/index.html +++ b/control/settings/index.html @@ -58,19 +58,7 @@
-
-
- - - No tags are selected - - -
+

No tags are selected

diff --git a/control/tests/control.html b/control/tests/control.html index b24e353..6a5916c 100644 --- a/control/tests/control.html +++ b/control/tests/control.html @@ -57,6 +57,8 @@ + + diff --git a/control/tests/widget.html b/control/tests/widget.html index c913ca9..87edfec 100644 --- a/control/tests/widget.html +++ b/control/tests/widget.html @@ -59,6 +59,7 @@ + diff --git a/gulpfile.js b/gulpfile.js index e7e8091..631a753 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -134,7 +134,7 @@ jsTasks.forEach(function (task) { presets: ["@babel/preset-env"], }) ) - .pipe(uglify()) + // .pipe(uglify()) .pipe(concat("scripts.min.js")) .pipe(gulp.dest(destinationFolder + task.dest)); }); @@ -184,6 +184,7 @@ gulp.task("widgetHTML", function () { gulp.task("resources", function () { return gulp .src(["resources/*", "plugin.json"], { base: "." }) + .pipe(imagemin()) .pipe(gulp.dest(destinationFolder)); }); @@ -204,7 +205,7 @@ var buildTasksToRun = ["controlHTML", "widgetHTML", "resources", "images"]; gulp.task("zip", function () { return gulp - .src("./dist/**") + .src("./dist/**", { dot: true }) .pipe(zip("metricActionItemPlugin_release.zip")) .pipe(gulp.dest("../")); }); diff --git a/tests/control/content.spec.js b/tests/control/content.spec.js index 756b7e0..26d9c64 100644 --- a/tests/control/content.spec.js +++ b/tests/control/content.spec.js @@ -1,5 +1,7 @@ describe("Test The Control Side", () => { let metrics = {}; + let clientProfile = ""; + let nodeSelector = "metrics"; describe("Test the Metric class", () => { @@ -31,22 +33,17 @@ describe("Test The Control Side", () => { await Metrics.getMetrics().then((data) => { metrics = data; }); + + await Histories.getHistories(clientProfile).then((data) => { + histories = data; + }); }); let metric1 = new Metric({ actionItem: {}, createdBy: "Amjad Hamza", createdOn: new Date(), - history: [ - { - date: helpers.getAbsoluteDate(), - value: 56, - createdOn: new Date(), - createdBy: "Amjad Hamza", - lastUpdatedOn: new Date(), - lastUpdatedBy: "Amjad Hamza", - }, - ], + history: [], icon: "https://img.icons8.com/material/4ac144/256/user-male.png", lastUpdatedBy: "Amjad Hamza", lastUpdatedOn: new Date(), @@ -62,16 +59,7 @@ describe("Test The Control Side", () => { actionItem: {}, createdBy: "Amjad Hamza", createdOn: new Date(), - history: [ - { - date: helpers.getAbsoluteDate(), - value: 41, - createdOn: new Date(), - createdBy: "Amjad Hamza", - lastUpdatedOn: new Date(), - lastUpdatedBy: "Amjad Hamza", - }, - ], + history: [], icon: "https://img.icons8.com/material/4ac144/256/user-male.png", lastUpdatedBy: "Amjad Hamza", lastUpdatedOn: new Date(), @@ -87,16 +75,6 @@ describe("Test The Control Side", () => { actionItem: {}, createdBy: "Amjad Hamza", createdOn: new Date(), - history: [ - { - date: helpers.getAbsoluteDate(), - value: 23, - createdOn: new Date(), - createdBy: "Amjad Hamza", - lastUpdatedOn: new Date(), - lastUpdatedBy: "Amjad Hamza", - }, - ], icon: "https://img.icons8.com/material/4ac144/256/user-male.png", lastUpdatedBy: "Amjad Hamza", lastUpdatedOn: new Date(), @@ -109,7 +87,12 @@ describe("Test The Control Side", () => { }); it("Should return the metrics object without any errors", async () => { - await expectAsync(Metrics.getMetrics()).toBeResolved(); + await expectAsync( + Metrics.getMetrics().then(async (result) => { + metrics = result; + await helpers.filterCustomerMetrics(metrics, clientProfile); + }) + ).toBeResolved(); }); it("Should save metrics correctly", async () => { @@ -131,8 +114,18 @@ describe("Test The Control Side", () => { await expect(Object.keys(metrics.metrics).length).toBe(3); }); + it("Should filter the metrics based on history correctly", async () => { + await expectAsync( + helpers + .filterCustomerMetrics(metrics, clientProfile) + .then((filteredMetrics) => { + metrics.metrics = filteredMetrics; + }) + ).toBeResolved(); + }); + it("Should calculate the value of the big object correctly", async () => { - await expect(Metric.getHistoryValue(metrics)).toBe(40); + await expect(Math.round(Metric.getHistoryValue(metrics))).toBe(0); }); it("Should change the order of metrics correctly", async () => { @@ -203,6 +196,12 @@ describe("Test The Control Side", () => { nodeSelector = "metrics"; await expectAsync( Metrics.delete({ nodeSelector, metricsId: metrics.id }, metric3.id) + // .then(async () => { + // Histories.delete( + // { clientProfile, nodeSelector, historyId: histories.id }, + // metric3.id + // ); + // }) ).toBeResolved(); await Metrics.getMetrics().then((data) => { metrics = data; @@ -235,10 +234,20 @@ describe("Test The Control Side", () => { // To delete everything (Big Object) const deleteEverything = () => { return new Promise((resolve, reject) => { - buildfire.publicData.save({}, "metrics", (err, result) => { + buildfire.datastore.save({}, "metrics", (err, result) => { if (err) reject(err); else { - resolve(result); + buildfire.datastore.save( + { metrics: {} }, + `history${clientProfile}`, + (err, result) => { + if (err) { + console.error(err); + return reject(err); + } + resolve(); + } + ); } }); }); diff --git a/tests/widget/widget.spec.js b/tests/widget/widget.spec.js index 62cbd5f..9f192e5 100644 --- a/tests/widget/widget.spec.js +++ b/tests/widget/widget.spec.js @@ -38,7 +38,7 @@ describe("Test The Widget Side", () => { ).toBeResolved(); }); - it("Should calculate the value of the big object correctly", async () => { + xit("Should calculate the value of the big object correctly", async () => { expect(Metrics.getHistoryValue(metrics, 1)).toBe(48.5); }); @@ -52,7 +52,7 @@ describe("Test The Widget Side", () => { Metrics.updateMetricHistory( { nodeSelector, metricsId: metrics.id }, 55, - `${currentUser.firstName} ${currentUser.lastName}` + currentUser && currentUser.username ? currentUser.username : null ) ).toBeResolved(); }); diff --git a/widget/app.js b/widget/app.js index 884477d..4b1cab6 100644 --- a/widget/app.js +++ b/widget/app.js @@ -1,6 +1,18 @@ // The big object that contains all the metrics let metrics = {}; +// Object containes a specific client history +let histories = {}; + +// Client profile (client query) +let clientProfile = ""; + +// Check if a query string was provided +let queryString = buildfire.parseQueryString(); +if (queryString && queryString.clientProfile) { + clientProfile = queryString.clientProfile; +} + // We used nodeSelector to determine where are we inside the big object let nodeSelector = "metrics"; @@ -18,7 +30,7 @@ let metricChart = {}; // A variable that is used to set how many times to pop the breadcrumb when the control side go back multiple levels at once let numberOfPops = 0; -let snackbarMessage = {}; +let snackbarMessages = {}; // Get the app's theme to utilize its colors in design let appThemeObj = {}; @@ -37,16 +49,45 @@ const getCurrentUser = () => { getCurrentUser(); +// hide metric screen on init; +helpers.hideElem("#metricsScreen"); + // Login and Logout listners buildfire.auth.onLogin(() => getCurrentUser()); buildfire.auth.onLogout(() => (currentUser = null)); +let isDeeplink = false; buildfire.deeplink.getData((data) => { if (data && data.link) { - nodeSelector = data.link; + isDeeplink = true; + let itemPath = data.link.split("."); + itemPath.pop(); + nodeSelector = itemPath.join("."); } }); +buildfire.navigation.onAppLauncherActive(() => { + if (nodeSelector !== "metrics") { + nodeSelector = "metrics"; + buildfire.messaging.sendMessageToControl({ nodeSelector }); + renderInit(); + } +}, false); + +const getBreadCrumps = () => { + return new Promise((resolve, reject) => { + buildfire.history.get( + { + pluginBreadcrumbsOnly: false, + }, + (err, result) => { + if (err) return reject(err); + resolve(result); + } + ); + }); +}; + // Get all user bookmarks // let bookmarks = {}; @@ -61,78 +102,94 @@ buildfire.deeplink.getData((data) => { // }; // To sync betwwen the widget and the control when any change (in metrics) happened in the control side -buildfire.publicData.onUpdate((event) => { +buildfire.datastore.onUpdate((event) => { if (event.data && event.id) { - metrics = event.data; - metrics.id = event.id; + if (event.tag.includes("metrics")) { + metrics = event.data; + metrics.id = event.id; + } else if (event.tag === "settings") { + return Settings.load().then(() => { + renderInit(); + }); + } renderInit(); } }); -// To sync betwwen the widget and the control when any change (in settings) happened in the control side -buildfire.datastore.onUpdate((event) => { - if (event.tag === "settings") { - Settings.load().then(() => { - renderInit(); - }); - } -}); - // To get all metrics and start rendering Metrics.getMetrics().then((result) => { metrics = result; - initMaterialComponents(); + // Initialize clinet history + Histories.getHistories(clientProfile).then((result) => { + histories = result; - Settings.load().then(() => { - // To prevent Functional Tests from Applying these lines where it will cause some errors - // Check if the user have the permission to update metrics - isUserAuthorized(); - renderInit(); + initMaterialComponents(); + + Settings.load().then(() => { + // To prevent Functional Tests from Applying these lines where it will cause some errors + // Check if the user have the permission to update metrics + renderInit(); + }); }); }); // To initialize and prepare metrics to be rendered const renderInit = () => { - listViewContainer.innerHTML = ""; - // Extract the desired metrics (children) from the big object using nodeSelector - let readyMetrics = helpers.nodeSplitter(nodeSelector, metrics); - // Hide the summary in the Home Page if the settings is set to hide it - if (nodeSelector === "metrics" && !Settings.showSummary) { - helpers.hideElem("#summary"); - } else { - helpers.showElem("#summary"); - } - // Get metrics that should be rendered - let metricsChildren = readyMetrics.metricsChildren; - // Init metrics values' chart - initChart(readyMetrics.metricsParent); - - if (readyMetrics.metricsParent.description) { - description.style.display = "block"; - document.getElementById("metricDescription").innerHTML = - readyMetrics.metricsParent.description; - } else { - description.style.display = "none"; - } + try { + // Filter Metrics before rendering + helpers + .filterCustomerMetrics(metrics, clientProfile) + .then((filteredMetrics) => { + metrics.metrics = filteredMetrics; + + listViewContainer.innerHTML = ""; + // Extract the desired metrics (children) from the big object using nodeSelector + let readyMetrics = helpers.nodeSplitter(nodeSelector, metrics); + // Hide the summary in the Home Page if the settings is set to hide it + if (nodeSelector === "metrics" && !Settings.showSummary) { + helpers.hideElem("#summary"); + } else { + helpers.showElem("#summary"); + } - let currentMetricList = []; - // Prepare metrics to be rendered in the ListView component - for (let metricId in metricsChildren) { - metricsChildren[metricId].id = metricId; - let newMetric = new Metric(metricsChildren[metricId]); - let InitMetricAsItem = metricAsItemInit(newMetric); - currentMetricList.push(InitMetricAsItem); - } - // Add the summary value of the parent metric - summaryValue.innerText = `${readyMetrics.metricsParent.value || 0}%`; + // Get metrics that should be rendered + let metricsChildren = readyMetrics.metricsChildren; + // Init metrics values' chart + initChart(readyMetrics.metricsParent); - checkIncreaseOrDecrease(readyMetrics); + helpers.showElem("#metricsScreen"); + helpers.hideElem("#updateHistoryContainer, #updateHistoryButton"); - currentMetricList = helpers.sortMetrics( - currentMetricList, - readyMetrics.metricsSortBy - ); - renderMetrics(currentMetricList); + if (readyMetrics.metricsParent.description) { + description.style.display = "block"; + document.getElementById("metricDescription").innerHTML = + readyMetrics.metricsParent.description; + } else { + description.style.display = "none"; + } + + let currentMetricList = []; + // Prepare metrics to be rendered in the ListView component + for (let metricId in metricsChildren) { + metricsChildren[metricId].id = metricId; + let newMetric = new Metric(metricsChildren[metricId]); + let InitMetricAsItem = metricAsItemInit(newMetric); + currentMetricList.push(InitMetricAsItem); + } + // Add the summary value of the parent metric + summaryValue.innerText = `${readyMetrics.metricsParent.value || 0}%`; + + checkIncreaseOrDecrease(readyMetrics); + + currentMetricList = helpers.sortMetrics( + currentMetricList, + readyMetrics.metricsSortBy + ); + renderMetrics(currentMetricList); + }); + } catch (err) { + console.error(err); + } }; // Render metrics using ListView component @@ -198,7 +255,7 @@ const metricAsItemInit = (newMetric) => { renderInit(); } else { - if (currentUser && isUserAuthorized()) { + if (isUserAuthorized()) { helpers.hideElem("#metricsScreen"); helpers.showElem("#updateHistoryContainer, #updateHistoryButton"); historyCloseBtn.style.background = appThemeObj.colors.warningTheme; @@ -240,12 +297,9 @@ const metricAsItemInit = (newMetric) => { // Add onclick handler to add notes icon inorder to add notes helpers.getElem("#notes").querySelector("button").onclick = () => { // Get the parent path for the metric - let itemPath = nodeSelector.split("."); - itemPath.pop(); - itemPath = itemPath.join("."); const options = { - itemId: itemPath, + itemId: nodeSelector, title: newMetric.title, imageUrl: newMetric.icon, }; @@ -266,22 +320,28 @@ const metricAsItemInit = (newMetric) => { updateHistoryBtn.onclick = (event) => { const value = Math.round(bar.value() * 100); // the value of the progressbar - Metrics.updateMetricHistory( - { nodeSelector, metricsId: metrics.id }, - { value, username: currentUser.firstName } + Histories.updateMetricHistory( + { clientProfile, nodeSelector, historyId: histories.id }, + { + value, + username: + currentUser && currentUser.username + ? currentUser.username + : null, + } ) .then((result) => { - metrics = result; + // metrics = result; buildfire.history.pop(); }) .catch((err) => { console.log(err); }); }; + updateHistoryBtn.style.backgroundColor = + appThemeObj.colors.successTheme; initProgressBar(newMetric); document.body.scrollTop = 0; - } else { - snackbarMessage.open(); } } }; @@ -295,12 +355,17 @@ const initChart = (metric) => { } let title = !metric.title ? `Home History` : `${metric.title} History`; - let historyValues = []; + // let historyValues = []; // This for loop calculate and set all the values of all metrics for the last 7 days - for (let i = 7; i > 0; i--) { - let value = Metrics.getHistoryValue(metric, i) || 0; - historyValues.push(isNaN(value) ? value : value.toPrecision(3)); - } + // for (let i = 7; i > 0; i--) { + // let value = Metrics.getHistoryValue(metric, i) || 0; + // historyValues.push(isNaN(value) ? value : value.toPrecision(3)); + // } + + let history = Metrics.getHistoryValues(metric); + + let historyValues = history.historyData; + let historyDates = history.historyDays; let datasets = [ { @@ -314,16 +379,16 @@ const initChart = (metric) => { fill: true, }, ]; - renderChart(datasets); + renderChart(datasets, historyDates); }; -const renderChart = (datasets) => { +const renderChart = (datasets, historyDates) => { const ctx = document.getElementById("chart").getContext("2d"); metricChart = new Chart(ctx, { type: "line", data: { - labels: helpers.getLast7Days(), + labels: historyDates, datasets, }, options: { @@ -335,11 +400,11 @@ const renderChart = (datasets) => { }, elements: { point: { - radius: 3, - hitRadius: 20, + radius: 4, borderWidth: 2, - hoverRadius: 5, - hoverBorderWidth: 1, + hitRadius: 15, + hoverRadius: 7, + hoverBorderWidth: 2, }, line: { tension: 0, @@ -347,16 +412,19 @@ const renderChart = (datasets) => { }, layout: { padding: { - top: 6, - left: 6, - right: 6, - bottom: 6, + top: 10, + left: 8, + right: 8, + bottom: 0, }, }, scales: { xAxes: [ { - display: false, + display: true, + gridLines: { + display: false, + }, }, ], yAxes: [ @@ -387,7 +455,7 @@ const initProgressBar = (newMetric) => { alignToBottom: true, }, from: { color: appThemeObj.colors.dangerTheme }, - to: { color: appThemeObj.colors.primaryTheme }, + to: { color: appThemeObj.colors.successTheme }, // Set default step function for all animate calls step: (state, bar) => { bar.path.setAttribute("stroke", state.color); @@ -447,13 +515,16 @@ const changeProgressbarValue = (direction, newMetric) => { }; const isUserAuthorized = () => { - if (!currentUser) return false; - let authorized = false; let currentTags = {}; if (Settings.tags.length === 0) { authorized = true; } else { + if (!currentUser) { + authManager.login(); + return false; + } + Settings.tags.forEach((tag) => { currentTags[tag.tagName] = tag.tagName; }); @@ -466,6 +537,11 @@ const isUserAuthorized = () => { }); } } + if (!authorized) { + let snackbar = helpers.getElem(".mdc-snackbar .mdc-snackbar__label"); + snackbar.innerHTML = "You do not have access to update."; + snackbarMessages.open(); + } return authorized; }; @@ -478,8 +554,8 @@ const initMaterialComponents = () => { mdc.ripple.MDCRipple.attachTo(btn); }); - snackbarMessage = mdc.snackbar.MDCSnackbar.attachTo( - document.querySelector(".mdc-snackbar") + snackbarMessages = mdc.snackbar.MDCSnackbar.attachTo( + document.querySelector(".mdc-snackbar.no-access") ); }; diff --git a/widget/index.html b/widget/index.html index 74c78b5..4e46558 100644 --- a/widget/index.html +++ b/widget/index.html @@ -54,7 +54,7 @@
- +
@@ -128,14 +128,13 @@ -
+
- You do not have access to update
+ @@ -163,6 +163,8 @@ + + diff --git a/widget/js/authManager.js b/widget/js/authManager.js index 8fd7aba..97c3229 100644 --- a/widget/js/authManager.js +++ b/widget/js/authManager.js @@ -10,4 +10,15 @@ const authManager = { }); }); }, + login: () => { + return new Promise((resolve, reject) => { + buildfire.auth.login({ allowCancel: true }, (err, user) => { + if (err) { + reject(err); + } else { + resolve(user); + } + }); + }); + }, }; diff --git a/widget/js/classes/histories.js b/widget/js/classes/histories.js new file mode 100644 index 0000000..7bc49be --- /dev/null +++ b/widget/js/classes/histories.js @@ -0,0 +1,110 @@ +class Histories { + constructor() {} + + // Get the big Object (history object) + static getHistories(clientProfile) { + return new Promise((resolve, reject) => { + buildfire.publicData.get(`history${clientProfile}`, (err, result) => { + if (err) { + console.error(err); + return reject(err); + } + else { + // Check if there is already objects in the database + if (!result.data.metrics) { + // If there is no object, then create the parent object + buildfire.publicData.save( + { metrics: {} }, + `history${clientProfile}`, + (err, result) => { + if (err) { + console.error(err); + return reject(err); + } + else { + this.getHistories(clientProfile).then((result) => { + resolve(result); + }); + } + } + ); + } else { + result.data.id = result.id; + resolve(result.data); + } + } + }); + }); + } + + static updateMetricHistory({ clientProfile, nodeSelector, historyId }, data) { + const absoluteDate = helpers.getAbsoluteDate(); + const dateOnly = helpers.getAbsoluteDate().slice(0, 10); + + return new Promise((resolve, reject) => { + if (!nodeSelector) return reject("nodeSelector not provided"); + if (!historyId) return reject("historyId not provided"); + if ( + nodeSelector.slice(-7) === "metrics" || + nodeSelector.slice(-8) === "metrics." + ) { + console.error("ERROR"); + return reject("nodeSelector is not right"); + } + + buildfire.publicData.searchAndUpdate( + { [`${nodeSelector}.history.date`]: { $regex: `.*${dateOnly}.*` } }, + { + $set: { + [`${nodeSelector}.history.$.value`]: data.value, + [`${nodeSelector}.history.$.lastUpdatedOn`]: new Date(), + [`${nodeSelector}.history.$.lastUpdatedBy`]: data.username, + }, + }, + `history${clientProfile}`, + (err, res) => { + if (err) { + console.error(err); + return reject(err); + } + if (res.nModified === 0) { + buildfire.publicData.update( + historyId, + { + $push: { + [`${nodeSelector}.history`]: { + date: absoluteDate, + createdOn: new Date(), + createdBy: data.username, + lastUpdatedOn: new Date(), + lastUpdatedBy: data.username, + value: data.value, + }, + }, + }, + `history${clientProfile}`, + (err, result) => { + if (err) { + console.error(err); + return reject(err); + } else { + result.data.id = result.id; + resolve(result.data); + } + } + ); + } + // Extract metric id from nodeSelector + let updatedMetricId = nodeSelector.slice().split("."); + updatedMetricId = updatedMetricId[updatedMetricId.length - 1]; + // Track action + Analytics.trackAction(`METRIC_${updatedMetricId}_HISTORY_UPDATE`); + + Metrics.getMetrics().then((result) => { + resolve(result); + }); + } + ); + }); + } +} diff --git a/widget/js/classes/history.js b/widget/js/classes/history.js new file mode 100644 index 0000000..29b7841 --- /dev/null +++ b/widget/js/classes/history.js @@ -0,0 +1,6 @@ +class ClientHistory { + constructor(data = {}) { + this.id = data.id || ""; + this.history = data.history || []; + } +} diff --git a/widget/js/classes/metrics.js b/widget/js/classes/metrics.js index 2e2854a..080245c 100644 --- a/widget/js/classes/metrics.js +++ b/widget/js/classes/metrics.js @@ -3,28 +3,14 @@ class Metrics { static getMetrics() { return new Promise((resolve, reject) => { - buildfire.publicData.get("metrics", (err, result) => { + buildfire.datastore.get("metrics", (err, result) => { if (err) reject(err); else { - // Check if there is already objects in the database - if (!result.data.metrics) { - // If there is no object, then create the parent object - buildfire.publicData.save( - { metrics: {}, sortBy: "manual" }, - "metrics", - (err, result) => { - if (err) reject(err); - else { - this.getMetrics().then((result) => { - resolve(result); - }); - } - } - ); - } else { - result.data.id = result.id; - resolve(result.data); + if (!result || !result.data) { + result = { data: { metrics: {} } }; } + result.data.id = result.id; + resolve(result.data); } }); }); @@ -32,6 +18,7 @@ class Metrics { static updateMetricHistory({ nodeSelector, metricsId }, data) { const absoluteDate = helpers.getAbsoluteDate(); + const dateOnly = helpers.getAbsoluteDate().slice(0, 10); return new Promise((resolve, reject) => { if (!nodeSelector) return reject("nodeSelector not provided"); @@ -41,9 +28,8 @@ class Metrics { nodeSelector.slice(-8) === "metrics." ) return reject("nodeSelector is not right"); - - buildfire.publicData.searchAndUpdate( - { [`${nodeSelector}.history.date`]: absoluteDate }, + buildfire.datastore.searchAndUpdate( + { [`${nodeSelector}.history.date`]: { $regex: `.*${dateOnly}.*` } }, { $set: { [`${nodeSelector}.history.$.value`]: data.value, @@ -55,7 +41,7 @@ class Metrics { (err, res) => { if (err) reject(err); if (res.nModified === 0) { - buildfire.publicData.update( + buildfire.datastore.update( metricsId, { $push: { @@ -93,51 +79,179 @@ class Metrics { }); } - static getHistoryValue(metric, inde) { + // static getHistoryValue(metric, inde) { + // if (metric.type === "metric") { + // let todayDate = new Date(); + // for (var i = 1; i <= 7; i++) { + // if (metric.history[metric.history.length - i]) { + // let metricHistoryDate = new Date( + // metric.history[metric.history.length - i].date + // ); + // const diffTime = Math.abs(todayDate - metricHistoryDate); + // const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + + // if (diffDays >= inde) { + // let val = metric.history[metric.history.length - i].value; + // if (inde === 1) { + // metric.value = val || 0; + // } else if (inde === 2) { + // metric.previousValue = val || 0; + // } + // return val; + // } + // } + // } + // return "No value"; + // } else if (metric.type === "parent" || !metric.type) { + // if (metric.metrics && Object.keys(metric.metrics).length === 0) { + // return "No value"; + // } + // if (metric.metrics) { + // let sum = 0; + // let numberChildren = 0; + // for (let key in metric.metrics) { + // let result = Metrics.getHistoryValue(metric.metrics[key], inde); + // if (!isNaN(result)) { + // numberChildren++; + // sum += Metrics.getHistoryValue(metric.metrics[key], inde); + // } + // } + // let avg = sum / numberChildren; + // if (inde === 1) { + // metric.value = parseFloat(avg.toPrecision(3)) || 0; + // } else if (inde === 2) { + // metric.previousValue = parseFloat(avg.toPrecision(3)) || 0; + // } + // return avg; + // } + // return 0; + // } + // } + + static extractHistoryValues(metric) { if (metric.type === "metric") { - let todayDate = helpers.getAbsoluteDate(); - for (var i = 1; i <= 7; i++) { - if (metric.history[metric.history.length - i]) { - if ( - new Date( - todayDate - - new Date(metric.history[metric.history.length - i].date) - ).getDate() >= inde - ) { - let val = metric.history[metric.history.length - i].value; - if (inde === 1) { - metric.value = val || 0; - } else if (inde === 2) { - metric.previousValue = val || 0; + // Get the metric history array + let history = metric.history; + + // Creates the set to be able to calculate the average in this format: + // [{"date":"11/14/2020","value":0},{"date":"11/13/2020","value":0}, .....] + let dataset = helpers.getLast7Days(); + + // To track which value from the history to take; + let index = 1; + + // Loop throw the set created above + dataset.forEach((set) => { + if (!history[history.length - index]) { + // This means that this has no value change at this date + set.value = "No value"; + } else { + // Convert the ISO date from the coming from the database to local date + let historyDate = new Date( + history[history.length - index].date + ).toLocaleDateString(); + + // If the saved date was one day ahead of the current date, don't take the current value, take the previous one; + if (set.date < historyDate) { + index++; + set.value = history[history.length - index].value || 0; + + // Check if there is another element in the history + if ( + history[history.length - index] && + history[history.length - index].date + ) { + let lastDate = new Date( + history[history.length - index].date + ).toLocaleDateString(); + // If the previous value's date === the the current set.date we just used then we need to move on to the next value, + // other wise we skip + if (set.date === lastDate) { + index++; + } + } else { + set.value = history[history.length - index].value + ? history[history.length - index].value + : 0; } - return val; + } else if (set.date === historyDate) { + set.value = history[history.length - index].value || 0; + index++; + } else { + set.value = history[history.length - index].value + ? history[history.length - index].value + : 0; } } - } - return "No value"; + }); + // Assign current and previous average for the child metric + metric.value = + dataset[0] && !isNaN(dataset[0].value) ? dataset[0].value : 0; + metric.previousValue = + dataset[1] && !isNaN(dataset[1].value) ? dataset[1].value : 0; + + return dataset; } else if (metric.type === "parent" || !metric.type) { if (metric.metrics && Object.keys(metric.metrics).length === 0) { - return "No value"; - } - if (metric.metrics) { - let sum = 0; - let numberChildren = 0; + return helpers.getLast7DaysNoValue(); + } else if (metric.metrics) { + // Creates the set to be able to calculate the average in this format: + let historyDataset = helpers.getLast7DaysNoValue(); + // [{"date":"11/14/2020","value":0},{"date":"11/13/2020","value":0}, .....] for (let key in metric.metrics) { - let result = Metrics.getHistoryValue(metric.metrics[key], inde); - if (!isNaN(result)) { - numberChildren++; - sum += Metrics.getHistoryValue(metric.metrics[key], inde); - } - } - let avg = sum / numberChildren; - if (inde === 1) { - metric.value = parseFloat(avg.toPrecision(3)) || 0; - } else if (inde === 2) { - metric.previousValue = parseFloat(avg.toPrecision(3)) || 0; + let metricHistory = Metrics.extractHistoryValues(metric.metrics[key]); + + historyDataset.forEach((day, i) => { + if (!isNaN(metricHistory[i].value)) { + if (isNaN(day.value)) day.value = 0; + day.value += metricHistory[i].value; + + // Add-increase the value of children which have values (type number) to calculate the average + // based on the sum of the calculated children + if (!day.increased) { + day.increased = 1; + } else day.increased++; + } + }); } - return avg; + // Calculate the average + historyDataset.forEach((input) => { + if (input.increased) { + input.value = input.value / input.increased; + } + }); + // Assign current and previous average for the parent metric + metric.value = !isNaN(historyDataset[0].value) + ? parseFloat(historyDataset[0].value.toPrecision(3)) + : 0; + metric.previousValue = !isNaN(historyDataset[1].value) + ? parseFloat(historyDataset[1].value.toPrecision(3)) + : 0; + + return historyDataset; } - return 0; } } + + static getHistoryValues(metrics) { + // Reverse the data to be displayed on the chart + let historyDataset = Metrics.extractHistoryValues(metrics).reverse(); + + // Extract the values only from the history + let historyData = historyDataset.map((elem) => { + if (isNaN(elem.value)) return 0; + return parseFloat(elem.value.toPrecision(3)); + }); + // Format the dates which will appear on the chart + let historyDays = historyDataset.map((elem) => { + let date = elem.date.split("/"); + date.pop(); + return date.join("/"); + }); + + return { + historyData, + historyDays, + }; + } } diff --git a/widget/js/helper.js b/widget/js/helper.js index 0ff2c76..b57e806 100644 --- a/widget/js/helper.js +++ b/widget/js/helper.js @@ -3,7 +3,7 @@ const helpers = { uuidv4: (m = Math, d = Date, h = 16, s = (s) => m.floor(s).toString(h)) => s(d.now() / 1000) + " ".repeat(h).replace(/./g, () => s(m.random() * h)), // Return absolute date - getAbsoluteDate: () => new Date(new Date().setHours(0, 0, 0, 0)), + getAbsoluteDate: () => new Date().toISOString(), nodeSplitter: (nodeSelector, metrics) => { let splittedNode = nodeSelector.split("."); if (splittedNode[splittedNode.length - 1] !== "metrics") { @@ -15,20 +15,37 @@ const helpers = { let metricsParent = null; let metricsSortBy = ""; - splittedNode.forEach((item, i) => { - // If we are at the home page (top of the object) - if (nodeSelector === "metrics") { - metricsParent = metrics; - metricsSortBy = metrics.sortBy; - } - // Assign the parent metric sortBy value (If we are in parent metric); - if (nodeSelector !== "metrics" && i === splittedNode.length - 2) { - metricsParent = metricsChildren[item]; - metricsSortBy = metricsChildren[item].sortBy; + try { + splittedNode.forEach((item, i) => { + // If we are at the home page (top of the object) + if (nodeSelector === "metrics") { + metricsParent = metrics; + metricsSortBy = metrics.sortBy; + } + // Assign the parent metric sortBy value (If we are in parent metric); + if (nodeSelector !== "metrics" && i === splittedNode.length - 2) { + metricsParent = metricsChildren[item]; + metricsSortBy = metricsChildren[item].sortBy; + } + + metricsChildren = metricsChildren[item]; + }); + } catch (err) { + let snackbar = helpers.getElem('.mdc-snackbar .mdc-snackbar__label'); + // Change snackbar error text + if (isDeeplink) { + snackbar.innerHTML = "The note you selected has been removed, taking you back home." + } else { + snackbar.innerHTML = "Something went wrong, taking you back home." } + // Show snackbar + snackbarMessages.open(); + + setTimeout(() => { + buildfire.navigation._goBackOne(); + }, 2000); + } - metricsChildren = metricsChildren[item]; - }); return { metricsChildren, metricsSortBy, metricsParent }; }, sortMetrics: (currentMetricList, sortBy) => { @@ -42,19 +59,19 @@ const helpers = { } return currentMetricList; }, - getLast7Days: () => { - let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - let result = []; - let date = helpers.getAbsoluteDate(); - result.push(days[date.getDay()]); + // getLast7Days: () => { + // let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + // let result = []; + // let date = new Date(); + // result.push(`${date.getMonth()}-${date.getDate()}`); - for (let i = 1; i <= 6; i++) { - let copiedDate = new Date(date); - copiedDate.setDate(date.getDate() - i); - result.push(days[copiedDate.getDay()]); - } - return result.reverse(); - }, + // for (let i = 1; i <= 6; i++) { + // let copiedDate = new Date(date); + // copiedDate.setDate(date.getDate() - i); + // result.push(`${copiedDate.getMonth()}/${copiedDate.getDate()}`); + // } + // return result.reverse(); + // }, hideElem: (selector) => { let elements = document.querySelectorAll(selector); @@ -71,4 +88,71 @@ const helpers = { getElem: (selector) => { return document.querySelector(selector); }, + // Filter Metrics based on the provided customer + filterCustomerMetrics: (metrics, clientProfile) => { + return new Promise((resolve, reject) => { + // Get client history data; + Histories.getHistories(clientProfile).then((result) => { + histories = result; + // Add the history data to each metric + helpers.addHistoryToMetrics(metrics, histories); + + resolve(metrics.metrics); + }); + }); + }, + // Loop recursively on the metrics object and add the history value from histories object + addHistoryToMetrics: (metrics, histories) => { + if (Object.keys(metrics.metrics).length > 0) { + for (var key in metrics.metrics) { + if ( + histories.metrics[key] && + metrics.metrics[key].type === "parent" && + Object.keys(metrics.metrics[key].metrics).length > 0 + ) { + helpers.addHistoryToMetrics( + metrics.metrics[key], + histories.metrics[key] + ); + } else { + if (histories.metrics[key] && histories.metrics[key].history) { + metrics.metrics[key].history = histories.metrics[key].history; + } else { + metrics.metrics[key].history = []; + } + } + } + } + }, + + getLast7Days: () => { + let result = []; + let date = new Date(); + result.push({ date: date.toLocaleDateString(), value: 0 }); + + for (let i = 1; i <= 6; i++) { + let copiedDate = new Date(date); + copiedDate.setDate(date.getDate() - i); + result.push({ + date: copiedDate.toLocaleDateString(), + value: 0, + }); + } + return result; + }, + getLast7DaysNoValue: () => { + let result = []; + let date = new Date(); + result.push({ date: date.toLocaleDateString(), value: "No value" }); + + for (let i = 1; i <= 6; i++) { + let copiedDate = new Date(date); + copiedDate.setDate(date.getDate() - i); + result.push({ + date: copiedDate.toLocaleDateString(), + value: "No value", + }); + } + return result; + } }; diff --git a/widget/js/lib/listView.js b/widget/js/lib/listView.js index 6ee2a75..d5f01d2 100644 --- a/widget/js/lib/listView.js +++ b/widget/js/lib/listView.js @@ -63,6 +63,9 @@ class ListViewItem { this.id = obj.id; this.title = obj.title; this.icon = obj.icon; + this.history = obj.history; + this.type = obj.type; + this.metrics = obj.metrics; this.value = obj.value || 0; this.data = obj.data; this.order = obj.order || null; @@ -75,6 +78,9 @@ class ListViewItem { id: this.id, title: this.title, icon: this.icon, + history: this.history, + type: this.type, + metrics: this.metrics, value: this.value, data: this.data, order: this.order, @@ -170,8 +176,17 @@ class ListViewItem { "mdc-button__icon", ]); } + this.viewdValue = this.value; + + if ( + (this.history.length === 0 && this.type === "metric") || + (this.type === "parent" && Object.keys(this.metrics).length === 0) || + (this.value === 0 && Object.keys(this.metrics).length > 0) + ) { + this.viewdValue = "- "; + } - ui.create("span", listViewItemToolbar, `${this.value}%`, [ + ui.create("span", listViewItemToolbar, `${this.viewdValue}%`, [ "listViewItemToolbarItem", "value", ]); diff --git a/widget/style.css b/widget/style.css index c305d9d..8246c95 100644 --- a/widget/style.css +++ b/widget/style.css @@ -175,7 +175,7 @@ span { } .summary-card .trending-icon { - padding-left: 15px; + padding-left: 12px; font-size: 20px; vertical-align: middle; }