diff --git a/ReportJ.Extension.Chrome/app/css/mixins.scss b/ReportJ.Extension.Chrome/app/css/mixins.scss
new file mode 100644
index 0000000..f5b2784
--- /dev/null
+++ b/ReportJ.Extension.Chrome/app/css/mixins.scss
@@ -0,0 +1,6 @@
+@mixin background-img($url) {
+ background-image: url($url);
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
+}
\ No newline at end of file
diff --git a/ReportJ.Extension.Chrome/app/css/variables.scss b/ReportJ.Extension.Chrome/app/css/variables.scss
new file mode 100644
index 0000000..16bd69a
--- /dev/null
+++ b/ReportJ.Extension.Chrome/app/css/variables.scss
@@ -0,0 +1,21 @@
+// Colors
+
+$color-primary: #003366;
+
+$color-background: #fff;
+
+$color-text: #333;
+
+$color-border: #dedede;
+
+// Images
+
+$logo-path: "https://raw.githubusercontent.com/mishani0x0ef/ReportJ/master/resources/Icons/logo48x48.png";
+
+// Shadows
+
+$panel-shadow: 0 1px 4px rgba(0,0,0,0.3);
+
+// Z-Index
+
+$z-index-base-popup: 1;
\ No newline at end of file
diff --git a/ReportJ.Extension.Chrome/app/js/content/content.js b/ReportJ.Extension.Chrome/app/js/content/content.js
index 52b4c24..537de54 100644
--- a/ReportJ.Extension.Chrome/app/js/content/content.js
+++ b/ReportJ.Extension.Chrome/app/js/content/content.js
@@ -1,6 +1,7 @@
import AutoIssueSumaryExtender from "./autoIssueSummary/autoIssueSummaryExtender";
import CloseIssueExtender from "./closeIssue/closeIssueExtender";
+import LogTimeExtender from "./logTime/logTimeExtender";
import StorageService from "~/js/services/storageService";
import { checkIsInsideJira } from "~/js/content/common/jiraUtil";
@@ -32,6 +33,7 @@ class ContentController {
_addJiraExtenders(extenders, settings) {
extenders.push(new CloseIssueExtender());
+ extenders.push(new LogTimeExtender());
if (settings.autoIssueSummary.enabled) {
extenders.push(new AutoIssueSumaryExtender());
diff --git a/ReportJ.Extension.Chrome/app/js/content/logTime/logTimeExtender.html b/ReportJ.Extension.Chrome/app/js/content/logTime/logTimeExtender.html
new file mode 100644
index 0000000..4b56df4
--- /dev/null
+++ b/ReportJ.Extension.Chrome/app/js/content/logTime/logTimeExtender.html
@@ -0,0 +1,18 @@
+
+
+
+
\ No newline at end of file
diff --git a/ReportJ.Extension.Chrome/app/js/content/logTime/logTimeExtender.js b/ReportJ.Extension.Chrome/app/js/content/logTime/logTimeExtender.js
new file mode 100644
index 0000000..31712f9
--- /dev/null
+++ b/ReportJ.Extension.Chrome/app/js/content/logTime/logTimeExtender.js
@@ -0,0 +1,110 @@
+import "./logTimeExtender.scss";
+import $ from "jquery";
+import JiraDialogObserver from "~/js/util/jiraDialogObserver";
+import template from "./logTimeExtender.html";
+
+export default class LogTimeExtender {
+ constructor() {
+ this.componentName = "reportj-log-time-extender";
+ this.hours = {
+ group: "hours",
+ placeholder: `${this.componentName} .hours`,
+ values: ["0h", "1h", "2h", "3h", "4h", "5h", "6h", "7h", "8h"]
+ };
+ this.minutes = {
+ group: "minutes",
+ placeholder: `${this.componentName} .minutes`,
+ values: ["0m", "15m", "30m", "45m"]
+ };
+ }
+
+ start() {
+ this._initLogWorkObserver();
+ }
+
+ _initLogWorkObserver() {
+ const observer = new JiraDialogObserver("Log Work");
+ observer.onAppear(($dialog) => {
+ const $logTimeInput = $dialog.find("#log-work-time-logged");
+ const $component = $(template).insertAfter($logTimeInput);
+ this._initComponentTemplate($component);
+ this._addButtonHandlers($component);
+ });
+ }
+
+ _initComponentTemplate($template) {
+ const $link = $template.find(".reportj-link");
+ $link.click(() => this._showPopup());
+ this._createTimeOptionsGroup(this.hours);
+ this._createTimeOptionsGroup(this.minutes);
+ }
+
+ _addButtonHandlers($template) {
+ const $okButton = $template.find("#reportj-submit-log-time");
+ const $cancelButton = $template.find("#reportj-cancel-log-time");
+
+ $okButton.click(() => {
+ const time = this._getLogTimeFromPopup();
+ this._setLogTimeToJira(time);
+ this._hidePopup();
+ });
+
+ $cancelButton.click(() => this._hidePopup());
+ }
+
+ _showPopup() {
+ const $logWorkPopup = $(`${this.componentName} .log-work-popup`);
+ $logWorkPopup.addClass("active");
+ }
+
+ _hidePopup() {
+ const $logWorkPopup = $(`${this.componentName} .log-work-popup`);
+ $logWorkPopup.removeClass("active");
+ }
+
+ _createTimeOptionsGroup(timeOptions) {
+ const group = timeOptions.group;
+ const groupSelector = `input[name=${group}]`;
+ const $elements = timeOptions.values.map((value) => this._createTimeElement(group, value));
+
+ $(timeOptions.placeholder).append($elements);
+ $(groupSelector).change((e) => this._onSelctionChange(e));
+ }
+
+ _createTimeElement(group, value) {
+ const inputId = `${group}_${value}`;
+ return $(
+ `
+
+
+
`
+ );
+ }
+
+ _onSelctionChange(e) {
+ const groupName = e.target.name;
+ const groupSelector = `input[name=${groupName}]`;
+ const $group = $(groupSelector).parent();
+ const activationClass = "active";
+
+ $group.removeClass(activationClass);
+
+ if (e.target.checked) {
+ const $targetContainer = $(e.target.parentElement);
+ $targetContainer.addClass(activationClass);
+ }
+ }
+
+ _getLogTimeFromPopup() {
+ const hoursValue = $(`input[name=${this.hours.group}]:checked`).val() || "";
+ const minutesValue = $(`input[name=${this.minutes.group}]:checked`).val() || "";
+ const logTime = `${hoursValue} ${minutesValue}`;
+
+ return logTime.trim();
+ }
+
+ _setLogTimeToJira(time) {
+ const $timeInput = $("#log-work-time-logged");
+ $timeInput.val(time);
+ }
+}
\ No newline at end of file
diff --git a/ReportJ.Extension.Chrome/app/js/content/logTime/logTimeExtender.scss b/ReportJ.Extension.Chrome/app/js/content/logTime/logTimeExtender.scss
new file mode 100644
index 0000000..a2c2eac
--- /dev/null
+++ b/ReportJ.Extension.Chrome/app/js/content/logTime/logTimeExtender.scss
@@ -0,0 +1,92 @@
+@import "~root/css/variables";
+@import "~root/css/mixins";
+
+$link-size: 20px;
+
+reportj-log-time-extender {
+ position: relative;
+ display: inline-flex;
+ vertical-align: middle;
+
+ h4 {
+ margin: 0;
+ }
+
+ .reportj-link {
+ margin: 0 5px;
+ width: $link-size;
+ height: $link-size;
+ cursor: pointer;
+ @include background-img($logo-path);
+ }
+
+ .log-work-popup {
+ display: none;
+ background-color: $color-background;
+ padding: 12px 16px;
+ box-shadow: $panel-shadow;
+
+ &.active {
+ position: absolute;
+ display: flex;
+ flex-direction: column;
+ top: 25px;
+ left: -75px;
+ z-index: $z-index-base-popup;
+ }
+
+ .popup-section {
+ display: flex;
+ flex-direction: column;
+ padding-bottom: 12px;
+ }
+
+ .popup-section:last-of-type {
+ padding-bottom: 0;
+ }
+
+ .buttons-section {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: flex-end;
+
+ .product-placement {
+ font-size: 12px;
+ flex-grow: 1;
+ }
+ }
+
+ .grid-row {
+ display: flex;
+ border: 1px solid $color-border;
+
+ .grid-cell {
+ display: flex;
+ flex-grow: 1;
+ border-right: 1px solid $color-border;
+ text-align: center;
+
+ &.active {
+ font-weight: bold;
+ background-color: lighten($color-primary, 75%);
+ }
+
+ input {
+ display: none;
+ }
+
+ label {
+ width: 100%;
+ height: 100%;
+ padding: 6px 8px;
+ color: $color-text;
+ }
+ }
+
+ .grid-cell:last-of-type {
+ border: none;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ReportJ.Extension.Chrome/app/manifest.json b/ReportJ.Extension.Chrome/app/manifest.json
index 193ead7..d8e6d65 100644
--- a/ReportJ.Extension.Chrome/app/manifest.json
+++ b/ReportJ.Extension.Chrome/app/manifest.json
@@ -34,8 +34,7 @@
"*://*/*"
],
"css": [
- // todo: uncomment when styles would be added. MR
- //"build/content.css"
+ "build/content.css"
],
"js": [
"build/manifest.js",
diff --git a/ReportJ.Extension.Chrome/config/base.config.js b/ReportJ.Extension.Chrome/config/base.config.js
index 4c96666..1d0891d 100644
--- a/ReportJ.Extension.Chrome/config/base.config.js
+++ b/ReportJ.Extension.Chrome/config/base.config.js
@@ -18,6 +18,11 @@ function getCssLoaders() {
];
}
+function getSassLoaders() {
+ const cssLoaders = getCssLoaders();
+ return cssLoaders.concat(["sass-loader"]);
+}
+
module.exports = function () {
return {
entry: {
@@ -50,11 +55,18 @@ module.exports = function () {
use: getCssLoaders()
})
},
+ {
+ test: /\.scss$/,
+ use: ExtractTextPlugin.extract({
+ use: getSassLoaders()
+ })
+ },
]
},
resolve: {
alias: {
- "~": path.resolve(__dirname, "../app")
+ "~": path.resolve(__dirname, "../app"),
+ "root": path.resolve(__dirname, "../app"),
},
extensions: [".js"]
},
diff --git a/ReportJ.Extension.Chrome/package.json b/ReportJ.Extension.Chrome/package.json
index abb84e8..47951cb 100644
--- a/ReportJ.Extension.Chrome/package.json
+++ b/ReportJ.Extension.Chrome/package.json
@@ -30,12 +30,15 @@
"eslint": "^4.7.2",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
+ "node-sass": "^4.6.0",
"postcss": "^6.0.12",
"postcss-discard-duplicates": "^2.1.0",
"postcss-loader": "^2.0.6",
+ "raw-loader": "^0.5.1",
+ "sass-loader": "^6.0.6",
"style-loader": "^0.18.2",
"uglifyjs-webpack-plugin": "^0.4.6",
- "webpack": "^3.6.0",
+ "webpack": "^3.8.1",
"webpack-merge": "^4.1.0"
},
"dependencies": {