Skip to content

Commit 746353d

Browse files
committed
contest-tile: update countdown to account for paused audit status
1 parent 1be35d2 commit 746353d

File tree

8 files changed

+145
-53
lines changed

8 files changed

+145
-53
lines changed

src/lib/ContestStatus/ContestStatus.types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,21 @@ export interface ContestStatusProps {
1212
/** HTML element identifier */
1313
id?: string;
1414
}
15+
16+
export const AuditStatus = {
17+
Booking: "Booking",
18+
PreAudit: "Pre-Audit",
19+
Active: "Active",
20+
/** Paused: The audit is in between Rolling Triage cohorts */
21+
Paused: "Paused",
22+
Review: "Review",
23+
Judging: "Judging",
24+
PJQA: "Post-Judging QA",
25+
JudgingComplete: "Judging Complete",
26+
Awarding: "Awarding",
27+
Reporting: "Reporting",
28+
Completed: "Completed",
29+
LostDeal: "Lost Deal",
30+
} as const;
31+
// Take the AuditStatus object, and make a string literal type of the values
32+
export type AuditStatus = (typeof AuditStatus)[keyof typeof AuditStatus];

src/lib/ContestTile/CompactTemplate.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { BountyTileData, ContestSchedule, ContestTileData, ContestTileProps, Con
44
import { Status, TagSize, TagVariant } from '../types';
55
import { ContestStatus } from '../ContestStatus';
66
import { ContestCountdown } from './ContestTile';
7-
import { getDates } from '../../utils/time';
7+
import { getContestSchedule } from '../../utils/time';
88
import { Tag } from '../Tag';
99
import { Icon } from '../Icon';
1010
import wolfbotIcon from "../../../public/icons/wolfbot.svg";
@@ -82,15 +82,15 @@ const IsContest = ({title, isDarkTile = true, contestData, sponsorUrl, sponsorIm
8282
}
8383

8484
if (startDate && endDate) {
85-
const newTimelineObject = getDates(contestData.startDate, contestData.endDate, contestData.cohorts);
85+
const newTimelineObject = getContestSchedule(contestData);
8686
setContestTimelineObject(newTimelineObject);
8787
}
8888
}
8989
}, [contestData]);
9090

9191
useEffect(() => {
9292
if (contestData && startDate && endDate) {
93-
const newTimelineObject = getDates(startDate, endDate, contestData.cohorts);
93+
const newTimelineObject = getContestSchedule(contestData);
9494
setContestTimelineObject(newTimelineObject);
9595
}
9696
}, [contestData])

src/lib/ContestTile/ContestTile.stories.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { addDays, subDays } from "date-fns";
33
import { ContestTile } from "./ContestTile";
44
import { Meta, StoryObj } from "@storybook/react";
55
import { CodingLanguage, ContestEcosystem, ContestTileVariant } from "./ContestTile.types";
6+
import { AuditStatus } from "../types";
67

78
const meta: Meta<typeof ContestTile> = {
89
component: ContestTile,
@@ -151,7 +152,7 @@ export const ContestTileLivePreCohort2: Story = (args) => {
151152
/>
152153
</Fragment>
153154
};
154-
export const ContestTileLiveCohort3: Story = (args) => {
155+
export const ContestTileLiveAwaitingCohort3: Story = (args) => {
155156
const isDark = args.variant === ContestTileVariant.DARK || args.variant === ContestTileVariant.COMPACT_DARK;
156157

157158
return <Fragment>
@@ -207,7 +208,7 @@ ContestTileUpcomingRollingTriage.parameters = parameters;
207208
ContestTileLive.parameters = parameters;
208209
ContestTileLiveCohort1.parameters = parameters;
209210
ContestTileLivePreCohort2.parameters = parameters;
210-
ContestTileLiveCohort3.parameters = parameters;
211+
ContestTileLiveAwaitingCohort3.parameters = parameters;
211212
ContestTileEnded.parameters = parameters;
212213
BountyTile.parameters = parameters;
213214

@@ -216,7 +217,8 @@ ContestTileUpcoming.args = {
216217
contestData: {
217218
...defaultArgs.contestData,
218219
startDate: "2030-07-12T18:00:00Z",
219-
endDate: "2030-07-21T18:00:00.000Z"
220+
endDate: "2030-07-21T18:00:00.000Z",
221+
status: AuditStatus.PreAudit,
220222
}
221223
};
222224
ContestTileUpcomingRollingTriage.args = {
@@ -238,6 +240,7 @@ ContestTileUpcomingRollingTriage.args = {
238240
}],
239241
startDate: addDays(Date.now(), 3).toISOString(),
240242
endDate: addDays(Date.now(), 20).toISOString(),
243+
status: AuditStatus.PreAudit,
241244
}
242245
};
243246

@@ -247,7 +250,8 @@ ContestTileLive.args = {
247250
contestData: {
248251
...defaultArgs.contestData,
249252
startDate: "2023-07-12T18:00:00Z",
250-
endDate: "2030-07-21T18:00:00.000Z"
253+
endDate: "2030-07-21T18:00:00.000Z",
254+
status: AuditStatus.Active,
251255
}
252256
};
253257
ContestTileLiveCohort1.args = {
@@ -269,6 +273,7 @@ ContestTileLiveCohort1.args = {
269273
}],
270274
startDate: subDays(Date.now(), 1).toISOString(),
271275
endDate: addDays(Date.now(), 18).toISOString(),
276+
status: AuditStatus.Active,
272277
}
273278
};
274279
ContestTileLivePreCohort2.args = {
@@ -290,9 +295,10 @@ ContestTileLivePreCohort2.args = {
290295
}],
291296
startDate: subDays(Date.now(), 6).toISOString(),
292297
endDate: addDays(Date.now(), 16).toISOString(),
298+
status: AuditStatus.Paused,
293299
}
294300
};
295-
ContestTileLiveCohort3.args = {
301+
ContestTileLiveAwaitingCohort3.args = {
296302
...defaultArgs,
297303
contestData: {
298304
...defaultArgs.contestData,
@@ -311,6 +317,7 @@ ContestTileLiveCohort3.args = {
311317
}],
312318
startDate: subDays(Date.now(), 16).toISOString(),
313319
endDate: addDays(Date.now(), 6).toISOString(),
320+
status: AuditStatus.Paused,
314321
}
315322
};
316323

@@ -319,7 +326,8 @@ ContestTileEnded.args = {
319326
contestData: {
320327
...defaultArgs.contestData,
321328
startDate: "2023-07-12T18:00:00Z",
322-
endDate: "2023-07-21T18:00:00Z"
329+
endDate: "2023-07-21T18:00:00Z",
330+
status: AuditStatus.Review,
323331
}
324332
};
325333

src/lib/ContestTile/ContestTile.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
CountdownProps,
77
} from "./ContestTile.types";
88
import { getDates } from "../../utils/time";
9-
import { Status } from "../ContestStatus/ContestStatus.types";
9+
import { AuditStatus, Status } from "../ContestStatus/ContestStatus.types";
1010
import { formatDistanceToNow, formatDistanceToNowStrict } from "date-fns";
1111
import "./ContestTile.scss";
1212
import CompactTemplate from "./CompactTemplate";
@@ -28,9 +28,9 @@ export const Countdown = ({
2828
}: CountdownProps) => {
2929
const secondsInDay = 86400;
3030
const [lessThan24h, setLessThan24h] = useState(false);
31-
const [contestTimer, setContestTimer] = useState<ContestSchedule>();
31+
const [contestTimer, setContestTimer] = useState<Pick<ContestSchedule, "contestStatus" | "botRaceStatus">>();
3232

33-
const getCountdownTarget = (schedule: ContestSchedule): Date => {
33+
const getCountdownTarget = (schedule: Pick<ContestSchedule, "contestStatus" | "end" | "start">): Date => {
3434
if (schedule.contestStatus === Status.LIVE) {
3535
return schedule.end;
3636
}
@@ -57,7 +57,7 @@ export const Countdown = ({
5757
}
5858

5959
const countDown = useCallback(() => {
60-
const newTimer = getDates(start, end, []);
60+
const newTimer = getDates(start, end);
6161
const target = getCountdownTarget(newTimer);
6262
// Get total number of seconds remaining
6363
const totalSeconds = formatDistanceToNowStrict(target, {
@@ -78,7 +78,7 @@ export const Countdown = ({
7878
useEffect(() => {
7979
const timer = setInterval(
8080
() => {
81-
const newTimer = getDates(start, end, []);
81+
const newTimer = getDates(start, end);
8282
if (
8383
contestTimer &&
8484
(contestTimer.contestStatus !== newTimer.contestStatus ||
@@ -137,11 +137,18 @@ export const ContestCountdown = ({
137137
if (schedule.contestStatus === Status.UPCOMING) {
138138
text = "Starts in ";
139139
} else if (schedule.contestStatus === Status.LIVE) {
140-
if (schedule.resume && +schedule.resume >= Date.now()) {
141-
text = "Cohort tentatively resumes in ";
140+
if (schedule.status === AuditStatus.Paused && schedule.resume && +schedule.resume >= Date.now()) {
141+
text = "Next cohort starts in ";
142142
start = schedule.resume.toISOString();
143-
} else if (schedule.pause && +schedule.pause >= Date.now()) {
144-
text = "Cohort pauses in ";
143+
} else if (schedule.status === AuditStatus.Paused && schedule.resume && +schedule.resume <= Date.now()) {
144+
// The resume time has elapsed, give a generic time for now
145+
return (
146+
<div className="countdown">
147+
{"Next cohort starts soon"}
148+
</div>
149+
);
150+
} else if (schedule.status === AuditStatus.Active && schedule.pause && +schedule.pause >= Date.now()) {
151+
text = "Current cohort ends in ";
145152
end = schedule.pause.toISOString();
146153
}
147154
}

src/lib/ContestTile/ContestTile.types.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ReactNode } from "react";
2-
import { Status } from "../ContestStatus/ContestStatus.types";
2+
import { Status, AuditStatus } from "../ContestStatus/ContestStatus.types";
33

44
export enum ContestTileVariant {
55
LIGHT = "LIGHT",
@@ -87,17 +87,14 @@ export interface ContestTileData {
8787
endDate: string;
8888
/** Boolean indicating certification status of logged in user. Required for viewing certain contests. */
8989
isUserCertified: boolean;
90+
status: AuditStatus;
9091
}
9192

92-
export interface ContestSchedule {
93+
export interface BaseContestSchedule {
9394
contestStatus?: Status;
9495
botRaceStatus?: Status;
9596
start: Date;
9697
end: Date;
97-
/** The time the current cohort will pause. */
98-
pause: Date | null;
99-
/** The time the current cohort will resume. */
100-
resume: Date | null;
10198
botRaceEnd: Date;
10299
formattedStart: string;
103100
formattedEnd: string;
@@ -106,6 +103,15 @@ export interface ContestSchedule {
106103
formattedDuration: string;
107104
}
108105

106+
export interface ContestSchedule extends BaseContestSchedule {
107+
status: AuditStatus;
108+
end: Date;
109+
/** The time the current cohort will pause. */
110+
pause: Date | null;
111+
/** The time the current cohort will resume. */
112+
resume: Date | null;
113+
}
114+
109115
export interface CountdownProps {
110116
start: string;
111117
end: string;

src/lib/ContestTile/DefaultTemplate.tsx

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import React, { Fragment, useCallback, useEffect, useState } from "react";
22
import clsx from "clsx";
33
import wolfbotIcon from "../../../public/icons/wolfbot.svg";
4-
import { BountyTileData, ContestSchedule, ContestTileData, ContestTileProps, ContestTileVariant } from "./ContestTile.types";
4+
import { BaseContestSchedule, BountyTileData, ContestSchedule, ContestTileData, ContestTileProps, ContestTileVariant } from "./ContestTile.types";
55
import { DropdownLink, Status, TagSize, TagVariant } from "../types";
66
import { ContestStatus } from "../ContestStatus";
77
import { ContestCountdown, Countdown } from "./ContestTile";
8-
import { getDates } from "../../utils/time";
8+
import { getDates, getContestSchedule } from "../../utils/time";
99
import { isBefore } from "date-fns";
1010
import { Dropdown } from "../Dropdown";
1111
import { Icon } from "../Icon";
@@ -32,7 +32,7 @@ export default function DefaultTemplate({
3232
const [canViewContest, setCanViewContest] = useState(false);
3333
const [dropdownLinks, setDropdownLinks] = useState<DropdownLink[]>([]);
3434
const [contestTimelineObject, setContestTimelineObject] = useState<ContestSchedule | undefined>();
35-
const [bountyTimelineObject, setBountyTimelineObject] = useState<ContestSchedule | undefined>();
35+
const [bountyTimelineObject, setBountyTimelineObject] = useState<BaseContestSchedule | undefined>();
3636

3737
const updateContestTileStatus = useCallback(() => {
3838
if (contestData) {
@@ -41,7 +41,7 @@ export default function DefaultTemplate({
4141
updateContestStatus();
4242
}
4343
if (contestData.startDate) {
44-
const newTimelineObject = getDates(contestData.startDate, contestData.endDate, contestData.cohorts);
44+
const newTimelineObject = getContestSchedule(contestData);
4545
setContestTimelineObject(newTimelineObject);
4646
}
4747
}
@@ -54,7 +54,7 @@ export default function DefaultTemplate({
5454
updateBountyStatus();
5555
}
5656
if (bountyData.startDate) {
57-
const newTimelineObject = getDates(bountyData.startDate, "2999-01-01T00:00:00Z", []);
57+
const newTimelineObject = getDates(bountyData.startDate, "2999-01-01T00:00:00Z");
5858
setBountyTimelineObject(newTimelineObject);
5959
}
6060
}
@@ -64,19 +64,16 @@ export default function DefaultTemplate({
6464
if (bountyData && bountyData.startDate) {
6565
const newTimelineObject = getDates(
6666
bountyData.startDate,
67-
"2999-01-01T00:00:00Z",
68-
[]
67+
"2999-01-01T00:00:00Z"
6968
);
7069
setBountyTimelineObject(newTimelineObject);
7170
}
7271

7372
if (contestData) {
7473
setHasBotRace(!!contestData.botFindingsRepo);
7574
if (contestData.startDate && contestData.endDate) {
76-
const newTimelineObject = getDates(
77-
contestData.startDate,
78-
contestData.endDate,
79-
contestData.cohorts
75+
const newTimelineObject = getContestSchedule(
76+
contestData
8077
);
8178
setContestTimelineObject(newTimelineObject);
8279
}
@@ -403,7 +400,7 @@ function IsBounty({
403400
bountyData: BountyTileData;
404401
isDarkTile: boolean;
405402
updateBountyTileStatus?: () => void;
406-
bountyTimelineObject?: ContestSchedule | undefined;
403+
bountyTimelineObject?: BaseContestSchedule | undefined;
407404
}) {
408405
const { bountyUrl, amount, startDate, ecosystem, languages } = bountyData;
409406
const endDate = "2999-01-01T00:00:00Z"

0 commit comments

Comments
 (0)