Last active
September 12, 2024 20:24
-
-
Save justinbmeyer/3a8c6ce07531d150e5f99aec6c0f7097 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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