Greasy Fork

JIRA Board Epic Filter

Helper functions to only see one epic at a time on the board

// ==UserScript==
// @name         JIRA Board Epic Filter
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  Helper functions to only see one epic at a time on the board
// @author       cartok
// @match        https://*.atlassian.net/*/boards/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=atlassian.net
// @grant        none
// @license      MIT
// ==/UserScript==

// TODOs:
// * feature: pulling instead of waiting for the installation
// * feature: better ux, toggleable buttons / radios
// * fix: standalone subtasks are hidden, can epic get detected?

(function() {

    function installEpicToolbar () {
        const toolbar = document.getElementById('epicToolbar')
        if (toolbar) {
            toolbar.remove()
        }

        createEpicToolbar()
    }

    function createEpicToolbar () {
        const container = document.getElementById('ghx-rabid')
        const toolbar = container.querySelector('#ghx-operations')
        const range = document.createRange()

        //   * create styles and container div
        const epicToolbar = range.createContextualFragment(`
            <style>
                #epic-ghx-operations {
                    display: flex;
                    flex-direction: row;
                    gap: 0.5rem;
                    margin-bottom: 2rem;
                }

                .epic-ghx-button {
                    border: none;
                    padding: 0.5rem;
                    font-weight: 500;
                    background-color: rgba(9, 30, 66, 0.04);
                    color: rgb(66, 82, 110);
                }

                .epic-ghx-button-reset {
                    background-color: #0052cc;
                    color: #fff;
                }
            </style>
            <div id="epic-ghx-operations" />
        `)
        const epicToolbarContainer = epicToolbar.getElementById('epic-ghx-operations')

        //  * get all information required for button rendering
        const epicTasks = getTasks().filter(i => i.querySelector('.ghx-highlighted-field'))
        const visibleEpics = [...new Set(epicTasks.map(i => i.querySelector('.ghx-highlighted-field').textContent))]

        //  * add epic buttons
        visibleEpics.forEach(epic => {
            const buttonFragment = range.createContextualFragment(`
                <button class="epic-ghx-button">${epic}</button>
            `)
            buttonFragment.querySelector('.epic-ghx-button').addEventListener('click', () => setEpicFilter(epic))
            epicToolbarContainer.appendChild(buttonFragment)
        })

        //  * add reset button
        const resetButtonFragment = range.createContextualFragment(`
            <button class="epic-ghx-button epic-ghx-button-reset">Reset</button>
        `)
        resetButtonFragment.querySelector('.epic-ghx-button').addEventListener('click', resetEpicFilter)
        epicToolbarContainer.appendChild(resetButtonFragment)

        container.insertBefore(epicToolbar, toolbar.nextSibling)
    }

    function setEpicFilter (epic) {
        resetEpicFilter()

        //  * filter out all tasks that aren't epic!
        //  * filter out all tasks from epics that are not enabled
        getTasks()
            .filter(i => {
                const epicElement = i.querySelector('.ghx-highlighted-field')
                return !epicElement || epicElement.textContent !== epic
            })
            .forEach(i => {
                i.style.display = 'none'
            })
    }

    function resetEpicFilter () {
        getTasks().forEach(i => {
            i.style.display = null
        })
    }

    function getTasks () {
        return [...document.querySelectorAll('.ghx-wrap-issue > *')]
            .filter(i => !i.classList.contains('ghx-show-old'))
    }

    setTimeout(installEpicToolbar, 1000 * 10)
})();