Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 574015e

Browse files
committed
Merge branch 'develop' of https://github.com/topcoder-platform/leaderboard-ui into develop
2 parents b3c10d3 + 2e85cdb commit 574015e

24 files changed

+481
-130
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ CONTENTFUL_MESSAGE_ENTRY_ID // Entry id for the content relevant for the message
2020
CONTENTFUL_COUNTDOWN_ENTRY_ID // Entry id for the content relevant for the countdown timer page
2121
HOST // URL address where the app is hosted, without any trailing slash "/". Example: http://localhost:3000
2222
NODE_ENV // self explanatory
23+
ADMIN_USERNAME // username of admin
24+
ADMIN_PASSWORD // password of admin
2325
```
2426

2527
## Deployment

common/helper.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ const hexToName = (hexColor) => {
3030
* @param {Array} finalists The list of known finalists
3131
* @param {String} groupId The group Id
3232
* @param {Array} groupChallengeIds The challenges in the group
33+
* @param {Boolean} isF2f Is the leaderboard for F2F track? If yes, multiplier is applied
3334
*/
34-
async function prepareLeaderboard (challengeId, finalists, groupId, groupChallengeIds) {
35+
async function prepareLeaderboard (challengeId, finalists, groupId, groupChallengeIds, isF2f) {
3536
const { publicRuntimeConfig } = getConfig()
37+
const multiplier = [2, 3, 5]
3638

3739
let res
3840
let leaderboard
@@ -50,6 +52,7 @@ async function prepareLeaderboard (challengeId, finalists, groupId, groupChallen
5052
})
5153

5254
if (member) {
55+
let aggregateScore = 0
5356
// Next determine the member score details
5457
// Since it is a group, for each member we will have a maximum of n reviews and 1 final score
5558
// where n is the number of contests per group
@@ -73,11 +76,20 @@ async function prepareLeaderboard (challengeId, finalists, groupId, groupChallen
7376
if (review) {
7477
// Member has a review for that challenge
7578
if (review.status !== 'queued') {
79+
let score
80+
81+
if (isF2f) {
82+
score = Math.round(review.aggregateScore * multiplier[i] * 10000) / 10000
83+
} else {
84+
score = Math.round(review.aggregateScore * 10000) / 10000
85+
}
7686
member.reviews.push({
77-
score: review.aggregateScore,
87+
score,
7888
testsPassed: review.testsPassed,
7989
totalTestCases: review.totalTestCases
8090
})
91+
92+
aggregateScore = aggregateScore + (review.aggregateScore * multiplier[i])
8193
} else {
8294
// If a review is in queue, do not show any points or tests. Show the status
8395
member.reviews.push({ status: 'review queued' })
@@ -89,6 +101,11 @@ async function prepareLeaderboard (challengeId, finalists, groupId, groupChallen
89101
member.reviews.push({})
90102
}
91103
}
104+
105+
if (isF2f) {
106+
// multiplier points get applied for f2f track
107+
member.points = Math.round(aggregateScore * 10000) / 10000
108+
}
92109
} else {
93110
// Member exists in leaderboard api but not in contentful
94111
member = {}

components/FinalistTable.js

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types'
22
import React from 'react'
33

44
const table = (props) => {
5-
const { finalists, primaryColor, smallerDesign, largeColumns, track, isF2f, isMini } = props
5+
const { finalists, primaryColor, smallerDesign, largeColumns, track, fullWidth, isMini, isDev, isQa } = props
66
const smallClass = smallerDesign || isMini ? ' small ' : ''
77
const sizeClass = largeColumns ? ' largerCells ' : ''
88
const trackTableClass = (track) => {
@@ -12,7 +12,7 @@ const table = (props) => {
1212
}
1313
}
1414
const algorithmLeaderboard = track === 'algorithm'
15-
const f2fLeaderboard = isF2f
15+
const f2fLeaderboard = fullWidth
1616
return (
1717
<div className={'container' + smallClass + sizeClass + `${trackTableClass(track)}`}>
1818
<div className='header'>
@@ -51,7 +51,7 @@ const table = (props) => {
5151
<div style={{ display: 'flex', flexGrow: '1', justifyContent: 'space-between' }}>
5252
<div className='competitor'>competitor</div>
5353
<div className='points'>points</div>
54-
<div className='tests-passed'>tests passed</div>
54+
{!isQa && <div className='tests-passed'>{ isDev ? '% Complete' : 'tests passed'}</div>}
5555
</div>
5656
}
5757
</div>
@@ -75,7 +75,7 @@ const table = (props) => {
7575

7676
<div className='points'>
7777
{ profile.scoreLevel && <img className={`animate fade${profile.scoreLevel} infinite`} src={`/static/img/trend/${profile.scoreLevel}.png`} /> }
78-
{ profile.points != null && <div className={profile.scoreLevel ? '' : 'non-score-lvl-pt'}>
78+
{ profile.points >= 0 && <div className={profile.scoreLevel ? '' : 'non-score-lvl-pt'}>
7979
<span className='value'>
8080
{profile.points}
8181
</span>
@@ -86,14 +86,30 @@ const table = (props) => {
8686
</div>
8787

8888
{
89-
profile.testsPassed && <div className='tests-passed'>
89+
!isQa && profile.totalTestCases > 0 && <div className='tests-passed'>
9090
<div>
91-
<span className='value'>
92-
{`${profile.testsPassed} / ${profile.totalTestCases}`}
93-
</span>
94-
<span className='hint'>
95-
TESTS
96-
</span>
91+
{
92+
!isDev &&
93+
<React.Fragment>
94+
<span className='value'>
95+
{`${profile.testsPassed} / ${profile.totalTestCases}`}
96+
</span>
97+
<span className='hint'>
98+
TESTS
99+
</span>
100+
</React.Fragment>
101+
}
102+
{
103+
isDev &&
104+
<React.Fragment>
105+
<span className='value'>
106+
{`${parseFloat(profile.testsPassed / profile.totalTestCases * 100).toFixed(2)}%`}
107+
</span>
108+
<span className='hint'>
109+
COMPLETED
110+
</span>
111+
</React.Fragment>
112+
}
97113
</div>
98114
</div>
99115
}
@@ -128,17 +144,32 @@ const table = (props) => {
128144

129145
{
130146
profile.reveal === true && <React.Fragment>
131-
{ largeColumns && f2fLeaderboard && profile.hasOwnProperty('handle') && <div className='handleName animate fadeIn'>
132-
{profile.handle}
133-
</div> }
147+
{ largeColumns && f2fLeaderboard && profile.hasOwnProperty('handle') &&
148+
<div className='competitor'>
149+
<div className='avatar'>
150+
<img src={profile.profilePic} />
151+
</div>
152+
<img className='country-flag' src={profile.countryFlag} />
153+
<div className='handle' style={{ color: primaryColor }}>{profile.handle}</div>
154+
</div>
155+
}
134156
{ largeColumns && f2fLeaderboard && profile.hasOwnProperty('reviews') && profile.reviews.map((review, i) => (
135157
<div key={review.challengeId} className='f2fScoreTests animate fadeIn'>
136158
{
137159
!review.status && <React.Fragment>
138160
<div className='f2fFieldCell'>{review.score}</div>
139-
<div className='f2fFieldCell'>
140-
{review.testsPassed}{review.testsPassed > -1 && <span>/</span>}{review.totalTestCases}
141-
</div>
161+
{
162+
!isDev &&
163+
<div className='f2fFieldCell'>
164+
{review.testsPassed}{review.testsPassed > -1 && <span>/</span>}{review.totalTestCases}
165+
</div>
166+
}
167+
{
168+
isDev &&
169+
<div className='f2fFieldCell'>
170+
{review.testsPassed > -1 && `${parseFloat(review.testsPassed / profile.totalTestCases * 100).toFixed(2)}%`}
171+
</div>
172+
}
142173
</React.Fragment>
143174
}
144175
{
@@ -300,8 +331,8 @@ const table = (props) => {
300331
}
301332
302333
.country-flag {
303-
height: 26px;
304334
width: 24px;
335+
height: auto;
305336
position: absolute;
306337
z-index: 3;
307338
left: 56px;
@@ -365,7 +396,6 @@ const table = (props) => {
365396
366397
.row .tests-passed {
367398
display: flex;
368-
justify-content: center;
369399
align-items: center;
370400
}
371401
@@ -493,7 +523,6 @@ const table = (props) => {
493523
494524
.f2fScoreTests .f2fFieldCell:nth-child(2) {
495525
width: 66%;
496-
font-weight: 400;
497526
}
498527
499528
.non-score-lvl-pt {

components/Header.js

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ class Header extends React.Component {
77
super(props)
88

99
this.state = {
10-
timerText: null
10+
timerText: null,
11+
usedStartDate: false,
12+
usedEndDate: false
1113
}
1214

1315
setInterval(this.tick.bind(this), 1000)
1416
}
1517

1618
tick () {
17-
const { timerText } = this.state
19+
let { timerText, usedStartDate, usedEndDate } = this.state
1820
let { eventStartDateTime, eventEndDateTime } = this.props
1921

2022
if (!eventStartDateTime && !eventEndDateTime) {
@@ -24,25 +26,40 @@ class Header extends React.Component {
2426
return
2527
}
2628

27-
let endTime = eventEndDateTime || eventStartDateTime
29+
let endTime = eventStartDateTime
2830
endTime = Math.floor(new Date(endTime).getTime() / 1000)
2931

30-
const now = Math.floor(Date.now() / 1000)
31-
const diff = endTime - now
32+
let now = Math.floor(Date.now() / 1000)
33+
let diff = endTime - now
3234

3335
if (diff < 0) {
34-
this.setState({ timerText: null })
35-
} else {
36-
const hour = leadingZero(Math.floor(diff / 3600))
37-
const minutes = leadingZero(Math.floor((diff - hour * 3600) / 60))
38-
const seconds = leadingZero(diff % 60)
36+
usedStartDate = false
37+
endTime = eventEndDateTime
38+
endTime = Math.floor(new Date(endTime).getTime() / 1000)
39+
40+
now = Math.floor(Date.now() / 1000)
41+
diff = endTime - now
3942

40-
this.setState({ timerText: `${hour}:${minutes}:${seconds}` })
43+
if (diff < 0) {
44+
this.setState({ timerText: null })
45+
return
46+
} else {
47+
usedEndDate = true
48+
}
49+
} else {
50+
usedStartDate = true
51+
usedEndDate = false
4152
}
53+
54+
const hour = leadingZero(Math.floor(diff / 3600))
55+
const minutes = leadingZero(Math.floor((diff - hour * 3600) / 60))
56+
const seconds = leadingZero(diff % 60)
57+
58+
this.setState({ timerText: `${hour}:${minutes}:${seconds}`, usedStartDate, usedEndDate })
4259
}
4360

4461
timer () {
45-
const { timerText } = this.state
62+
const { timerText, usedStartDate, usedEndDate } = this.state
4663
const { smallHeader } = this.props
4764
const smallClass = smallHeader ? ' small' : ''
4865
return (
@@ -52,13 +69,13 @@ class Header extends React.Component {
5269
(
5370
<div className='timer'>
5471

55-
{smallHeader && <div className='hint'>
72+
{usedEndDate && <div className='hint'>
5673
Ends in
5774
</div>}
5875
<div className='clock'>
5976
{timerText}
6077
</div>
61-
{!smallHeader && <div className='hint'>
78+
{usedStartDate && <div className='hint'>
6279
Time to start
6380
</div>}
6481
</div>

components/Sponsors.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import PropTypes from 'prop-types'
22

33
const sponsors = (props) => {
4-
const { showFlatDesign, hideMainSponsor, showDivider, mainSponsor, otherSponsors, smallerSponsor } = props
4+
const { showFlatDesign, showDivider, otherSponsors, smallerSponsor } = props
5+
const sponsorGroup1 = otherSponsors.slice(0, 7)
6+
const sponsorGroup2 = otherSponsors.slice(8, otherSponsors.length)
57
let containerClass = (showFlatDesign) ? 'flatContainer' : (smallerSponsor) ? 'small' : ''
68
return (<div className={`container ${containerClass}`}>
79
{showDivider && <img className='divider' src='/static/img/divider.png' alt='divider' />}
@@ -10,10 +12,14 @@ const sponsors = (props) => {
1012
SPONSORED BY
1113
</div>
1214
}
13-
{ !hideMainSponsor && <img className='sponsor' src={mainSponsor} />}
1415
<div className='otherSponsorsContainer'>
1516
{
16-
otherSponsors.map((sponsor, i) => (<img key={`${sponsor}${i}`} className='otherSponsor' src={sponsor} alt='sponsor' />))
17+
sponsorGroup1.map((sponsor, i) => (<img key={`${sponsor}${i}`} className='otherSponsor' src={sponsor} alt='sponsor' />))
18+
}
19+
</div>
20+
<div className='otherSponsorsContainer'>
21+
{
22+
sponsorGroup2.map((sponsor, i) => (<img key={`${sponsor}${i}`} className='otherSponsor' src={sponsor} alt='sponsor' />))
1723
}
1824
</div>
1925
<style jsx>
@@ -27,7 +33,7 @@ const sponsors = (props) => {
2733
}
2834
2935
.container.small {
30-
margin-bottom: 0px;
36+
margin-bottom: 10px;
3137
}
3238
3339
.flatContainer {
@@ -86,10 +92,13 @@ const sponsors = (props) => {
8692
.otherSponsor {
8793
margin: 0px 30px;
8894
height: 24px;
95+
width: auto;
96+
opacity: 0.6;
8997
}
9098
9199
.small .otherSponsor {
92100
margin: 0px 20px;
101+
width: auto;
93102
}
94103
95104
.flatContainer .otherSponsorsContainer .otherSponsor {
@@ -137,16 +146,14 @@ sponsors.propTypes = {
137146
showDivider: PropTypes.bool,
138147
mainSponsor: PropTypes.string,
139148
otherSponsors: PropTypes.arrayOf(PropTypes.string),
140-
smallerSponsor: PropTypes.bool,
141-
hideMainSponsor: PropTypes.bool
149+
smallerSponsor: PropTypes.bool
142150
}
143151

144152
sponsors.defaultProps = {
145153
showDivider: false,
146154
showFlatDesign: false,
147155
mainSponsor: undefined,
148-
smallerSponsor: false,
149-
hideMainSponsor: false
156+
smallerSponsor: false
150157
}
151158

152159
export default sponsors

0 commit comments

Comments
 (0)