NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Coloring jira tickets // @namespace https://github.com/D4ST1N/jira-tickets-coloring/ // @version 1.0.1 // @description Coloring jira tickets // @author D4ST1N // @license MIT // @require https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js // @match https://jira.betlab.com/* // @grant none // ==/UserScript== (function() { 'use strict'; Vue.component( 'jiracolors', { template: ` <div class="jc-root"> <button class="jc-expand-button" type="button" @click="expandLegend">Show colors</button> <div v-show="legendShowed" class="js-legend"> <div class="js-legend__overlay" @click.self="collapseLegend"> <div class="js-legend__content"> <div v-for="item in statuses" :key="item.status" class="js-legend__item"> <span class="js-legend__item-status">{{ item.status }}</span> <div class="js-legend__item-detail"> <span>Color: </span> <input class="js-legend__item-color" type="color" :value="item.color" @change="changeColor(item, $event)"> </div> <div class="js-legend__item-detail js-legend__item-detail--full"> <span>Opacity: {{ opacityInteger(item.opacity) }}%</span> <input class="js-legend__item-opacity" type="range" min="0" max="100" :value="opacityInteger(item.opacity)" @change="changeOpacity(item, $event)" @input="changeOpacity(item, $event)"> </div> </div> </div> </div> </div> </div>`, data() { return { styles: document.createElement('style'), legendShowed: false, baseTemplate: ` .ghx-issue [data-tooltip="Status: {status}" i]::before { background: {color}; opacity: {opacity}; }`, baseStyles: ` .ghx-issue [data-tooltip~="Status:"]::before { content: ''; position: absolute; width: 100%; height: 100%; opacity: {opacity}; top: 0; left: 0; pointer-events: none; } .jc-root { position: fixed; bottom: 5px; left: 60px; z-index: 10; } .jc-expand-button { background: #455A64; color: #fff; padding: 6px 18px; font-size: 16px; border: 0; border-radius: 6px; box-shadow: 2px 2px 5px 0 #455A64; line-height: 1; cursor: pointer; } .js-legend { position: fixed; top: 0; left: 0; width: 100%; height: 100%; } .js-legend__overlay { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; background: rgba(69, 90, 100, .8); } .js-legend__content { background: #fff; padding: 20px 50px; display: flex; flex-wrap: wrap; max-width: 800px; justify-content: space-between; } .js-legend__item { font-size: 16px; width: 45%; margin-bottom: 20px; background: #E3F2FD; padding: 5px 15px; box-sizing: border-box; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; } .js-legend__item-status { font-weight: bold; } .js-legend__item-color { margin-left: 10px; } .js-legend__item-opacity { width: 220px; } .js-legend__item-detail { display: flex; justify-content: space-between; align-items: center; } .js-legend__item-detail--full { width: 100%; } .ghx-issue .ghx-end, .ghx-issue.ghx-selected .ghx-end { box-shadow: none; background: transparent; }`, statuses: [ { status: 'Open', color: '#607D8B', opacity: 0.25, }, { status: 'Reopened', color: '#607D8B', opacity: 0.25, }, { status: 'Reopen', color: '#607D8B', opacity: 0.25, }, { status: 'System analysis', color: '#9E9E9E', opacity: 0.25, }, { status: 'Testing', color: '#00BCD4', opacity: 0.25, }, { status: 'In Progress', color: '#8BC34A', opacity: 0.25, }, { status: 'In development', color: '#8BC34A', opacity: 0.25, }, { status: 'Hold', color: '#9E9E9E', opacity: 0.25, }, { status: 'UAT', color: '#9E9E9E', opacity: 0.25, }, { status: 'Review', color: '#009688', opacity: 0.25, }, { status: 'Ready for Deploy', color: '#2196F3', opacity: 0.25, }, { status: 'Ready for Testing', color: '#03A9F4', opacity: 0.25, }, { status: 'Proposed to cancel', color: '#FFC107', opacity: 0.25, }, { status: 'Closed', color: '#00C853', opacity: 0.25, }, { status: 'Done', color: '#00C853', opacity: 0.25, }, { status: 'Canceled', color: '#FF5722', opacity: 0.25, }, { status: 'Rejected', color: '#FF5722', opacity: 0.25, }, ], }; }, mounted() { this.getStatuses(); this.createStyles(); this.updateStyles(); }, computed: { styleString() { return this.baseStyles.replace('{opacity}', this.baseOpacity); }, baseOpacity() { return 0.25; }, }, methods: { getStatuses() { try { const statuses = JSON.parse(localStorage.getItem('jc-statuses')); console.log(statuses); if (statuses) { this.statuses = statuses; } } catch (e) {} }, setStatuses() { localStorage.setItem('jc-statuses', JSON.stringify(this.statuses)); }, expandLegend() { this.legendShowed = true; }, collapseLegend() { this.legendShowed = false; }, createStyles() { document.body.appendChild(this.styles); }, buildStyles() { return this.statuses.map(item => this.baseTemplate .replace('{status}', item.status) .replace('{color}', item.color) .replace('{opacity}', (item.opacity || this.baseOpacity))).join(''); }, updateStyles() { this.styles.innerHTML = `${this.styleString}${this.buildStyles()}`; this.setStatuses(); }, changeColor(item, event) { item.color = event.target.value; this.updateStyles(); }, changeOpacity(item, event) { item.opacity = Math.round(event.target.value) / 100; this.updateStyles(); }, opacityInteger(opacity) { return Math.round(opacity * 100); }, }, } ); const appRoot = document.createElement('div'); appRoot.id = 'app'; appRoot.innerHTML = '<jiracolors></jiracolors>'; document.body.appendChild(appRoot); new Vue( { el: '#app', } ); })();