Skip to content

Instantly share code, notes, and snippets.

@jphastings
Last active May 18, 2022 08:59
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jphastings/28aeb9fc9cee1be8576b67461bf5d7ba to your computer and use it in GitHub Desktop.
Save jphastings/28aeb9fc9cee1be8576b67461bf5d7ba to your computer and use it in GitHub Desktop.
JIRA Standup!

Standup helper

The following Manager Code™ will let you walk through your Jira Project board person by person, in a 'random order' (deterministic on a per-day basis) and show only their tickets.

It works in my Chrome browser; your mileage may vary… (forks of this gist very welcome!)

To use it:

  1. Copy the code below
  2. Create a new bookmark in Chrome
  3. Paste the code in, and name it something cool
  4. Go to your Jira Project board
  5. Click your new bookmarklet and win!
javascript:(function()%7Bconst%20assigneeLabels%3Ddocument.querySelector(%22%23ASSIGNEE%20%2B%20div%22).children%2CquickFiltersList%3D%24(%22%23ghx-quick-filters%20ul%22)%5B0%5D%2CignoreNames%3D%5B%22%22%2C%22Unassigned%22%5D%2Cpeople%3DArray.from(assigneeLabels).reduce((e%2Ct)%3D%3E%7Bif(t.querySelector('span%5Brole%3D%22img%22%5D'))%7Bconst%20n%3Dt.querySelector('span%5Brole%3D%22img%22%5D').getAttribute(%22aria-label%22)%3BignoreNames.includes(n)%7C%7C(e%5Bn%5D%3Dt.querySelector(%22input%22))%7Dreturn%20e%7D%2C%7B%7D)%3Basync%20function%20sha256(e)%7Bconst%20t%3Dnew%20TextEncoder(%22utf-8%22).encode(e)%2Cn%3Dawait%20crypto.subtle.digest(%22SHA-256%22%2Ct)%3Breturn%20Array.from(new%20Uint8Array(n)).map(e%3D%3E(%2200%22%2Be.toString(16)).slice(-2)).join(%22%22)%7Dconst%20salt%3D(new%20Date).toDateString()%2Chashify%3De%3D%3Esha256(e%2Bsalt).then(t%3D%3E%5Be%2CparseInt(t%2C16)%5D)%2Cmapify%3De%3D%3Ee.reduce((e%2Ct)%3D%3E(e%5Bt%5B0%5D%5D%3Dt%5B1%5D%2Ce)%2C%7B%7D)%3BPromise.all(Object.keys(people).map(hashify)).then(mapify).then(e%3D%3EObject.keys(people).sort((t%2Cn)%3D%3Ee%5Bt%5D-e%5Bn%5D)).then(e%3D%3E%7Bconst%20t%3Ddocument.createElement(%22li%22)%2Cn%3Ddocument.createElement(%22button%22)%3Bn.className%3D%22aui-button%22%2Cn.style.paddingRight%3D%2210px%22%3Blet%20i%3D-1%3Bn.addEventListener(%22click%22%2C()%3D%3E%7Bconst%20s%3De%5Bi%2B%3D1%5D%3BObject.keys(people).forEach(e%3D%3E%7Be%3D%3Ds!%3Dpeople%5Be%5D.checked%26%26people%5Be%5D.click()%7D)%3Bconst%20r%3De%5Bi%2B1%5D%3Br%3Fn.textContent%3D%22Next%20in%20standup%20(%22%2Br.split(%22%20%22)%5B0%5D%2B%22)%22%3At.remove()%7D)%2Ct.appendChild(n)%2CquickFiltersList.appendChild(t)%2Cn.click()%7D)%7D)()

Example

// I keep hacking at this, one day I'm going to have to refactor it properly.
const assigneeLabels = document.querySelector('#ASSIGNEE + div').children;
const quickFiltersList = $('#ghx-quick-filters ul')[0];
const ignoreNames = ['', 'Unassigned'];
const people = Array.from(assigneeLabels).reduce((acc, label) => {
const img = label.querySelector('span[role="img"]');
if (img) {
const name = label.querySelector('span[role="img"]').getAttribute('aria-label');
if (!ignoreNames.includes(name)) {
acc[name] = label.querySelector('input');
}
}
return acc;
}, {});
async function sha256(message) {
const msgBuffer = new TextEncoder('utf-8').encode(message);
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
return hashHex;
}
const salt = new Date().toDateString();
const hashify = name => sha256(name + salt).then(hex => [name, parseInt(hex, 16)]);
const mapify = arrayOfArrays => (arrayOfArrays.reduce((acc, hashed) => {
acc[hashed[0]] = hashed[1];
return acc;
}, {}));
Promise.all(Object.keys(people).map(hashify))
.then(mapify)
.then(sortIndex => Object.keys(people).sort((a, b) => (sortIndex[a] - sortIndex[b])))
.then(orderedPeople => {
const listItem = document.createElement('li');
const doNextButton = document.createElement('button');
doNextButton.className = 'aui-button';
doNextButton.style.paddingRight = '10px';
let currentEngineer = -1;
doNextButton.addEventListener('click', () => {
currentEngineer += 1;
const name = orderedPeople[currentEngineer];
Object.keys(people).forEach(thisName => {
const desired = (thisName == name);
if (desired != people[thisName].checked)
people[thisName].click();
});
const nextName = orderedPeople[currentEngineer + 1];
if (nextName) {
doNextButton.textContent = "Next in standup (" + nextName.split(' ')[0] + ")";
} else {
listItem.remove();
}
});
listItem.appendChild(doNextButton);
quickFiltersList.appendChild(listItem);
doNextButton.click();
});
@0x2me
Copy link

0x2me commented May 18, 2022

image

Hi Jp, I noticed this doesn't work if you have more than 6 users. I think it should be fairly straight forward to add the extra users. If I change the code how to I generate the bookmarklet string?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment