Skip to content

Commit 23a408d

Browse files
authored
feat: Show bonus xp end date (#3025)
* feat: Implement tool tip to show bonus xp end date * fix: Fix formatting * fix: Change to use overview's info instead * fix: Add missing fields for test * refactor: Use CSS Module instead of global scoped stylesheet * feat: Add relative deadline to helper function * chore: Correct wording
1 parent 24d6dfa commit 23a408d

File tree

8 files changed

+53
-17
lines changed

8 files changed

+53
-17
lines changed

src/commons/XMLParser/XMLParserHelper.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ const makeAssessmentOverview = (result: any, maxXpVal: number): AssessmentOvervi
8787
isGradingPublished: false,
8888
xp: 0,
8989
maxTeamSize: 1,
90-
hasVotingFeatures: false
90+
hasVotingFeatures: false,
91+
hoursBeforeEarlyXpDecay: 0
9192
};
9293
};
9394

src/commons/application/actions/__tests__/SessionActions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,8 @@ test('updateAssessmentOverviews generates correct action object', () => {
484484
xp: 0,
485485
isGradingPublished: false,
486486
maxTeamSize: 1,
487-
hasVotingFeatures: false
487+
hasVotingFeatures: false,
488+
hoursBeforeEarlyXpDecay: 0
488489
}
489490
];
490491
const action = SessionActions.updateAssessmentOverviews(overviews);

src/commons/application/reducers/__tests__/SessionReducer.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,8 @@ const assessmentOverviewsTest1: AssessmentOverview[] = [
335335
xp: 0,
336336
isGradingPublished: false,
337337
maxTeamSize: 5,
338-
hasVotingFeatures: false
338+
hasVotingFeatures: false,
339+
hoursBeforeEarlyXpDecay: 0
339340
}
340341
];
341342

@@ -358,7 +359,8 @@ const assessmentOverviewsTest2: AssessmentOverview[] = [
358359
xp: 1,
359360
isGradingPublished: false,
360361
maxTeamSize: 1,
361-
hasVotingFeatures: false
362+
hasVotingFeatures: false,
363+
hoursBeforeEarlyXpDecay: 0
362364
}
363365
];
364366

src/commons/assessment/Assessment.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { useDispatch } from 'react-redux';
2525
import { Navigate, useLoaderData, useParams } from 'react-router';
2626
import { NavLink } from 'react-router-dom';
2727
import { numberRegExp } from 'src/features/academy/AcademyTypes';
28+
import classes from 'src/styles/Academy.module.scss';
2829

2930
import defaultCoverImage from '../../assets/default_cover_image.jpg';
3031
import SessionActions from '../application/actions/SessionActions';
@@ -38,7 +39,7 @@ import Markdown from '../Markdown';
3839
import NotificationBadge from '../notificationBadge/NotificationBadge';
3940
import { filterNotificationsByAssessment } from '../notificationBadge/NotificationBadgeHelper';
4041
import Constants from '../utils/Constants';
41-
import { beforeNow, getPrettyDate } from '../utils/DateHelper';
42+
import { beforeNow, getPrettyDate, getPrettyDateAfterHours } from '../utils/DateHelper';
4243
import { useResponsive, useSession } from '../utils/Hooks';
4344
import { assessmentTypeLink, convertParamToInt } from '../utils/ParamParseHelper';
4445
import AssessmentNotFound from './AssessmentNotFound';
@@ -179,12 +180,19 @@ const Assessment: React.FC = () => {
179180
</div>
180181
<div className={classNames('listing-text', !isMobileBreakpoint && 'col-xs-9')}>
181182
{makeOverviewCardTitle(overview, index, renderGradingTooltip)}
182-
<div className="listing-xp">
183+
<div className={classes['listing-xp']}>
183184
<H6>
184185
{overview.isGradingPublished
185186
? `XP: ${overview.xp} / ${overview.maxXp}`
186187
: `Max XP: ${overview.maxXp}`}
187188
</H6>
189+
{overview.earlySubmissionXp > 0 && (
190+
<Tooltip
191+
content={`Max XP ends on ${getPrettyDateAfterHours(overview.openAt, overview.hoursBeforeEarlyXpDecay)}`}
192+
>
193+
<Icon icon={IconNames.InfoSign} />
194+
</Tooltip>
195+
)}
188196
</div>
189197
<div className="listing-description">
190198
<Markdown content={overview.shortSummary} />

src/commons/assessment/AssessmentTypes.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export type AssessmentOverview = {
8181
title: string;
8282
xp: number;
8383
maxTeamSize: number; // For team assessment
84+
hoursBeforeEarlyXpDecay: number;
8485
};
8586

8687
/*
@@ -259,7 +260,8 @@ export const overviewTemplate = (): AssessmentOverview => {
259260
isGradingPublished: false,
260261
xp: 0,
261262
maxTeamSize: 1,
262-
hasVotingFeatures: false
263+
hasVotingFeatures: false,
264+
hoursBeforeEarlyXpDecay: 0
263265
};
264266
};
265267

src/commons/mocks/AssessmentMocks.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ const mockUnopenedAssessmentsOverviews: AssessmentOverview[] = [
127127
xp: 0,
128128
isGradingPublished: false,
129129
maxTeamSize: 1,
130-
hasVotingFeatures: false
130+
hasVotingFeatures: false,
131+
hoursBeforeEarlyXpDecay: 0
131132
}
132133
];
133134

@@ -162,7 +163,8 @@ const mockOpenedAssessmentsOverviews: AssessmentOverview[] = [
162163
xp: 1,
163164
isGradingPublished: false,
164165
maxTeamSize: 4,
165-
hasVotingFeatures: false
166+
hasVotingFeatures: false,
167+
hoursBeforeEarlyXpDecay: 0
166168
},
167169
{
168170
type: 'Missions',
@@ -182,7 +184,8 @@ const mockOpenedAssessmentsOverviews: AssessmentOverview[] = [
182184
xp: 2,
183185
isGradingPublished: false,
184186
maxTeamSize: 1,
185-
hasVotingFeatures: false
187+
hasVotingFeatures: false,
188+
hoursBeforeEarlyXpDecay: 0
186189
},
187190
{
188191
type: 'Quests',
@@ -202,7 +205,8 @@ const mockOpenedAssessmentsOverviews: AssessmentOverview[] = [
202205
xp: 3,
203206
isGradingPublished: false,
204207
maxTeamSize: 2,
205-
hasVotingFeatures: false
208+
hasVotingFeatures: false,
209+
hoursBeforeEarlyXpDecay: 0
206210
},
207211
{
208212
type: 'Paths',
@@ -222,7 +226,8 @@ const mockOpenedAssessmentsOverviews: AssessmentOverview[] = [
222226
xp: 0,
223227
isGradingPublished: false,
224228
maxTeamSize: 2,
225-
hasVotingFeatures: false
229+
hasVotingFeatures: false,
230+
hoursBeforeEarlyXpDecay: 0
226231
},
227232
{
228233
type: 'Others',
@@ -243,7 +248,8 @@ const mockOpenedAssessmentsOverviews: AssessmentOverview[] = [
243248
private: true,
244249
maxTeamSize: 1,
245250
isGradingPublished: false,
246-
hasVotingFeatures: false
251+
hasVotingFeatures: false,
252+
hoursBeforeEarlyXpDecay: 0
247253
}
248254
];
249255

@@ -266,7 +272,8 @@ const mockClosedAssessmentOverviews: AssessmentOverview[] = [
266272
xp: 800,
267273
isGradingPublished: false,
268274
maxTeamSize: 1,
269-
hasVotingFeatures: false
275+
hasVotingFeatures: false,
276+
hoursBeforeEarlyXpDecay: 0
270277
},
271278
{
272279
type: 'Quests',
@@ -286,7 +293,8 @@ const mockClosedAssessmentOverviews: AssessmentOverview[] = [
286293
xp: 500,
287294
isGradingPublished: false,
288295
maxTeamSize: 1,
289-
hasVotingFeatures: false
296+
hasVotingFeatures: false,
297+
hoursBeforeEarlyXpDecay: 0
290298
},
291299
{
292300
type: 'Quests',
@@ -306,7 +314,8 @@ const mockClosedAssessmentOverviews: AssessmentOverview[] = [
306314
xp: 150,
307315
isGradingPublished: false,
308316
maxTeamSize: 1,
309-
hasVotingFeatures: false
317+
hasVotingFeatures: false,
318+
hoursBeforeEarlyXpDecay: 0
310319
},
311320
{
312321
type: 'Paths',
@@ -326,7 +335,8 @@ const mockClosedAssessmentOverviews: AssessmentOverview[] = [
326335
xp: 100,
327336
isGradingPublished: false,
328337
maxTeamSize: 1,
329-
hasVotingFeatures: false
338+
hasVotingFeatures: false,
339+
hoursBeforeEarlyXpDecay: 0
330340
}
331341
];
332342

src/commons/utils/DateHelper.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,10 @@ export const getStandardDate = (dateString: string): string => {
4141
const prettyDate = date.format('MMMM Do YYYY');
4242
return prettyDate;
4343
};
44+
45+
export const getPrettyDateAfterHours = (dateString: string, hours: number): string => {
46+
const date = moment(dateString).add(hours, 'hours');
47+
const absolutePrettyDate = date.format('Do MMMM, HH:mm');
48+
const relativePrettyDate = date.fromNow();
49+
return `${absolutePrettyDate} (${relativePrettyDate})`;
50+
};

src/styles/Academy.module.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,8 @@
1515
align-items: center;
1616
justify-content: center;
1717
}
18+
19+
.listing-xp {
20+
display: flex;
21+
gap: 0.5rem;
22+
}

0 commit comments

Comments
 (0)