Skip to content

Instantly share code, notes, and snippets.

@justinbmeyer
Last active September 12, 2024 20:24
Show Gist options
  • Save justinbmeyer/3a8c6ce07531d150e5f99aec6c0f7097 to your computer and use it in GitHub Desktop.
Save justinbmeyer/3a8c6ce07531d150e5f99aec6c0f7097 to your computer and use it in GitHub Desktop.
(async function(data){
function getIssueKey(card){
const field = card.fields.find( ({value, tooltip}) => {
return value.match( /\w+-\d+/)
});
return field.value;
}
function mapJiraKeyToCards(cards){
// get a map of all Jira IDs to cards
const jiraCardMap = new Map();
for( let card of cards ) {
const key = getIssueKey(card);
if(key){
if(jiraCardMap.has(key) ){
console.log("duplicate?", key)
}
jiraCardMap.set(key, card);
}
}
return jiraCardMap;
}
function getLastDate(milestones){
let last = -Infinity;
for( let milestone of milestones ){
for( let initiative of milestone.children ) {
if(initiative.due && new Date(initiative.due).getTime() > last ){
last = new Date(initiative.due).getTime()
}
}
}
return new Date(last);
}
const ROW_HEIGHT = 100;
const TIME_SPACE = 1800;
const lastDate = getLastDate( data );
function moveCardToRow(card, row) {
card.x = -100;
card.y = ROW_HEIGHT * row;
card.sync();
}
function cardXPosition(issue, lastDate){
if( issue.due ) {
const percent = ( new Date(issue.due) - new Date() ) / ( lastDate - new Date() );
return TIME_SPACE * percent;
} else {
return -300;
}
}
function moveCard(card, row, issue, lastDate) {
card.y = ROW_HEIGHT * row;
if( issue.due ) {
const percent = ( lastDate - new Date(issue.due) ) / ( lastDate - new Date() );
card.x = TIME_SPACE * percent;
console.log(card.x);
} else {
card.x = 0;
}
card.sync();
}
async function moveCards(){
const cards = await miro.board.get({ type: 'card' });
const jiraCardMap = mapJiraKeyToCards(cards);
// go through and move each card
let m = 0;
for( let milestone of data ){
let card = jiraCardMap.get(milestone.key) ;
if(!card) {
console.log("missing", milestone.type, milestone.key)
} else {
moveCardToRow(card, m);
jiraCardMap.delete(milestone.key)
}
for( let initiative of milestone.children ){
let childCard = jiraCardMap.get(initiative.key)
if(!childCard) {
console.log("missing", initiative.type, initiative.key);
} else {
moveCard(childCard, m, initiative, lastDate);
jiraCardMap.delete(initiative.key)
}
}
m++;
}
console.log("remaining", jiraCardMap );
}
function statusField(issue){
return {
value: issue.status,
fillColor: issue.status.toLowerCase() === "done" ? "#36b37e" : "#ffffff",
textColor: issue.status === "done" ? "#ffffff" : "#000000",
}
}
function releaseField(issue){
let release = issue.releases.find( r => r.match(/DRP-\d/) );
if(release){
return {value: release}
} else {
return {value: "tbd", textColor: "#ff0000"}
}
}
const RATING_COLOR = "#b5651d", COMMERCIAL_COLOR = "#ADD8E6";
const colorMapping = {
COMMRCL: COMMERCIAL_COLOR,
SALESENBMT: COMMERCIAL_COLOR,
ONBENGMT: COMMERCIAL_COLOR,
UCIF: COMMERCIAL_COLOR,
FENX: COMMERCIAL_COLOR,
BP: COMMERCIAL_COLOR,
DIGRATPFP: "#D3D3D3",
FINANCE: "#90EE90",
RATINGPFL: RATING_COLOR,
RADPROD: RATING_COLOR,
RODS: RATING_COLOR,
RADRDSSSQT: RATING_COLOR,
ARIEFIG: RATING_COLOR,
}
function teamField(issue){
return {
value: issue.team,
fillColor: colorMapping[issue.team],
textColor: "#000000",
}
}
function frameColor(issue){
if(issue.parent.key === "DIGRATPFP-1") {
return {fillColor: "#ffffff"}
} else if(issue.parent.key === "DIGRATPFP-2"){
return {fillColor: "#FFFFC5"}
} else {
return {};
}
}
async function createCards(){
const lastDate = getLastDate( data );
// go through and move each card
let m = 0;
for( let milestone of data ){
let frame = await miro.board.createFrame({
style: {
...frameColor(milestone),
},
x: (TIME_SPACE+500) / 2 - 400, // Default value: horizontal center of the board
y: m * ROW_HEIGHT, // Default value: vertical center of the board
width: TIME_SPACE+500,
height: ROW_HEIGHT,
});
let milestoneCard = await miro.board.createAppCard({
title: `<a href="${milestone.url}">${milestone.summary}</a>`,
style: {
cardTheme: colorMapping[milestone.team], fillBackground: true
},
fields: [
statusField(milestone),
teamField(milestone)
],
x: -100, // Default value: horizontal center of the board
y: m * ROW_HEIGHT-15,
width: 190
});
frame.add( milestoneCard );
for( let initiative of milestone.children ){
if( !initiative.due || ( new Date(initiative.due) > new Date() ) ) {
let initiativeCard = await miro.board.createAppCard({
title: `<a href="${initiative.url}">${initiative.summary}</a>`,
style: {
cardTheme: colorMapping[initiative.team], fillBackground: true
},
fields: [
statusField(initiative),
teamField(initiative),
releaseField(initiative)
],
x: cardXPosition(initiative, lastDate), // Default value: horizontal center of the board
y: m * ROW_HEIGHT,
width: 150
});
frame.add( initiativeCard );
}
}
m++;
}
}
createCards();
const dates = [
{name: "now", date: new Date()},
{name: "Q3.T2", date: new Date(2024, 8, 30)},
{name: "Q4.T1", date: new Date(2024, 10, 15)},
{name: "Q4.T2", date: new Date(2024, 11, 31)},
{name: "Q1.T1", date: new Date(2025, 1, 15)},
{name: "Q1.T2", date: new Date(2025, 2, 31)},
{name: "Q2.T1", date: new Date(2025, 4, 15)},
{name: "Q2.T2", date: new Date(2025, 5, 30)},
]
for( let date of dates ){
let xPos = ( TIME_SPACE * (date.date - new Date()) / (lastDate - new Date()) );
await miro.board.createStickyNote({
content: `<p>${date.name}</p>`,
style: {
fillColor: 'light_yellow', // Default value: light yellow
textAlign: 'center', // Default alignment: center
textAlignVertical: 'middle', // Default alignment: middle
},
x: xPos, // Default value: horizontal center of the board
y: -75, // Default value: vertical center of the board
shape: 'rectangle',
width: 100, // Set either 'width', or 'height'
});
await miro.board.createShape({
shape: 'rectangle',
style: {
fillColor: '#000000', // Default shape fill color: transparent (no fill)
},
x: xPos+75, // Default value: horizontal center of the board
y: data.length * ROW_HEIGHT / 2 - 50, // Default value: vertical center of the board
shape: 'rectangle',
width: 8,
height: data.length * ROW_HEIGHT
});
}
})(DATA)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment