prodpad

hehe :)

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name         prodpad
// @namespace    proximis
// @version      1.18
// @description  hehe :)
// @author       teles
// @match        https://app.prodpad.com/*
// @require      https://code.jquery.com/jquery-2.2.4.min.js
// @run-at       document-end
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    var manual = false;
    var sheet = (function() {
        var style = document.createElement("style");
        style.appendChild(document.createTextNode(""));
        document.head.appendChild(style);
        return style.sheet;
    })();
    sheet.insertRule(".roadmap-column__body { display: none; }", 0);
    sheet.insertRule(".roadmap-column-wrapper .roadmap-column { padding-bottom: 0; }", 0);
    sheet.insertRule(".roadmap-column { min-height: inherit !important; }", 0);
    sheet.insertRule(".pxroadmap { margin-bottom: 100px; margin-top: 20px; }", 0);
    sheet.insertRule(".pxms { display: none; }", 0);
    sheet.insertRule(".pxload { margin: 30px; }", 0);
    sheet.insertRule(".pxidea select, .input-select.small::after { opacity: 0.3; }", 0);
    sheet.insertRule(".pnewidea { background: #d9534f; }", 0);
    sheet.insertRule(".p037b4910498411e5b90322000a23849c { background: #d9534f; }", 0);
    sheet.insertRule(".p037b4f5a498411e5b90322000a23849c { background: #f0ad4e; }", 0);
    sheet.insertRule(".p037b50ae498411e5b90322000a23849c { background: #5cb85c; }", 0);
    sheet.insertRule(".p037b51da498411e5b90322000a23849c { background: #5cb85c; }", 0);
    sheet.insertRule(".p037b5482498411e5b90322000a23849c { background: black; }", 0);
    sheet.insertRule(".pxfilter { background: white; position: absolute; top: 10px; right: 120px; height: 42px; width:100px; padding:5px; }", 0);
    sheet.insertRule(".pxalert { background: chartreuse; transform: scale(2.0); transition: transform 0.3s; }", 0);
    sheet.insertRule(".pxstats > td { padding-top: 0; padding-bottom: 0; }", 0);
    sheet.insertRule(".pxfilterstatepnewidea { border-bottom-color: #d9534f; }", 0);
    sheet.insertRule(".pxfilterstatep037b4910498411e5b90322000a23849c { border-bottom-color: #d9534f; }", 0);
    sheet.insertRule(".pxfilterstatep037b4f5a498411e5b90322000a23849c { border-bottom-color: #f0ad4e; }", 0);
    sheet.insertRule(".pxfilterstatep037b50ae498411e5b90322000a23849c { border-bottom-color: #5cb85c; }", 0);
    sheet.insertRule(".pxfilterstatep037b51da498411e5b90322000a23849c { border-bottom-color: #5cb85c; }", 0);
    sheet.insertRule(".pxfilterstatep037b5482498411e5b90322000a23849c { border-bottom-color: black; }", 0);
    sheet.insertRule(".pxfilterstate { cursor: pointer; border: 40px solid #f4f4f4; border-bottom-width: 4px; border-top-width: 4px; }", 0);

    var session = JSON.parse(localStorage.getItem('pp_sess'));
    var role = session.current_roles ? session.current_roles[0].role : '';
    var apiKey = localStorage.getItem('apikey') || '', apiRoadmaps, apiUsers;
    if (apiKey) {
        $.get('https://api.prodpad.com/v1/roadmaps/10435?apikey='+apiKey).then(function(json){
            apiRoadmaps = json;
            console.log(json);
        }, function(){
            $('#apikey').show();
        });
        $.get('https://api.prodpad.com/v1/users?apikey='+apiKey).then(function(json){
            apiUsers = json;
        });
    } else {
        $.ajax({
            method: 'GET',
            url: 'https://api-beta.prodpad.com/api/v2/user/'+session.user.id+'/apikey',
            headers: {
                'X-API-Token': session.token
            }
        }).then(function(json){
           apiKey = json.apikey;
           $('#apikey').val(apiKey);
           localStorage.setItem('apikey', apiKey);
           $.get('https://api.prodpad.com/v1/roadmaps/10435?apikey='+apiKey).then(function(json){
               apiRoadmaps = json;
           }, function(){
               $('#apikey').show();
           });
           $.get('https://api.prodpad.com/v1/users?apikey='+apiKey).then(function(json){
               apiUsers = json;
           });
        }, function(){
            $('#apikey').show();
        });
    }
    var api = setInterval(function(){
        if (!$('#apikey').length) {
            $('.main-head__right').prepend('<li><input id="apikey" placeholder="API Key"/></li>');
            $('#apikey').val(apiKey);
            $('#apikey').hide();
            $('#apikey').keyup(function(){
                localStorage.setItem('apikey', $('#apikey').val());
                if ($('#apikey').val()) {
                    apiKey = $('#apikey').val();
                    $.get('https://api.prodpad.com/v1/roadmaps/10435?apikey='+apiKey).then(function(json){
                        apiRoadmaps = json;
                    }, function(){
                        $('#apikey').show();
                    });
                }
            });
        } else {
            clearInterval(api);
        }
    }, 200);

    var linkify = function(txt) {
        return txt.replace(/([^"]https?:\/\/[^\s<\)]*)/g, '<a href="$1">$1</a>').replace(/&nbsp;/g, ' ');
    };

    var enhanceRoadmap = function() {
        if ($('.pxroadmap').length) {
            return;
        }
        $('select').each(function(){
            $('option[value="'+$(this)[0].value+'"]', this).attr('selected', 'selected');
        });
        $('.card__pipeline > button').each(function() {
            if (!$(this).parent().find('.card__pipeline-list').length) {
                $(this).click();
            }
        });
        var roadmaps = {};
        $('.roadmap-column').each(function(){
            var version = $('.roadmap-column__head-title', this).text();
            if (!version || $.trim(version) == 'Enter a name...') {
                version = $('.roadmap-column__head-title textarea', this)[0]._value;
            }
            version = $.trim(version);
            $('.roadmap-column__head-title', this).attr('version', version);
            roadmaps[version] = [];
            $('.card', this).each(function(){
                var ideas = {};
                var title = $('.card__body .card__title', this).text();
                if (!title || $.trim(title) == 'Enter a card name...') {
                    title = $('.card__body .card__title textarea', this)[0]._value;
                }
                $('.idea-list-item', this).each(function(){
                    var id = $('.idea-list-item__body-header a', this).attr('href').split('/')[2];
                    ideas[id] = {
                        id: id,
                        num: $('.idea-list-item__body-header', this).text().replace(/IDEA ([0-9]*) .*/, '<strong style="font-size:1.3em"><a href="/ideas/$1"><u>$1</u></a> <a href="/ideas/$1" target="_blank"><u>➚</u></a></strong>'),
                        title: $('.idea-list-item__body-header', this).text().replace(/IDEA [0-9]*[ ]+-[ ]+/, ''),
                        status: $('.idea-list-item__status div:first', this).html(),
                        state: $('.idea-list-item__status div:first select option[selected]', this).text(),
                        class: 'p' + ($('.idea-list-item__status div:first option[selected]', this).attr('value').replace(/-/g, '') || 'newidea')
                    };
                });
                roadmaps[version].push({title: title, ideas: ideas});
            });
        });
        //console.log(roadmaps);
        var html = '<table class="pxroadmap">';
        var versionStats = {};
        for (var v in roadmaps) {
            versionStats[v] = {};
            for(var c=0; c<roadmaps[v].length; c++) {
                if (Object.keys(roadmaps[v][c].ideas).length) {
                    html += '<tr class="pxtitle pxms '+btoa(v).replace(/=/g, '')+'"><th colspan="2" style="font-size:1.3em">'+roadmaps[v][c].title+'</th></tr>';
                    for(var id in roadmaps[v][c].ideas) {
                        var idea = roadmaps[v][c].ideas[id];
                        html += '<tr class="pxidea pxidea'+idea.id+' pxms '+btoa(v).replace(/=/g, '')+' pxIdea'+idea.class+'">';
                        html += '<td width="140px" class="idea-list-item__status '+idea.class+'"><div class="input-select small" style="pointer-events: none;">'+idea.status+'</div></td>';
                        html += '<td style="vertical-align:top">'+idea.num+' - <a href="javascript:;" num="'+$(idea.num).find('u').text()+'" class="pxideatitle" style="font-size:1.2em; color:black">'+idea.title+'</a></td>';
                        html += '</tr>';
                        if (!versionStats[v][roadmaps[v][c].ideas[id].state]) {
                            versionStats[v][roadmaps[v][c].ideas[id].state] = { n: 1, c: roadmaps[v][c].ideas[id].class };
                        } else {
                            versionStats[v][roadmaps[v][c].ideas[id].state].n += 1;
                        }
                    }
                }
            }
        }
        html += '</table>';
        for (var vv in versionStats) {
            var nb = Object.keys(versionStats[vv]).length;
            var shtml = '<table class="pxroadmapfilter"><tr class="pxstats pxms '+btoa(vv).replace(/=/g, '')+'">';
            for(var s in versionStats[vv]) {
                shtml += '<td class="pxfilterstate pxfilterstate'+versionStats[vv][s].c+'" state="'+versionStats[vv][s].c+'" width="'+(100/nb)+'%">'+s+' : <strong style="font-size:1.2em;">'+versionStats[vv][s].n+'</strong></td>';
            }
            shtml += '</tr></table>';
            html = shtml + html;
        }
        $('.pxload').hide();
        var prefix;
        $('.roadmap-main').append(html);
        if (!$('#pxskip').length) {
            $('.right--btns .padded-content').prepend('<button id="pxskip" class="btn--small from-tablet" style="float:left"><span class="fa fa-undo"></span> <span>Revenir à l\'interface par défaut</span></button>');
        }
        if (!$('#pxreload').length) {
            $('.right--btns .padded-content').prepend('<button id="pxreload" class="btn--small from-tablet" style="float:left"><span class="fa fa-refresh"></span> <span>Recharger l\'interface qui déchire</span></button>');
        }
        $('.roadmap-column__head-title').append('<input placeholder="IDEA..." class="pxfilter"/>');
        $('.roadmap-column__head-title').click(function(){
            $('.roadmap-column__head-title').css('border-bottom', 'none').css('color', 'black').css('padding', '5px');
            $('.pxfilter').hide().val('');
            $('.pxfilter', this).show().focus();
            $(this).css('border-bottom', '4px solid #25a7d9');
            $('.pxms, .pxidealoaded').hide();
            $('.'+btoa($(this).attr('version')).replace(/=/g, '')).show();
            prefix = btoa($(this).attr('version')).replace(/=/g, '');
        });
        var sectionToShow = 'eq(1)';
        if ($('.roadmap-candidates-section .roadmap-column__body').length) {
            sectionToShow = 'last';
        } else if ($('.roadmap-completed .roadmap-column__body').length) {
            sectionToShow = 'first';
        }
        $('.roadmap-column__head-title:'+sectionToShow).click();
        $('.pxfilter').keyup(function(){
            if ($(this).val()) {
                $('.'+prefix+'.pxidea, .'+prefix+'.pxtitle').hide();
                $('.'+prefix+'.pxidea'+$(this).val()).show();
                $('.'+prefix+'.pxidea'+$(this).val()).prevAll('.pxtitle:first').show();
            } else {
                $('.'+prefix+'.pxidea, .'+prefix+'.pxtitle').show();
            }
            $('.pxidealoaded').hide();
        });
        $('.pxfilterstate').click(function(){
            $('.'+prefix+'.pxidea, .'+prefix+'.pxtitle').hide();
            $('.'+prefix+'.pxidea.pxIdea'+$(this).attr('state')).show();
            $('.'+prefix+'.pxidea.pxIdea'+$(this).attr('state')).each(function(){
                $(this).prevAll('.pxtitle:first').show();
            });
        });
        $('#pxreload').click(function(){
            $('.pxroadmap, .pxroadmapfilter').remove();
            $('.roadmap-column__body').hide();
            $('.pxload').show();
            if (manual) {
                enhanceRoadmap();
                if ($.trim($(this).text()) == 'View candidate cards') {
                    console.log('auto');
                    setTimeout(function(){
                        $('.roadmap-candidates-section .roadmap-column__head-title').click();
                    }, 300);
                }
            }
            manual = false;
        });
        $('div.from-tablet button, .multi-menu a').click(function(){
            $('.pxroadmap, .pxroadmapfilter').remove();
            $('.roadmap-column__body').hide();
            $('.pxload').show();
        });
        $('#pxskip').click(function(){
            manual = true;
            $('.pxroadmap, .pxroadmapfilter').remove();
            $('.roadmap-column__body').show();
            $('.roadmap-column__head-title').css('border', 'none').css('color', 'black').css('padding', '5px');
            $('.pxfilter').remove();
            $('.roadmap-column__head-title').off('click');
        });
        $('.pxideatitle').click(function(){
            if ($(this).parents('tr:first').next().hasClass('pxidealoaded')) {
                $(this).parents('tr:first').next().toggle();
            } else if (!apiKey) {
                $('#apikey').addClass('pxalert');
                $('.main-head__user .multi-menu').addClass('is-active');
                $('.main-head__user .multi-menu__body ul li:first a').css('color', '#25a7d9');
                setTimeout(function(){ $('#apikey').removeClass('pxalert'); }, 2000);
            } else {
                var found = false;
                var num = $(this).attr('num');
                var rnd = Math.random().toString().substring(3, 10);
                $('<tr class="pxidea pxidealoaded"><td></td><td id="pxi'+rnd+'">Chargement...</td></tr>').insertAfter($(this).parents('tr:first'));
                dance:
                for(var r=0; r<apiRoadmaps.length; r++) {
                    for(var c=0; c<apiRoadmaps[r].cards.length; c++) {
                        for(var i=0; i<apiRoadmaps[r].cards[c].pipeline.ideas.length; i++) {
                            if ($(this).text() == apiRoadmaps[r].cards[c].pipeline.ideas[i].title) {
                                var id = apiRoadmaps[r].cards[c].pipeline.ideas[i].id;
                                $.get('https://api.prodpad.com/v1/ideas/'+id+'?expand&apikey='+apiKey).then(function(json){
                                    //console.log(json);
                                    var html = '<table>';
                                    html += '<tr><th width="120">Creator :</th><th style="font-weight:normal">'+(json.creator?json.creator.display_name:'-')+'</th></tr>';
                                    html += '<tr><th>Onwer :</th><th style="font-weight:normal">'+(json.owner?json.owner.display_name:'-')+'</th></tr>';
                                    if (json.description) {
                                        html += '<tr><th>Description :</th><th style="font-weight:normal">'+linkify(json.description)+'</th></tr>';
                                    }
                                    if (json.functional_spec) {
                                        html += '<tr><th>Specifications :</th><th style="font-weight:normal">'+linkify(json.functional_spec)+'</th></tr>';
                                    }
                                    if (json.notes) {
                                        html += '<tr><th>Notes :</th><th style="font-weight:normal">'+linkify(json.notes)+'</th></tr>';
                                    }
                                    if (json.comments && json.comments.length) {
                                        html += '<tr><th>Commentaires :</th><th style="font-weight:normal">';
                                        for(var c=0; c<json.comments.length; c++) {
                                            html += '<div style="margin-bottom:20px">Le <u>'+json.comments[c].created_at + '</u> par <strong>' + json.comments[c].created_by.display_name + '</strong> :<br/>' + linkify(json.comments[c].comment)+'</div>';
                                        }
                                        html += '</th></tr>';
                                    }
                                    if (json.mockups && json.mockups.length) {
                                         html += '<tr><th>Images :</th><th style="font-weight:normal"><a href="/ideas/'+num+'/mockups">Voir l' + (json.mockups.length > 1 ? 'es '+ json.mockups.length +' images' : '\'image') + '</a></th></tr>';
                                    }
                                    html += '</table>';
                                    $('#pxi'+rnd).html(html);
                                }, function() {
                                    $('#pxi'+rnd).html('Erreur ProdPad lors du chargement.&nbsp;&nbsp;<a href="/ideas/'+num+'">Voir le détail de de l\'idée</a>&nbsp;&nbsp;&nbsp;<a href="/ideas/'+num+'" target="_blank">➚</a>');
                                });
                                found = true;
                                break dance;
                            }
                        }
                    }
                }
                if (!found) {
                    $('#pxi'+rnd).html('Cette idée n\'est pas dans l\'api de la roadmap et donc le détail n\'est pas affichable ici.&nbsp;&nbsp;<a href="/ideas/'+num+'">Voir le détail de de l\'idée</a>&nbsp;&nbsp;&nbsp;<a href="/ideas/'+num+'" target="_blank">➚</a>');
                }
            }
        });
    };
    setInterval(function(){
        if ($('.roadmap-column-wrapper').length) {
            if (!$('.pxload').length) {
                $('.roadmap-main').append('<h1 class="pxload">Chargement de l\'interface qui déchire...</h1>');
            }
            if (!$('.pxroadmap').length && !manual) {
                setTimeout(enhanceRoadmap, 1000);
            }
        }
    }, 200);
})();