diff --git a/Harvest Billing Widget/EirEvo-EvoLabs.HarvestWidgets-1.0.121.vsix b/Harvest Billing Widget/EirEvo-EvoLabs.HarvestWidgets-1.1.9.vsix similarity index 95% rename from Harvest Billing Widget/EirEvo-EvoLabs.HarvestWidgets-1.0.121.vsix rename to Harvest Billing Widget/EirEvo-EvoLabs.HarvestWidgets-1.1.9.vsix index 61bac8b..44f1bf0 100644 Binary files a/Harvest Billing Widget/EirEvo-EvoLabs.HarvestWidgets-1.0.121.vsix and b/Harvest Billing Widget/EirEvo-EvoLabs.HarvestWidgets-1.1.9.vsix differ diff --git a/Harvest Billing Widget/package-lock.json b/Harvest Billing Widget/package-lock.json index 1845f3c..88551ea 100644 --- a/Harvest Billing Widget/package-lock.json +++ b/Harvest Billing Widget/package-lock.json @@ -11,8 +11,7 @@ "dependencies": { "chart.js": "^4.4.3", "vss-web-extension-sdk": "^5.141.0" - }, - "devDependencies": {} + } }, "node_modules/@kurkle/color": { "version": "0.3.2", diff --git a/Harvest Billing Widget/scripts/widget-configuration.js b/Harvest Billing Widget/scripts/widget-configuration.js index 8967f73..57ac62c 100644 --- a/Harvest Billing Widget/scripts/widget-configuration.js +++ b/Harvest Billing Widget/scripts/widget-configuration.js @@ -1,4 +1,4 @@ -document.addEventListener("DOMContentLoaded", function() { +document.addEventListener("DOMContentLoaded", function () { VSS.init({ explicitNotifyLoaded: true, usePlatformStyles: true @@ -19,6 +19,13 @@ document.addEventListener("DOMContentLoaded", function() { var $internalColor = document.getElementById("internalColor"); var $rndColor = document.getElementById("rndColor"); var $timePeriod = document.getElementById("timePeriod"); // Time Period Field + var $theme = document.getElementById("themeSelector"); // Theme Selection Field + var $productSupportFilter = document.getElementById("productSupportFilter"); + var $productSupportLabel = document.getElementById("productSupportLabel"); + var $productSupportColor = document.getElementById("productSupportColor"); // Product Support Filter Field + var $unbilledFilter = document.getElementById("unbilledFilter"); + var $unbilledLabel = document.getElementById("unbilledLabel"); + var $unbilledColor = document.getElementById("unbilledColor"); function validateField(field) { if (field) { @@ -32,58 +39,108 @@ document.addEventListener("DOMContentLoaded", function() { } function notifyConfigurationChange(context) { - var customSettings = { - data: JSON.stringify({ - harvestAccountId: $harvestAccountId ? $harvestAccountId.value.trim() : "", - authToken: $authToken ? $authToken.value.trim() : "", - rndFilter: $rndFilter ? $rndFilter.value.trim() : "R&D", // R&D Filter - displayMode: $displayMode ? $displayMode.value : "", - billableLabel: $billableLabel ? $billableLabel.value.trim() : "Billable", - internalLabel: $internalLabel ? $internalLabel.value.trim() : "Internal", - rndLabel: $rndLabel ? $rndLabel.value.trim() : "R&D", // R&D Label - billableColor: $billableColor ? $billableColor.value : "#852d9d", - internalColor: $internalColor ? $internalColor.value : "#ec0bb7", - rndColor: $rndColor ? $rndColor.value : "#76f5ff", - timePeriod: $timePeriod ? $timePeriod.value : "7" // Time Period - }) - }; - - console.log('Custom Settings:', customSettings); // Debugging output - - if (context && typeof context.notify === 'function') { - context.notify(WidgetHelpers.WidgetEvent.ConfigurationChange, WidgetHelpers.WidgetEvent.Args(customSettings)); + try { + var customSettings = { + data: JSON.stringify({ + harvestAccountId: $harvestAccountId ? $harvestAccountId.value.trim() : "", + authToken: $authToken ? $authToken.value.trim() : "", + rndFilter: $rndFilter ? $rndFilter.value.trim() : "R&D", // R&D Filter + displayMode: $displayMode ? $displayMode.value : "", + billableLabel: $billableLabel ? $billableLabel.value.trim() : "Billable", + internalLabel: $internalLabel ? $internalLabel.value.trim() : "Internal", + rndLabel: $rndLabel ? $rndLabel.value.trim() : "R&D", // R&D Label + billableColor: $billableColor ? $billableColor.value : "#852d9d", + internalColor: $internalColor ? $internalColor.value : "#ec0bb7", + rndColor: $rndColor ? $rndColor.value : "#76f5ff", + timePeriod: $timePeriod ? $timePeriod.value : "7", // Time Period + theme: $theme ? $theme.value : "light", // Theme Selection + productSupportLabel: $productSupportLabel ? $productSupportLabel.value.trim() : "Product", + productSupportFilter: $productSupportFilter ? $productSupportFilter.value.trim() : "PRODUCT_SUPPORT", + productSupportColor: $productSupportColor ? $productSupportColor.value : "#FFD700", + unbilledFilter: $unbilledFilter ? $unbilledFilter.value.trim() : "Unbilled", + unbilledLabel: $unbilledLabel ? $unbilledLabel.value.trim() : "Unbilled", + unbilledColor: $unbilledColor ? $unbilledColor.value : "#27CA12" + + }) + }; + + console.log("Custom Settings for Notify:", customSettings); + + if (context && typeof context.notify === "function") { + context.notify(WidgetHelpers.WidgetEvent.ConfigurationChange, WidgetHelpers.WidgetEvent.Args(customSettings)); + } else { + console.error("Configuration context does not support notify."); + } + } catch (error) { + console.error("Error in notifyConfigurationChange:", error); + } + } + + function applyTheme(theme) { + const bodyElement = document.body; + const widgetElement = document.querySelector(".widget"); + + if (!bodyElement) { + console.error("Body element is not found."); + return; + } + + bodyElement.classList.remove("light-theme", "dark-theme"); + + if (theme === "dark") { + bodyElement.classList.add("dark-theme"); } else { - console.error("Configuration context does not support notify."); + bodyElement.classList.add("light-theme"); + } + + if (widgetElement) { + widgetElement.classList.remove("light-theme", "dark-theme"); + widgetElement.classList.add(`${theme}-theme`); + } else { + console.error("Widget element not found."); } } function bindEvents(context) { - var fields = [ - $harvestAccountId, - $authToken, - $rndFilter, // R&D Filter - $displayMode, - $billableLabel, - $internalLabel, - $rndLabel, // R&D Label - $billableColor, - $internalColor, + const fields = [ + $harvestAccountId, + $authToken, + $rndFilter, + $displayMode, + $billableLabel, + $internalLabel, + $rndLabel, + $billableColor, + $internalColor, $rndColor, - $timePeriod // Time Period + $timePeriod, + $productSupportLabel, + $productSupportFilter, + $productSupportColor, + $unbilledLabel, + $unbilledFilter, + $unbilledColor, ]; - fields.forEach(function(field) { + fields.forEach(function (field) { if (field) { - field.addEventListener("input", function() { + field.addEventListener("input", function () { validateField(field); notifyConfigurationChange(context); }); } }); + if ($theme) { + $theme.addEventListener("change", function () { + applyTheme($theme.value); + notifyConfigurationChange(context); + }); + } + if ($displayMode || $timePeriod) { - [$displayMode, $timePeriod].forEach(function(field) { - field.addEventListener("change", function() { + [$displayMode, $timePeriod].forEach(function (field) { + field.addEventListener("change", function () { notifyConfigurationChange(context); }); }); @@ -96,25 +153,26 @@ document.addEventListener("DOMContentLoaded", function() { if ($harvestAccountId) $harvestAccountId.value = settings.harvestAccountId || ""; if ($authToken) $authToken.value = settings.authToken || ""; if ($rndFilter) $rndFilter.value = settings.rndFilter || "R&D"; // R&D Filter + if ($productSupportFilter) $productSupportFilter.value = settings.productSupportFilter || "PRODUCT_SUPPORT"; + if ($unbilledFilter) $unbilledFilter.value = settings.unbilledFilter || "Unbilled"; if ($displayMode) $displayMode.value = settings.displayMode || "hours"; if ($billableLabel) $billableLabel.value = settings.billableLabel || "Billable"; if ($internalLabel) $internalLabel.value = settings.internalLabel || "Internal"; + if ($productSupportLabel) $productSupportLabel.value = settings.productSupportLabel || "Product"; + if ($unbilledLabel) $unbilledLabel.value = settings.unbilledLabel || "Unbilled"; if ($rndLabel) $rndLabel.value = settings.rndLabel || "R&D"; // R&D Label if ($billableColor) $billableColor.value = settings.billableColor || "#852d9d"; if ($internalColor) $internalColor.value = settings.internalColor || "#ec0bb7"; if ($rndColor) $rndColor.value = settings.rndColor || "#76f5ff"; + if ($productSupportColor) $productSupportColor.value = settings.productSupportColor || "#FFD700"; + if ($unbilledColor) $unbilledColor.value = settings.unbilledColor || "#27CA12"; if ($timePeriod) $timePeriod.value = settings.timePeriod || "7"; // Time Period + if ($theme) $theme.value = settings.theme || "light"; // Load Theme + + applyTheme(settings.theme || "light"); validateField($harvestAccountId); validateField($authToken); - validateField($rndFilter); // R&D Filter - validateField($billableLabel); - validateField($internalLabel); - validateField($rndLabel); // R&D Label - validateField($billableColor); - validateField($internalColor); - validateField($rndColor); - validateField($timePeriod); // Time Period VSS.resize(); @@ -128,6 +186,7 @@ document.addEventListener("DOMContentLoaded", function() { validateField($authToken); if (($harvestAccountId && $harvestAccountId.value.trim() === "") || ($authToken && $authToken.value.trim() === "")) { + console.error("Validation failed: Required fields are empty."); return WidgetHelpers.WidgetStatusHelper.Failure("Validation error: fields cannot be empty."); } @@ -143,11 +202,19 @@ document.addEventListener("DOMContentLoaded", function() { billableColor: $billableColor ? $billableColor.value : "#852d9d", internalColor: $internalColor ? $internalColor.value : "#ec0bb7", rndColor: $rndColor ? $rndColor.value : "#76f5ff", - timePeriod: $timePeriod ? $timePeriod.value : "7" // Time Period + timePeriod: $timePeriod ? $timePeriod.value : "7", // Time Period + theme: $theme ? $theme.value : "light", // Theme Selection + productSupportLabel: $productSupportLabel ? $productSupportLabel.value.trim() : "Product", + productSupportFilter: $productSupportFilter ? $productSupportFilter.value.trim() : "PRODUCT_SUPPORT", + productSupportColor: $productSupportColor ? $productSupportColor.value : "#FFD700", + unbilledFilter: $unbilledFilter ? $unbilledFilter.value.trim() : "Unbilled", + unbilledLabel: $unbilledLabel ? $unbilledLabel.value.trim() : "Unbilled", + unbilledColor: $unbilledColor ? $unbilledColor.value : "#27CA12", + }) }; - console.log('Saving Settings:', customSettings); // Debugging output + console.log("Saving Settings:", customSettings); // Debugging output return WidgetHelpers.WidgetConfigurationSave.Valid(customSettings); } @@ -156,4 +223,4 @@ document.addEventListener("DOMContentLoaded", function() { VSS.notifyLoadSucceeded(); }); -}); +}); \ No newline at end of file diff --git a/Harvest Billing Widget/vss-extension.json b/Harvest Billing Widget/vss-extension.json index 0e1b6aa..a95d47c 100644 --- a/Harvest Billing Widget/vss-extension.json +++ b/Harvest Billing Widget/vss-extension.json @@ -1,7 +1,7 @@ { "manifestVersion": 1, "id": "HarvestWidgets", - "version": "1.0.121", + "version": "1.1.9", "name": "Harvest Billing Widget", "description": "Displays hours from Harvest, providing detailed insights into time allocation for projects. The hours are categorized into billable hours, 'R&D' (Research & Development), and Internal hours.", "publisher": "EirEvo-EvoLabs", @@ -50,10 +50,7 @@ "catalogIconUrl": "img/preview.png", "uri": "widget.html", "supportedSizes": [ - { "rowSpan": 2, "columnSpan": 2 }, - { "rowSpan": 2, "columnSpan": 3 }, - { "rowSpan": 3, "columnSpan": 2 }, - { "rowSpan": 3, "columnSpan": 3 } + { "rowSpan": 2, "columnSpan": 2 } ], "supportedScopes": ["project_team"] } diff --git a/Harvest Billing Widget/widget-configuration.html b/Harvest Billing Widget/widget-configuration.html index 9c853b3..9fb618d 100644 --- a/Harvest Billing Widget/widget-configuration.html +++ b/Harvest Billing Widget/widget-configuration.html @@ -33,8 +33,6 @@ .tooltip { display: none; position: absolute; - background: #333; - color: #fff; padding: 5px; border-radius: 3px; font-size: 12px; @@ -48,6 +46,16 @@ word-wrap: break-word; box-sizing: border-box; } + body.light-theme .tooltip { + background: #333; + color: #fff; + border: 1px solid #fff; + } + body.dark-theme .tooltip { + background: #fff; + color: #000; + border: 1px solid #000; + } .info-icon:hover .tooltip { display: block; } @@ -115,79 +123,184 @@ .end-spacing { height: 30px; } + body.light-theme .widget-configuration { + background-color: #fff; + color: #000; + } + body.dark-theme .widget-configuration { + background-color: #333; + color: #fff; + } + body.dark-theme label { + color: #fff; + } + body.dark-theme .color-label { + color: #fff !important; + } + body.dark-theme .color-settings-title { + color: #fff !important; + } + .bold-text { + font-weight: bold; + }
- + +
- Pie Chart Sector Colors - ⓘ - Choose colors for the sectors of the Pie Chart. The selected colors will be reflected in the corresponding segments of the chart. - + Billable Settings
+ ⓘ + Please choose a color for the Billable sector of the Pie Chart. The selected color will be reflected in the corresponding segment of the chart. + +
+ +
+ + +
+ + +
+ Internal Settings
+ ⓘ + Please choose a color for the Internal sector of the Pie Chart. The selected color will be reflected in the corresponding segment of the chart. +
+ +
+ + +
+ + +
+ R&D Settings +
+
+ ⓘ + Please choose a color for the R&D sector of the Pie Chart. The selected color will be reflected in the corresponding segment of the chart. +
- - +
-
+
-
+ + +
+ Product Settings +
+ +
+
+ +
+ ⓘ + Please choose a color for the Product sector of the Pie Chart. The selected color will be reflected in the corresponding segment of the chart. + +
+
-
- +
-
+ + +
+ Unbilled Settings +
+ +
+
+ +
+ ⓘ + Please choose a color for the Unbilled sector of the Pie Chart. The selected color will be reflected in the corresponding segment of the chart. + + +
+ + +
+ - -
+ +
+ +
+ + ⓘ + Filter projects by the term "Unbilled" or another keyword to categorize them under Unbilled work. + + +
+
-
+
-
-
-
+ +
+ + +
+
diff --git a/Harvest Billing Widget/widget.html b/Harvest Billing Widget/widget.html index 076ef64..5387cb3 100644 --- a/Harvest Billing Widget/widget.html +++ b/Harvest Billing Widget/widget.html @@ -6,20 +6,96 @@ @@ -28,6 +104,7 @@

Billable/Non-billable Hours

+