Skip to content

Commit 7442d43

Browse files
authored
Merge pull request #89 from code4rena-dev/samus/audit-status-public
Add audit status to contest tile
2 parents 5103fd3 + de9f754 commit 7442d43

File tree

7 files changed

+1080
-810
lines changed

7 files changed

+1080
-810
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@code4rena/components-library",
3-
"version": "4.5.2",
3+
"version": "4.5.3",
44
"description": "Code4rena's official components library ",
55
"types": "./dist/lib.d.ts",
66
"exports": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import React, { useEffect, useState } from "react";
2+
import {
3+
AuditStatus,
4+
MapAuditStatusToAuditPublicStage,
5+
} from "./ContestStatus.types";
6+
import { getRelativeDateTimeLongFormat } from "../../utils/time";
7+
import { Icon } from "../Icon";
8+
9+
const getAuditStatusLabel = (status: AuditStatus | null) => {
10+
switch (status) {
11+
case AuditStatus.PreAudit:
12+
return "Starts";
13+
case AuditStatus.Active:
14+
return "Ends";
15+
case AuditStatus.Awarding:
16+
return "Awarding";
17+
case AuditStatus.Judging:
18+
return "Judging";
19+
case AuditStatus.PJQA:
20+
return "Post-judging QA";
21+
case AuditStatus.Reporting:
22+
return "Report in progress";
23+
case AuditStatus.Review:
24+
return "Sponsor review";
25+
case AuditStatus.Triage:
26+
return "Triage";
27+
case AuditStatus.Restricted:
28+
return "Paused";
29+
case AuditStatus.JudgingComplete:
30+
return "Judging";
31+
case AuditStatus.Paused:
32+
return "Paused";
33+
case AuditStatus.Completed:
34+
return "Completed";
35+
case AuditStatus.LostDeal:
36+
case AuditStatus.Booking:
37+
return null;
38+
default:
39+
return null;
40+
}
41+
};
42+
43+
const getAuditStatusColor = (status: AuditStatus | null) => {
44+
switch (status) {
45+
case AuditStatus.PreAudit:
46+
return "#FFFFFF";
47+
case AuditStatus.Active:
48+
return "#24c473"; // green-60
49+
case AuditStatus.Awarding:
50+
case AuditStatus.Judging:
51+
case AuditStatus.PJQA:
52+
case AuditStatus.Reporting:
53+
case AuditStatus.Review:
54+
case AuditStatus.Triage:
55+
return "#7549FF"; // blurple-60
56+
case AuditStatus.Restricted:
57+
case AuditStatus.Paused:
58+
return "#6B6680";
59+
case AuditStatus.JudgingComplete:
60+
case AuditStatus.LostDeal:
61+
case AuditStatus.Booking:
62+
case AuditStatus.Completed:
63+
return null;
64+
default:
65+
return null;
66+
}
67+
};
68+
69+
export const AuditStatusSection = ({
70+
auditStatus,
71+
startTime,
72+
endTime,
73+
}: {
74+
auditStatus: AuditStatus | null;
75+
startTime: string;
76+
endTime: string;
77+
}) => {
78+
// Get comparison time for relative time calculation
79+
const [comparisonTime, setComparisonTime] = useState<Date | null>(null);
80+
useEffect(() => {
81+
if (auditStatus === AuditStatus.Active) {
82+
setComparisonTime(new Date(endTime));
83+
} else if (auditStatus === AuditStatus.PreAudit) {
84+
setComparisonTime(new Date(startTime));
85+
} else {
86+
setComparisonTime(null);
87+
}
88+
}, [auditStatus, startTime, endTime]);
89+
90+
// Get interval seconds for updating the relative time calculation
91+
const [intervalSecs, setIntervalSecs] = useState<number>(1);
92+
useEffect(() => {
93+
if (!endTime) return;
94+
const endTimeDate = new Date(endTime);
95+
const now = new Date();
96+
const diffMs = endTimeDate.getTime() - now.getTime();
97+
const oneHourMs = 3600000;
98+
const halfHourSecs = 1800;
99+
if (diffMs > oneHourMs) {
100+
setIntervalSecs(halfHourSecs);
101+
} else {
102+
setIntervalSecs(1);
103+
}
104+
}, [endTime]);
105+
106+
const [relativeDateTime, setRelativeDateTime] = useState<string | null>(null);
107+
useEffect(() => {
108+
if (!comparisonTime) return;
109+
const updateDateTime = () => {
110+
const newRelativeDateTime = getRelativeDateTimeLongFormat(comparisonTime);
111+
setRelativeDateTime(newRelativeDateTime); // Update relative date time string every interval
112+
};
113+
const intervalId = setInterval(updateDateTime, intervalSecs);
114+
updateDateTime();
115+
return () => clearInterval(intervalId);
116+
}, [comparisonTime, intervalSecs]);
117+
118+
if (!auditStatus) return null;
119+
120+
const publicStage = MapAuditStatusToAuditPublicStage[auditStatus];
121+
122+
if (!publicStage) return null;
123+
124+
const auditStatusLabel = getAuditStatusLabel(auditStatus);
125+
const iconColor = getAuditStatusColor(auditStatus);
126+
127+
return (
128+
<div className="details">
129+
{iconColor && (
130+
<div className="audit-tile__status__icon">
131+
<Icon name="dot" color={iconColor} />
132+
</div>
133+
)}
134+
<div
135+
className="audit-tile__status__status"
136+
title={comparisonTime?.toString() || ""}
137+
>
138+
{auditStatusLabel} {relativeDateTime}
139+
</div>
140+
</div>
141+
);
142+
};
+27-25
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,33 @@
11
@import "../../styles/variables";
22

33
.c4conteststatus {
4-
display: flex;
5-
flex-direction: row;
6-
align-items: center;
7-
justify-content: center;
8-
gap: 0.75rem;
9-
color: $color__white;
10-
font-size: $font-size__text;
11-
12-
.statusindicator {
13-
width: $spacing__s;
14-
height: $spacing__s;
15-
border-radius: 50%;
4+
display: flex;
5+
flex-direction: row;
6+
align-items: center;
7+
justify-content: center;
8+
gap: 0.75rem;
9+
color: $color__white;
10+
font-size: $font-size__text;
1611

17-
&.ended {
18-
display: none;
19-
}
20-
&.upcoming {
21-
display: block;
22-
background: white;
23-
}
24-
&.live {
25-
display: block;
26-
background: $color__green;
27-
}
12+
.statusindicator {
13+
width: $spacing__s;
14+
height: $spacing__s;
15+
border-radius: 50%;
16+
17+
&.ended {
18+
display: none;
19+
}
20+
&.upcoming {
21+
display: block;
22+
background: white;
23+
}
24+
&.live {
25+
display: block;
26+
background: $color__green;
2827
}
28+
}
2929

30-
p { margin: 0; }
31-
}
30+
p {
31+
margin: 0;
32+
}
33+
}

src/lib/ContestStatus/ContestStatus.types.ts

+31
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export enum Status {
77
export interface ContestStatusProps {
88
/** Status indicator for the current contest. */
99
status?: Status;
10+
/** Audit status. */
11+
auditStatus?: AuditStatus | null;
1012
/** String of custom classes to extend the default styling of the component. */
1113
className?: string;
1214
/** HTML element identifier */
@@ -32,3 +34,32 @@ export const AuditStatus = {
3234
} as const;
3335
// Take the AuditStatus object, and make a string literal type of the values
3436
export type AuditStatus = (typeof AuditStatus)[keyof typeof AuditStatus];
37+
38+
export enum AuditPublicStage {
39+
Active = "Active",
40+
Upcoming = "Upcoming",
41+
SubsClosed = "Submissions closed",
42+
Completed = "Completed",
43+
}
44+
45+
// Grouping mapping of the audit statuses to the public stages
46+
export const MapAuditStatusToAuditPublicStage: Record<
47+
AuditStatus,
48+
AuditPublicStage | null
49+
> = {
50+
[AuditStatus.PreAudit]: AuditPublicStage.Upcoming,
51+
[AuditStatus.Active]: AuditPublicStage.Active,
52+
[AuditStatus.Awarding]: AuditPublicStage.SubsClosed,
53+
[AuditStatus.Judging]: AuditPublicStage.SubsClosed,
54+
[AuditStatus.PJQA]: AuditPublicStage.SubsClosed,
55+
[AuditStatus.Reporting]: AuditPublicStage.SubsClosed,
56+
[AuditStatus.Review]: AuditPublicStage.SubsClosed,
57+
[AuditStatus.Triage]: AuditPublicStage.SubsClosed,
58+
[AuditStatus.Restricted]: AuditPublicStage.SubsClosed,
59+
[AuditStatus.JudgingComplete]: AuditPublicStage.SubsClosed,
60+
[AuditStatus.Paused]: AuditPublicStage.SubsClosed,
61+
[AuditStatus.Completed]: AuditPublicStage.Completed,
62+
// Excluded statuses:
63+
[AuditStatus.LostDeal]: null,
64+
[AuditStatus.Booking]: null,
65+
};

0 commit comments

Comments
 (0)