Jira Task Priority Colorizer

Change card colors based on Jira task priority and add an emoji if a task has been in a status for more than 3 working days

当前为 2024-10-15 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Jira Task Priority Colorizer
// @namespace    http://tampermonkey.net/
// @version      0.2.1
// @description  Change card colors based on Jira task priority and add an emoji if a task has been in a status for more than 3 working days
// @author       erolatex
// @include      https://*/secure/RapidBoard.jspa*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // CSS styles to change card colors and position the emoji
    const styleContent = `
        .ghx-issue[data-priority*="P0"] {
            background-color: #FFADB0 !important;
        }
        .ghx-issue[data-priority*="P1"] {
            background-color: #FF8488 !important;
        }
        .ghx-issue[data-priority*="P2"] {
            background-color: #FFD3C6 !important;
        }
        .ghx-issue[data-priority*="P3"],
        .ghx-issue[data-priority*="P4"] {
            background-color: #FFF !important;
        }
        .stale-emoji {
            position: absolute;
            bottom: 5px;
            right: 5px;
            font-size: 32px;
            display: flex;
            align-items: center;
        }
        .stale-emoji span {
            margin-left: 5px;
            font-size: 16px;
            color: #000;
        }
        .ghx-issue {
            position: relative;
        }
    `;

    // Inject CSS styles into the page
    const styleElement = document.createElement('style');
    styleElement.type = 'text/css';
    styleElement.appendChild(document.createTextNode(styleContent));
    document.head.appendChild(styleElement);

    /**
     * Calculates the number of working days between two dates.
     * Excludes Saturdays and Sundays.
     * @param {Date} startDate - The start date.
     * @param {Date} endDate - The end date.
     * @returns {number} - The number of working days.
     */
    function calculateWorkingDays(startDate, endDate) {
        let count = 0;
        let currentDate = new Date(startDate);
        // Set time to midnight to avoid timezone issues
        currentDate.setHours(0, 0, 0, 0);
        endDate = new Date(endDate);
        endDate.setHours(0, 0, 0, 0);

        while (currentDate <= endDate) {
            const dayOfWeek = currentDate.getDay();
            if (dayOfWeek !== 0 && dayOfWeek !== 6) { // Exclude Sunday (0) and Saturday (6)
                count++;
            }
            currentDate.setDate(currentDate.getDate() + 1);
        }
        return count;
    }

    /**
     * Calculates the number of working days based on the total days in the column.
     * Assumes days are counted backward from the current date.
     * @param {number} daysInColumn - Total number of days in the column.
     * @returns {number} - The number of working days.
     */
    function calculateWorkingDaysFromDaysInColumn(daysInColumn) {
        let workingDays = 0;
        let currentDate = new Date();
        // Set time to midnight to avoid timezone issues
        currentDate.setHours(0, 0, 0, 0);

        for (let i = 0; i < daysInColumn; i++) {
            const dayOfWeek = currentDate.getDay();
            if (dayOfWeek !== 0 && dayOfWeek !== 6) { // Exclude Sunday (0) and Saturday (6)
                workingDays++;
            }
            // Move to the previous day
            currentDate.setDate(currentDate.getDate() - 1);
        }
        return workingDays;
    }

    /**
     * Updates the priorities of the cards and adds/removes the emoji based on working days.
     */
    function updateCardPriorities() {
        let cards = document.querySelectorAll('.ghx-issue');

        cards.forEach(card => {
            // Update priority attribute
            let priorityElement = card.querySelector('.ghx-priority');
            if (priorityElement) {
                let priority = priorityElement.getAttribute('title') || priorityElement.getAttribute('aria-label') || priorityElement.innerText || priorityElement.textContent;
                if (priority) {
                    card.setAttribute('data-priority', priority);
                }
            }

            // Initialize working days count
            let workingDays = 0;

            // Attempt to get the start date from a data attribute (e.g., data-start-date)
            let startDateAttr = card.getAttribute('data-start-date'); // Example: '2024-04-25'
            if (startDateAttr) {
                let startDate = new Date(startDateAttr);
                let today = new Date();
                workingDays = calculateWorkingDays(startDate, today);
            } else {
                // If start date is not available, use daysInColumn
                let daysElement = card.querySelector('.ghx-days');
                if (daysElement) {
                    let title = daysElement.getAttribute('title');
                    if (title) {
                        let daysMatch = title.match(/(\d+)\s+days?/);
                        if (daysMatch && daysMatch[1]) {
                            let daysInColumn = parseInt(daysMatch[1], 10);
                            workingDays = calculateWorkingDaysFromDaysInColumn(daysInColumn);
                        }
                    }
                }
            }

            // Check and update the emoji 💩
            if (workingDays > 3) {
                let existingEmoji = card.querySelector('.stale-emoji');
                if (!existingEmoji) {
                    let emojiContainer = document.createElement('div');
                    emojiContainer.className = 'stale-emoji';

                    let emojiElement = document.createElement('span');
                    emojiElement.textContent = '💩';

                    let daysText = document.createElement('span');
                    daysText.textContent = `${workingDays} d`;

                    emojiContainer.appendChild(emojiElement);
                    emojiContainer.appendChild(daysText);

                    card.appendChild(emojiContainer);
                } else {
                    let daysText = existingEmoji.querySelector('span:last-child');
                    daysText.textContent = `${workingDays} d`;
                }
            } else {
                let existingEmoji = card.querySelector('.stale-emoji');
                if (existingEmoji) {
                    existingEmoji.remove();
                }
            }
        });
    }

    // MutationObserver to watch for changes in the DOM and update priorities accordingly
    const observer = new MutationObserver(() => {
        updateCardPriorities();
    });

    observer.observe(document.body, { childList: true, subtree: true });

    // Update priorities when the page loads
    window.addEventListener('load', function() {
        updateCardPriorities();
    });

    // Periodically update priorities every 5 seconds
    setInterval(updateCardPriorities, 5000);
})();