From 2ebc72d97686574512e98e542ed4df683c9e3c00 Mon Sep 17 00:00:00 2001 From: Francois Gerthoffert Date: Sat, 21 Sep 2019 15:22:29 -0400 Subject: [PATCH 1/6] Fixed issues when authentication is disabled --- ui/src/components/Header/index.tsx | 2 +- ui/src/layout/index.tsx | 2 +- ui/src/models/global.ts | 16 +++++++++------- ui/src/models/roadmap.ts | 11 ++++++++--- ui/src/models/velocity.ts | 7 ++++--- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/ui/src/components/Header/index.tsx b/ui/src/components/Header/index.tsx index d3b2b3c..72c5cf1 100644 --- a/ui/src/components/Header/index.tsx +++ b/ui/src/components/Header/index.tsx @@ -85,7 +85,7 @@ const Header: FC = ({ setShowMenu, showMenu, pageTitle }) => { {pageTitle} - {window._env_.AUTH0_DISABLED !== true && } + {JSON.parse(window._env_.AUTH0_DISABLED) !== true && } ); diff --git a/ui/src/layout/index.tsx b/ui/src/layout/index.tsx index 2173035..689e9c3 100644 --- a/ui/src/layout/index.tsx +++ b/ui/src/layout/index.tsx @@ -50,7 +50,7 @@ export default function Layout(props: LayoutProps) {
- {window._env_.AUTH0_DISABLED !== true && } + {JSON.parse(window._env_.AUTH0_DISABLED) !== true && }
Date: Sat, 21 Sep 2019 21:24:05 -0400 Subject: [PATCH 2/6] Improved weekly velocity chart --- .../Charts/ChartJS/VelocityChart.tsx | 9 +- .../Charts/ChartJS/VelocityChartStacked.tsx | 199 ++++++++++++++++++ .../views/velocity/dashboard/WeeklyChart.tsx | 52 ++++- 3 files changed, 254 insertions(+), 6 deletions(-) create mode 100644 ui/src/components/Charts/ChartJS/VelocityChartStacked.tsx diff --git a/ui/src/components/Charts/ChartJS/VelocityChart.tsx b/ui/src/components/Charts/ChartJS/VelocityChart.tsx index 108b69c..a6a4e54 100644 --- a/ui/src/components/Charts/ChartJS/VelocityChart.tsx +++ b/ui/src/components/Charts/ChartJS/VelocityChart.tsx @@ -37,7 +37,6 @@ class VelocityChart extends Component { if (this.chart.destroy !== undefined) { this.chart.destroy(); } - this.chart = new Chart(myChartRef, { type: 'bar', data: { @@ -86,11 +85,13 @@ class VelocityChart extends Component { if (activePoints[0] !== undefined) { const idx = activePoints[0]._index; const issues = dataset[idx].completion.list; + console.log(issues); if (issues.length > 0 && this.allowClick === true) { this.allowClick = false; - const keys = issues.map((i: any) => i.key); - const url = - issues[0].host + '/issues/?jql=key in (' + keys.toString() + ')'; + // const keys = issues.map((i: any) => i.key); + //const url = + // issues[0].host + '/issues/?jql=key in (' + keys.toString() + ')'; + const url = issues[0].host + '/issues/?jql=' + issues[0].jql; window.open(url, '_blank'); setTimeout(() => { this.resetAllowClick(); diff --git a/ui/src/components/Charts/ChartJS/VelocityChartStacked.tsx b/ui/src/components/Charts/ChartJS/VelocityChartStacked.tsx new file mode 100644 index 0000000..ff9faa4 --- /dev/null +++ b/ui/src/components/Charts/ChartJS/VelocityChartStacked.tsx @@ -0,0 +1,199 @@ +import React, { Component } from 'react'; // let's also import Component +import { Theme, createStyles, withStyles } from '@material-ui/core/styles'; +import Chart from 'chart.js'; +import toMaterialStyle from 'material-color-hash'; + +const styles = (theme: Theme) => + createStyles({ + root: { + // height: 400 + } + }); + +class VelocityChartStacked extends Component { + chartRef: any = React.createRef(); + chart: any = {}; + allowClick: boolean = true; + + componentDidMount() { + this.buildChart(); + } + + componentDidUpdate() { + this.buildChart(); + } + + resetAllowClick = () => { + this.allowClick = true; + }; + + buildChart = () => { + const { dataset, defaultPoints } = this.props; + const myChartRef = this.chartRef.current.getContext('2d'); + let metric = 'points'; + if (!defaultPoints) { + metric = 'issues'; + } + + if (this.chart.destroy !== undefined) { + this.chart.destroy(); + } + + const days = [ + 'Sunday', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday' + ]; + const weekDays = []; + // @ts-ignore + for (const [idx, day] of days.entries()) { + weekDays.push({ + label: day, + // @ts-ignore + ...toMaterialStyle('t', (idx + 1) * 100), + // backgroundColor: '#64b5f6', + // borderColor: '#64b5f6', + borderWidth: 2, + data: dataset.map( + (w: any) => + w.weekDays.find((d: any) => d.weekdayTxt === day).completion[metric] + .count + ) + }); + } + this.chart = new Chart(myChartRef, { + type: 'bar', + data: { + datasets: [ + { + label: 'Velocity (rolling average)', + data: dataset.map((w: any) => w.completion[metric].velocity), + backgroundColor: '#ef5350', + fill: false, + type: 'line' + }, + ...weekDays + ], + labels: dataset.map((w: any) => w.legend) + }, + options: { + onClick: this.clickChart, + scales: { + yAxes: [ + { + ticks: { + beginAtZero: true + }, + stacked: true + } + ], + xAxes: [ + { + stacked: true + } + ] + }, + tooltips: { + position: 'nearest', + mode: 'index', + intersect: false, + callbacks: { + label: function(tooltipItem: any, data: any) { + if ( + tooltipItem.datasetIndex === 0 || + parseInt(tooltipItem.value, 10) === 0 + ) { + return ( + data.datasets[tooltipItem.datasetIndex].label + + ': ' + + tooltipItem.value + ); + } + const currentWeek = dataset.find( + (w: any) => w.weekTxt === tooltipItem.xLabel + ); + const currentDay = + currentWeek.weekDays[tooltipItem.datasetIndex - 1]; + return ( + currentDay.weekdayTxt + + ' (' + + currentDay.date.toJSON().slice(6, 10) + + '): ' + + tooltipItem.value + ); + }, + title: function(tooltipItems: any, data: any) { + // console.log(tooltipItems); + // console.log(data); + const currentWeek = dataset.find( + (w: any) => w.weekTxt === tooltipItems[0].xLabel + ); + return ( + currentWeek.weekTxt + ' week starting: ' + currentWeek.weekJira + ); + } + } + } + } + }); + }; + + // https://jsfiddle.net/u1szh96g/208/ + clickChart = (event: any) => { + const { dataset } = this.props; + const activePoints = this.chart.getElementsAtEvent(event); + if (activePoints[0] !== undefined) { + const idx = activePoints[0]._index; + const issues = dataset[idx].completion.list; + if (issues.length > 0 && this.allowClick === true) { + this.allowClick = false; + const clickedWeek = dataset[idx]; + let jqlString = ''; + const activeWeeks = clickedWeek.weekDays.filter( + (d: any) => d.jql !== null + ); + for (const [idx, day] of activeWeeks.entries()) { + jqlString = jqlString + ' (' + day.jql + ')'; + if (idx < activeWeeks.length - 1) { + jqlString = jqlString + ' OR'; + } + } + const url = issues[0].host + '/issues/?jql=' + jqlString; + window.open(url, '_blank'); + setTimeout(() => { + this.resetAllowClick(); + }, 1000); + } + // const issues = dataset[idx].completion.list; + // console.log(issues); + /* + if (issues.length > 0 && this.allowClick === true) { + this.allowClick = false; + const keys = issues.map((i: any) => i.key); + //const url = + // issues[0].host + '/issues/?jql=key in (' + keys.toString() + ')'; + const url = issues[0].host + '/issues/?jql=' + issues[0].jql; + window.open(url, '_blank'); + setTimeout(() => { + this.resetAllowClick(); + }, 1000); + } + */ + } + }; + + render() { + const { classes } = this.props; + return ( +
+ +
+ ); + } +} + +export default withStyles(styles)(VelocityChartStacked); diff --git a/ui/src/views/velocity/dashboard/WeeklyChart.tsx b/ui/src/views/velocity/dashboard/WeeklyChart.tsx index 0ba2bcd..f047931 100644 --- a/ui/src/views/velocity/dashboard/WeeklyChart.tsx +++ b/ui/src/views/velocity/dashboard/WeeklyChart.tsx @@ -1,5 +1,6 @@ import React from 'react'; -import VelocityChart from '../../../components/Charts/ChartJS/VelocityChart'; +import parseISO from 'date-fns/parseISO'; +import VelocityChartStacked from '../../../components/Charts/ChartJS/VelocityChartStacked'; export interface WeeklyChartsProps { velocity: any; @@ -23,5 +24,52 @@ export default function WeeklyChart(props: WeeklyChartsProps) { legend: week.weekTxt }; }); - return ; + + const days = [ + 'Sunday', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday' + ]; + const datasetWithDays = dataset.map((week: any) => { + const weekDays = []; + // @ts-ignore + for (const [idx, day] of days.entries()) { + const issuesList = week.completion.list.filter((issue: any) => { + // console.log(issue); + const issueDate = parseISO(issue.closedAt); + if (issueDate.getDay() === idx) { + return true; + } + return false; + }); + weekDays.push({ + weekdayTxt: day, + list: issuesList, + date: issuesList.length === 0 ? null : parseISO(issuesList[0].closedAt), + jql: issuesList.length === 0 ? null : issuesList[0].jql, + completion: { + issues: { + count: issuesList.length + }, + points: { + count: issuesList + .map((i: any) => i.points) + .reduce((acc: number, count: number) => acc + count, 0) + } + } + }); + } + return { ...week, weekDays }; + }); + + return ( + + ); } From f63e67b6b56cacff5935a01c7e62724d53517403 Mon Sep 17 00:00:00 2001 From: Francois Gerthoffert Date: Sun, 22 Sep 2019 15:22:57 -0400 Subject: [PATCH 3/6] Some updates related to team processing --- api/src/roadmap/roadmap.service.ts | 1 + api/src/velocity/velocity.service.ts | 1 + cli/src/utils/misc/teamUtils.ts | 1 + ui/package.json | 2 ++ ui/tsconfig.json | 13 +++++++++++-- 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/api/src/roadmap/roadmap.service.ts b/api/src/roadmap/roadmap.service.ts index ec9df86..a1bf64a 100644 --- a/api/src/roadmap/roadmap.service.ts +++ b/api/src/roadmap/roadmap.service.ts @@ -43,6 +43,7 @@ export class RoadmapService { export const getTeamId = (teamName: string) => { return String(teamName) + .replace('team-', '') // If team is prefixed by team-, we simply remove it from the string .replace(/[^a-z0-9+]+/gi, '') .toLowerCase(); }; diff --git a/api/src/velocity/velocity.service.ts b/api/src/velocity/velocity.service.ts index 06acb07..e2f2809 100644 --- a/api/src/velocity/velocity.service.ts +++ b/api/src/velocity/velocity.service.ts @@ -46,6 +46,7 @@ export class VelocityService { export const getTeamId = (teamName: string) => { return String(teamName) + .replace('team-', '') // If team is prefixed by team-, we simply remove it from the string .replace(/[^a-z0-9+]+/gi, '') .toLowerCase(); }; diff --git a/cli/src/utils/misc/teamUtils.ts b/cli/src/utils/misc/teamUtils.ts index ab8c571..980f440 100644 --- a/cli/src/utils/misc/teamUtils.ts +++ b/cli/src/utils/misc/teamUtils.ts @@ -3,6 +3,7 @@ */ export const getTeamId = (teamName: string) => { return String(teamName) + .replace('team-', '') // If team is prefixed by team-, we simply remove it from the string .replace(/[^a-z0-9+]+/gi, '') .toLowerCase(); }; diff --git a/ui/package.json b/ui/package.json index da4f5f3..c8132ad 100644 --- a/ui/package.json +++ b/ui/package.json @@ -24,8 +24,10 @@ "cytoscape": "^3.10.0", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-popper": "^1.0.4", + "date-fns": "^2.2.1", "install": "^0.13.0", "loglevel": "^1.6.3", + "material-color-hash": "^0.1.6", "material-table": "^1.50.0", "npm": "^6.11.3", "react": "^16.9.0", diff --git a/ui/tsconfig.json b/ui/tsconfig.json index 71e63c9..309ff6d 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -1,7 +1,14 @@ { "compilerOptions": { "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "es5", + "es2015.collection", + "es2015.iterable", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -13,7 +20,9 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react" + "jsx": "react", + "downlevelIteration": true, + "importHelpers": true }, "include": ["src"], "rules": { From aa7c4a1c0357072cc8fe804e9d9f34352baefc1f Mon Sep 17 00:00:00 2001 From: Francois Gerthoffert Date: Mon, 23 Sep 2019 04:56:05 -0400 Subject: [PATCH 4/6] Updated initiative short title --- ui/src/components/Charts/Nivo/RoadmapFutureChart.tsx | 4 +++- ui/src/components/Charts/Nivo/utils.ts | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ui/src/components/Charts/Nivo/RoadmapFutureChart.tsx b/ui/src/components/Charts/Nivo/RoadmapFutureChart.tsx index 0d223e8..8daf210 100644 --- a/ui/src/components/Charts/Nivo/RoadmapFutureChart.tsx +++ b/ui/src/components/Charts/Nivo/RoadmapFutureChart.tsx @@ -2,6 +2,8 @@ import React, { Component } from 'react'; // let's also import Component import { Theme, createStyles, withStyles } from '@material-ui/core/styles'; import { ResponsiveHeatMap } from '@nivo/heatmap'; +import { getInitiativeTitle } from './utils'; + const styles = (theme: Theme) => createStyles({ root: { @@ -43,7 +45,7 @@ class RoadmapFutureChart extends Component { const dataset: IDatasetObj[] = []; for (const initiative of roadmap.byFutureInitiative) { const initiativeData: IDatasetObj = { - initiative: initiative.fields.summary + ' (' + initiative.key + ')' + initiative: getInitiativeTitle(initiative) }; for (const week of initiative.weeks) { initiativeData[week.weekTxt] = week[metric].count; diff --git a/ui/src/components/Charts/Nivo/utils.ts b/ui/src/components/Charts/Nivo/utils.ts index e453ff1..e5aa099 100644 --- a/ui/src/components/Charts/Nivo/utils.ts +++ b/ui/src/components/Charts/Nivo/utils.ts @@ -19,7 +19,12 @@ export const getCellDataInitiatives = ( }; export const getInitiativeTitle = (initiative: any) => { - return initiative.fields.summary + ' (' + initiative.key + ')'; + const maxTitleLength = 30; + const initiativeTitle = + initiative.fields.summary.length > maxTitleLength + ? initiative.fields.summary.slice(0, maxTitleLength) + '...' + : initiative.fields.summary; + return initiativeTitle + ' (' + initiative.key + ')'; }; export const getNonInitiativeTitle = () => { From 263584d5f788509c7fac5fd0ecbcf0374481e4e6 Mon Sep 17 00:00:00 2001 From: Francois Gerthoffert Date: Mon, 23 Sep 2019 07:26:34 -0400 Subject: [PATCH 5/6] added colors to roadmap based on team name --- ui/src/components/Charts/Nivo/RoadmapFutureChart.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ui/src/components/Charts/Nivo/RoadmapFutureChart.tsx b/ui/src/components/Charts/Nivo/RoadmapFutureChart.tsx index 8daf210..6e2f301 100644 --- a/ui/src/components/Charts/Nivo/RoadmapFutureChart.tsx +++ b/ui/src/components/Charts/Nivo/RoadmapFutureChart.tsx @@ -1,6 +1,7 @@ import React, { Component } from 'react'; // let's also import Component import { Theme, createStyles, withStyles } from '@material-ui/core/styles'; import { ResponsiveHeatMap } from '@nivo/heatmap'; +import toMaterialStyle from 'material-color-hash'; import { getInitiativeTitle } from './utils'; @@ -32,6 +33,13 @@ class RoadmapFutureChart extends Component { Display different background colors based on the percentage of the effort spent on a particular activity for a week */ getCompletionColor = (data: any, value: any) => { + const { roadmap } = this.props; + const initiative = roadmap.byFutureInitiative.find( + (i: any) => getInitiativeTitle(i) === data.yKey + ); + if (initiative !== undefined) { + return toMaterialStyle(initiative.team.name, 200).backgroundColor; + } return 'rgb(65, 171, 93)'; }; From 4b1f5d2f68935cc2aa8eacf2883763228e83e2ab Mon Sep 17 00:00:00 2001 From: Francois Gerthoffert Date: Mon, 23 Sep 2019 07:36:56 -0400 Subject: [PATCH 6/6] Fixed some linting issues --- ui/src/components/Charts/ChartJS/VelocityChart.tsx | 3 +-- ui/src/components/Charts/ChartJS/VelocityChartStacked.tsx | 8 ++++---- ui/src/models/global.ts | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ui/src/components/Charts/ChartJS/VelocityChart.tsx b/ui/src/components/Charts/ChartJS/VelocityChart.tsx index a6a4e54..bf8b60a 100644 --- a/ui/src/components/Charts/ChartJS/VelocityChart.tsx +++ b/ui/src/components/Charts/ChartJS/VelocityChart.tsx @@ -85,11 +85,10 @@ class VelocityChart extends Component { if (activePoints[0] !== undefined) { const idx = activePoints[0]._index; const issues = dataset[idx].completion.list; - console.log(issues); if (issues.length > 0 && this.allowClick === true) { this.allowClick = false; // const keys = issues.map((i: any) => i.key); - //const url = + // const url = // issues[0].host + '/issues/?jql=key in (' + keys.toString() + ')'; const url = issues[0].host + '/issues/?jql=' + issues[0].jql; window.open(url, '_blank'); diff --git a/ui/src/components/Charts/ChartJS/VelocityChartStacked.tsx b/ui/src/components/Charts/ChartJS/VelocityChartStacked.tsx index ff9faa4..b81c4a0 100644 --- a/ui/src/components/Charts/ChartJS/VelocityChartStacked.tsx +++ b/ui/src/components/Charts/ChartJS/VelocityChartStacked.tsx @@ -102,7 +102,7 @@ class VelocityChartStacked extends Component { mode: 'index', intersect: false, callbacks: { - label: function(tooltipItem: any, data: any) { + label: (tooltipItem: any, data: any) => { if ( tooltipItem.datasetIndex === 0 || parseInt(tooltipItem.value, 10) === 0 @@ -126,7 +126,7 @@ class VelocityChartStacked extends Component { tooltipItem.value ); }, - title: function(tooltipItems: any, data: any) { + title: (tooltipItems: any, data: any) => { // console.log(tooltipItems); // console.log(data); const currentWeek = dataset.find( @@ -156,9 +156,9 @@ class VelocityChartStacked extends Component { const activeWeeks = clickedWeek.weekDays.filter( (d: any) => d.jql !== null ); - for (const [idx, day] of activeWeeks.entries()) { + for (const [widx, day] of activeWeeks.entries()) { jqlString = jqlString + ' (' + day.jql + ')'; - if (idx < activeWeeks.length - 1) { + if (widx < activeWeeks.length - 1) { jqlString = jqlString + ' OR'; } } diff --git a/ui/src/models/global.ts b/ui/src/models/global.ts index 5b8dbe1..125be69 100644 --- a/ui/src/models/global.ts +++ b/ui/src/models/global.ts @@ -115,7 +115,6 @@ export const global = createModel({ }, async initAuth() { - console.log(window._env_.AUTH0_DISABLED); if (JSON.parse(window._env_.AUTH0_DISABLED) !== true) { log.info('User not logged in, initializing authentication'); if (window.Auth0 !== undefined) {