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": {