diff --git a/app/directives/distribution-graph/distribution-graph.directive.js b/app/directives/distribution-graph/distribution-graph.directive.js
index fb5565efb..1d6197044 100644
--- a/app/directives/distribution-graph/distribution-graph.directive.js
+++ b/app/directives/distribution-graph/distribution-graph.directive.js
@@ -1,5 +1,8 @@
import angular from 'angular'
import d3 from 'd3'
+import React from 'react' // eslint-disable-line no-unused-vars
+import ReactDOM from 'react-dom'
+import Tooltip from 'appirio-tech-react-components/components/Tooltip/Tooltip.jsx'
(function() {
'use strict'
@@ -59,7 +62,7 @@ import d3 from 'd3'
]
var measurements = {
- w: 600,
+ w: 900,
h: 400,
padding: {
top: 20,
@@ -176,7 +179,24 @@ import d3 from 'd3'
.attr('fill', function(d) {
return ratingToColor($scope.colors, d.start)
})
-
+
+ var mousemoveInterval = null
+
+ /* render react tooltip component */
+ ReactDOM.unmountComponentAtNode(document.getElementById('chart-tooltip'))
+ ReactDOM.render(
+
+
+
+ , document.getElementById('chart-tooltip'))
+
+ $scope.isFocused = false
svg.selectAll('rect.hover')
.data(ranges)
.enter()
@@ -187,24 +207,81 @@ import d3 from 'd3'
return xScale(i)
})
.attr('y', function(d) {
- return padding.top
+ return yScale(d.number)
})
.attr('width', xScale.rangeBand())
.attr('height', function(d) {
- return totalH - padding.bottom - padding.top
+ return totalH - padding.bottom - yScale(d.number)
})
.on('mouseover', function(d) {
+ $scope.isFocused = true
$scope.highlightedRating = d.start
$scope.displayCoders = true
$scope.numCoders = d.number
+
+ /* update tooltip location on mouseover, feature currently not inbuilt in react tooltip component */
+ d3.select('#chart-tooltip')
+ .style('left', (d3.event.pageX-4) + 'px')
+ .style('top', (d3.event.pageY-4) + 'px')
+
+ $('#chart-tooltip').addClass('distribution')
+ d3.select('#chart-tooltip .tooltip-container')
+ .style('left', '20px !important')
+ .style('top', '-20px !important')
+
+ d3.select('#chart-tooltip .tooltip-container .tooltip-pointer')
+ .style('left', '-5.5px !important')
+ .style('bottom', '25px !important')
+
+ d3.select('#chart-tooltip .challenge-name').text($scope.numCoders + ' Coders')
+ d3.select('#chart-tooltip .challenge-date').text('Rating Range: '+ $scope.highlightedRating + '-'+($scope.highlightedRating+99))
+ d3.select('#chart-tooltip .tooltip-rating').text($scope.numCoders)
+ d3.select('#chart-tooltip .tooltip-rating').style('background', ratingToColor($scope.colors, $scope.highlightedRating))
$scope.$digest()
})
+ .on('mousemove', function(d) {
+
+ /* update tooltip on mousemove, using interval of 50ms to improve performance */
+ window.clearTimeout(mousemoveInterval)
+ var left = (d3.event.pageX-4)
+ var top = (d3.event.pageY-4)
+
+ mousemoveInterval = window.setTimeout(function(){
+ d3.select('#chart-tooltip')
+ .style('left', left + 'px')
+ .style('top', top + 'px')
+
+ d3.select('#chart-tooltip .tooltip-container')
+ .style('left', '20px !important')
+ .style('top', '-20px !important')
+
+ d3.select('#chart-tooltip .tooltip-container .tooltip-pointer')
+ .style('left', '-5.5px !important')
+ .style('bottom', '25px !important')
+ }, 50)
+
+ })
.on('mouseout', function(d) {
$scope.displayCoders = false
$scope.highlightedRating = false
+ $scope.isFocused = false
$scope.$digest()
})
-
+
+ /* hide tooltip when clicked anywhere outside */
+ d3.select('body').on('click', function(){
+ if((d3.event.target.classList[0] != 'tooltip-target') && !$('#chart-tooltip .tooltip-container').hasClass('tooltip-hide') &&
+ !isInArray(d3.event.target.classList[0], ['tooltip-content-container', 'tooltip-container', 'tooltip-body', 'Tooltip']) &&
+ (d3.event.target.tagName.toLowerCase()!='circle') && !(d3.event.target.tagName.toLowerCase()=='rect' && d3.event.target.classList[0] == 'hover')) {
+ $('#chart-tooltip .tooltip-target').trigger('click')
+ $('#chart-tooltip').removeClass('distribution')
+ }
+ })
+
+ function isInArray(value, array) {
+ return array.indexOf(value) > -1
+ }
+
svg.selectAll('line.xaxis')
.data(ranges)
.enter()
diff --git a/app/directives/distribution-graph/distribution-graph.jade b/app/directives/distribution-graph/distribution-graph.jade
index 6faf6b4fd..f146363af 100644
--- a/app/directives/distribution-graph/distribution-graph.jade
+++ b/app/directives/distribution-graph/distribution-graph.jade
@@ -1,15 +1,11 @@
.distribution-graph-directive(ng-show="graphState.show == 'distribution'")
- .graph-viewer
- .distribution-graph
+ .graph-title
+ .text Rating Distribution Graph
+ .button-group
+ button.tc-btn.tc-btn-s(ng-click="graphState.show = 'history'") View Rating History
+ button.tc-btn.tc-btn-s.active(ng-click="graphState.show = 'distribution'") View Rating Distribution
- .info-port
- .coders(ng-if="displayCoders", style="background: {{highlightedRating || rating | ratingColor}}")
- .num {{numCoders}}
- .label CODERS
- .coders(ng-if="!displayCoders", style="background: {{rating | ratingColor}}")
- .num {{rating}}
- .label RATING
- button.tc-btn.tc-btn-s.compare(ng-click="graphState.show = 'history'")
- | View Rating History
+ .graph-viewer
+ .distribution-graph
\ No newline at end of file
diff --git a/app/directives/history-graph/history-graph.directive.js b/app/directives/history-graph/history-graph.directive.js
index f42120200..d634cc885 100644
--- a/app/directives/history-graph/history-graph.directive.js
+++ b/app/directives/history-graph/history-graph.directive.js
@@ -1,6 +1,9 @@
import angular from 'angular'
import moment from 'moment'
import d3 from 'd3'
+import React from 'react' // eslint-disable-line no-unused-vars
+import ReactDOM from 'react-dom'
+import Tooltip from 'appirio-tech-react-components/components/Tooltip/Tooltip.jsx'
(function() {
'use strict'
@@ -16,11 +19,11 @@ import d3 from 'd3'
rating: '=',
graphState: '='
},
- controller: ['$scope', HistoryGraphController]
+ controller: ['$scope', '$state', '$filter', 'CONSTANTS', HistoryGraphController]
}
}
- function HistoryGraphController($scope) {
+ function HistoryGraphController($scope, $state, $filter, CONSTANTS) {
$scope.colors = [
// grey
{
@@ -59,7 +62,7 @@ import d3 from 'd3'
}
]
var measurements = {
- w: 600,
+ w: 900,
h: 400,
padding: {
top: 20,
@@ -156,7 +159,6 @@ import d3 from 'd3'
.attr('width', w + padding.left + padding.right)
.attr('height', h + padding.top + padding.bottom)
-
svg.append('rect')
.attr('x', padding.left)
.attr('y', padding.top)
@@ -243,7 +245,21 @@ import d3 from 'd3'
return y
}
}
-
+
+ /* render react tooltip component */
+ ReactDOM.unmountComponentAtNode(document.getElementById('chart-tooltip'))
+ ReactDOM.render(
+
+
+
+ , document.getElementById('chart-tooltip'))
+
svg.selectAll('circle')
.data(history)
.enter()
@@ -261,20 +277,44 @@ import d3 from 'd3'
$scope.historyRating = d.newRating
$scope.historyDate = moment(d.ratingDate).format('YYYY-MM-DD')
$scope.historyChallenge = d.challengeName
+ $('#chart-tooltip .tooltip-container').on('click', function(){
+ if($state.params && ($state.params.subTrack === 'SRM' || $state.params.subTrack === 'MARATHON_MATCH'))
+ location.href = $filter('challengeLinks')({'rounds': [{id: d.challengeId, forumId: null}], 'track': $state.params.track, 'subTrack': $state.params.subTrack}, 'detail')
+ else
+ location.href = $filter('challengeLinks')({id: d.challengeId, 'track': $state.params.track, 'subTrack': $state.params.subTrack}, 'detail')
+ })
+
+ /* update tooltip location on mouseover, feature currently not inbuilt in react tooltip component */
+ d3.select('#chart-tooltip')
+ .style('left', (d3.event.pageX-5) + 'px')
+ .style('top', (d3.event.pageY-5) + 'px')
+ d3.select('#chart-tooltip .tooltip-container')
+ .style('left', '20px !important')
+ .style('top', '-20px !important')
+ d3.select('#chart-tooltip .tooltip-container .tooltip-pointer')
+ .style('left', '-5.5px !important')
+ .style('bottom', '25px !important')
+
+ d3.select('#chart-tooltip .challenge-name').text($scope.historyChallenge)
+ d3.select('#chart-tooltip .challenge-date').text(moment(d.ratingDate).format('MMM DD, YYYY'))
+ d3.select('#chart-tooltip .tooltip-rating').text($scope.historyRating)
+ d3.select('#chart-tooltip .tooltip-rating').style('background', ratingToColor($scope.colors, $scope.historyRating))
+ $('#chart-tooltip').removeClass('distribution')
$scope.$digest()
- d3.select(this)
- .attr('r', 6.0)
})
.on('mouseout', function(d) {
$scope.historyRating = undefined
$scope.$digest()
- d3.select(this)
- .attr('r', 4.5)
- .attr('stroke', 'none')
- .attr('stroke-width', '0px')
})
- .attr('r', 4.5)
+ /* hide tooltip when clicked anywhere outside */
+ d3.select('body').on('click', function(){
+ if((d3.event.target.classList[0] != 'tooltip-target') && !$('#chart-tooltip .tooltip-container').hasClass('tooltip-hide') &&
+ (d3.event.target.tagName.toLowerCase()!='circle') && !(d3.event.target.tagName.toLowerCase()=='rect' && d3.event.target.classList[0] == 'hover')) {
+ $('#chart-tooltip .tooltip-target').trigger('click')
+ $('#chart-tooltip .tooltip-container').off('click')
+ }
+ })
}
diff --git a/app/directives/history-graph/history-graph.jade b/app/directives/history-graph/history-graph.jade
index 4eef54967..6d9dd7d16 100644
--- a/app/directives/history-graph/history-graph.jade
+++ b/app/directives/history-graph/history-graph.jade
@@ -1,13 +1,11 @@
.history-graph-directive(ng-show="graphState.show == 'history'")
- .history-graph-container
- .history-graph
+ .graph-title
+ .text Rating History Graph
+ .button-group
+ button.tc-btn.tc-btn-s.active(ng-click="graphState.show = 'history'") View Rating History
+ button.tc-btn.tc-btn-s(ng-click="graphState.show = 'distribution'") View Rating Distribution
+
+ .history-graph-container
- .info-port
- .rating(style="background: {{historyRating || rating | ratingColor}}")
- .num {{historyRating || rating}}
- .label RATING
- .history-info
- .challenge(ng-if="historyRating") {{historyChallenge}}
- .date(ng-if="historyRating") {{historyDate | date}}
- button.tc-btn.tc-btn-s.compare(ng-click="graphState.show = 'distribution'") View Rating Distribution
+ .history-graph
\ No newline at end of file
diff --git a/app/filters/challengeLinks.filter.js b/app/filters/challengeLinks.filter.js
index 5f3922110..e6360ce0d 100644
--- a/app/filters/challengeLinks.filter.js
+++ b/app/filters/challengeLinks.filter.js
@@ -23,6 +23,15 @@ import angular from 'angular'
case 'detail':
return String.supplant('https://community.{domain}/longcontest/stats/?module=ViewOverview&rd={roundId}', data)
}
+ } else if (challenge.subTrack === 'SRM') {
+ data = {
+ domain: CONSTANTS.domain,
+ roundId: challenge.rounds[0].id
+ }
+ switch (type) {
+ case 'detail':
+ return String.supplant('https://community.{domain}/stat?c=round_overview&rd={roundId}', data)
+ }
} else {
data = {
domain: CONSTANTS.domain,
diff --git a/app/index.jade b/app/index.jade
index 5a5a0beeb..2424fcc00 100644
--- a/app/index.jade
+++ b/app/index.jade
@@ -36,3 +36,5 @@ html
.fold-pusher
div(ui-view="footer")
+
+ #chart-tooltip
\ No newline at end of file
diff --git a/app/index.js b/app/index.js
index e698b267b..89760c27c 100644
--- a/app/index.js
+++ b/app/index.js
@@ -25,6 +25,7 @@ require('xml2js')
require('appirio-tech-ng-ui-components')
require('appirio-tech-ng-iso-constants')
+require('appirio-tech-react-components')
// Vendor styles
require('../node_modules/angucomplete-alt/angucomplete-alt.css')
diff --git a/assets/css/directives/distribution-graph.scss b/assets/css/directives/distribution-graph.scss
index 9b0b83e31..cce226949 100644
--- a/assets/css/directives/distribution-graph.scss
+++ b/assets/css/directives/distribution-graph.scss
@@ -12,6 +12,51 @@
}
+.graph-title {
+ width: 960px;
+ padding-left: 45px;
+ text-align: left;
+ margin: auto;
+ font-size: 28px;
+ line-height: 35px;
+ color: #47474F;
+
+ .text {
+ float: left;
+ }
+
+ .button-group {
+ float: right;
+ display: block;
+ button.tc-btn.tc-btn-s {
+ background: #fff;
+ color: #47474f;
+ border: 1px solid #c3c3c8;
+ height: 30px;
+ box-sizing: border-box;
+ &.active {
+ background: #dcdce0;
+ -webkit-box-shadow: inset 0px 1px 3px 0px rgba(0,0,0,1);
+ -moz-box-shadow: inset 0px 1px 3px 0px rgba(0,0,0,1);
+ box-shadow: inset 0px 1px 3px 0px rgba(0,0,0,1);
+ border: none;
+ }
+ &:first-child {
+ border-top-left-radius: 2px;
+ border-bottom-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+ &:last-child {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ }
+ }
+ }
+}
+
.graph-viewer {
margin-top: 18px;
display: flex;
diff --git a/assets/css/directives/history-graph.scss b/assets/css/directives/history-graph.scss
index 38c4692ae..3726d3614 100644
--- a/assets/css/directives/history-graph.scss
+++ b/assets/css/directives/history-graph.scss
@@ -4,6 +4,7 @@
display: flex;
flex-direction: column;
align-items: center;
+ margin-top: 20px;
.compare {
margin-top: 5px;
margin-bottom: 15px;
@@ -13,6 +14,35 @@
display: flex;
flex-direction: column;
align-items: center;
+ .graph-title {
+ width: 960px;
+ padding-left: 45px;
+ text-align: left;
+ margin: auto;
+ font-size: 28px;
+ line-height: 35px;
+ color: #47474F;
+
+ .text {
+ float: left;
+ }
+
+ .button-group {
+ float: right;
+ display: block;
+ button.tc-btn.tc-btn-s {
+ background: #fff;
+ color: #47474f;
+ &.active {
+ background: #dcdce0;
+ -webkit-box-shadow: 0px 1px 3px 0px rgba(0,0,0,1);
+ -moz-box-shadow: 0px 1px 3px 0px rgba(0,0,0,1);
+ box-shadow: 0px 1px 3px 0px rgba(0,0,0,1);
+ }
+ }
+ }
+ }
+
.history-graph {
display: flex;
diff --git a/assets/css/my-dashboard/subtrack-stats.scss b/assets/css/my-dashboard/subtrack-stats.scss
index c0153a0cf..38ba99048 100644
--- a/assets/css/my-dashboard/subtrack-stats.scss
+++ b/assets/css/my-dashboard/subtrack-stats.scss
@@ -106,3 +106,81 @@
}
}
}
+
+#chart-tooltip {
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ position: absolute;
+ z-index: 1000;
+ cursor: pointer;
+}
+
+#chart-tooltip.distribution {
+ width: 4px;
+ height: 4px;
+ cursor: auto;
+}
+
+.tooltip-target {
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ display: block;
+ top: -4px;
+ cursor: pointer;
+ left: -4px;
+}
+
+#chart-tooltip.distribution .tooltip-target {
+ top: -4px;
+ left: -4px;
+ width: 8px;
+ height: 8px;
+ border-radius: 0;
+}
+
+.Tooltip .tooltip-container {
+ position: relative;
+ opacity: 1;
+ top: 10px !important;
+ left: -150px !important;
+ width: 320px;
+ height: 115px;
+}
+
+.Tooltip .tooltip-container .tooltip-pointer {
+ left: 150px !important;
+ top: -5.5px !important;
+ bottom: auto !important;
+}
+
+
+
+#chart-tooltip .tooltip-rating {
+ width: 60px;
+ height: 60px;
+ margin-right: 15px;
+ border-radius: 50%;
+ float: left;
+ text-align: center;
+ padding-top: 23px;
+}
+
+#chart-tooltip .tooltip-challenge {
+ height: 100%;
+ width: calc(100% - 90px);
+ float: left;
+}
+
+#chart-tooltip .tooltip-challenge .challenge-name {
+ white-space: normal;
+ line-height: 20px;
+ word-wrap: break-word;
+}
+
+#chart-tooltip .tooltip-challenge .challenge-date {
+ font-weight: normal;
+ margin-top: 13px;
+ font-size: 12px;
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 469c5a3bb..70fe41f59 100644
--- a/package.json
+++ b/package.json
@@ -55,6 +55,7 @@
"appirio-styles": "0.x.x",
"appirio-tech-ng-iso-constants": "^1.0.6",
"appirio-tech-ng-ui-components": "^2.1.2",
+ "appirio-tech-react-components": "^0.0.12",
"auth0-angular": "^4.1.0",
"auth0-js": "^6.8.0",
"d3": "^3.5.14",