Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@code4rena/components-library",
"version": "4.5.2",
"version": "4.5.3",
"description": "Code4rena's official components library ",
"types": "./dist/lib.d.ts",
"exports": {
Expand Down
142 changes: 142 additions & 0 deletions src/lib/ContestStatus/AuditStatusSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import React, { useEffect, useState } from "react";
import {
AuditStatus,
MapAuditStatusToAuditPublicStage,
} from "./ContestStatus.types";
import { getRelativeDateTimeLongFormat } from "../../utils/time";
import { Icon } from "../Icon";

const getAuditStatusLabel = (status: AuditStatus | null) => {
switch (status) {
case AuditStatus.PreAudit:
return "Starts";
case AuditStatus.Active:
return "Ends";
case AuditStatus.Awarding:
return "Awarding";
case AuditStatus.Judging:
return "Judging";
case AuditStatus.PJQA:
return "Post-judging QA";
case AuditStatus.Reporting:
return "Report in progress";
case AuditStatus.Review:
return "Sponsor review";
case AuditStatus.Triage:
return "Triage";
case AuditStatus.Restricted:
return "Paused";
case AuditStatus.JudgingComplete:
return "Judging";
case AuditStatus.Paused:
return "Paused";
case AuditStatus.Completed:
return "Completed";
case AuditStatus.LostDeal:
case AuditStatus.Booking:
return null;
default:
return null;
}
};

const getAuditStatusColor = (status: AuditStatus | null) => {
switch (status) {
case AuditStatus.PreAudit:
return "#FFFFFF";
case AuditStatus.Active:
return "#24c473"; // green-60
case AuditStatus.Awarding:
case AuditStatus.Judging:
case AuditStatus.PJQA:
case AuditStatus.Reporting:
case AuditStatus.Review:
case AuditStatus.Triage:
return "#7549FF"; // blurple-60
case AuditStatus.Restricted:
case AuditStatus.Paused:
return "#6B6680";
case AuditStatus.JudgingComplete:
case AuditStatus.LostDeal:
case AuditStatus.Booking:
case AuditStatus.Completed:
return null;
default:
return null;
}
};

export const AuditStatusSection = ({
auditStatus,
startTime,
endTime,
}: {
auditStatus: AuditStatus | null;
startTime: string;
endTime: string;
}) => {
// Get comparison time for relative time calculation
const [comparisonTime, setComparisonTime] = useState<Date | null>(null);
useEffect(() => {
if (auditStatus === AuditStatus.Active) {
setComparisonTime(new Date(endTime));
} else if (auditStatus === AuditStatus.PreAudit) {
setComparisonTime(new Date(startTime));
} else {
setComparisonTime(null);
}
}, [auditStatus, startTime, endTime]);

// Get interval seconds for updating the relative time calculation
const [intervalSecs, setIntervalSecs] = useState<number>(1);
useEffect(() => {
if (!endTime) return;
const endTimeDate = new Date(endTime);
const now = new Date();
const diffMs = endTimeDate.getTime() - now.getTime();
const oneHourMs = 3600000;
const halfHourSecs = 1800;
if (diffMs > oneHourMs) {
setIntervalSecs(halfHourSecs);
} else {
setIntervalSecs(1);
}
}, [endTime]);

const [relativeDateTime, setRelativeDateTime] = useState<string | null>(null);
useEffect(() => {
if (!comparisonTime) return;
const updateDateTime = () => {
const newRelativeDateTime = getRelativeDateTimeLongFormat(comparisonTime);
setRelativeDateTime(newRelativeDateTime); // Update relative date time string every interval
};
const intervalId = setInterval(updateDateTime, intervalSecs);
updateDateTime();
return () => clearInterval(intervalId);
}, [comparisonTime, intervalSecs]);

if (!auditStatus) return null;

const publicStage = MapAuditStatusToAuditPublicStage[auditStatus];

if (!publicStage) return null;

const auditStatusLabel = getAuditStatusLabel(auditStatus);
const iconColor = getAuditStatusColor(auditStatus);

return (
<div className="details">
{iconColor && (
<div className="audit-tile__status__icon">
<Icon name="dot" color={iconColor} />
</div>
)}
<div
className="audit-tile__status__status"
title={comparisonTime?.toString() || ""}
>
{auditStatusLabel} {relativeDateTime}
</div>
</div>
);
};
52 changes: 27 additions & 25 deletions src/lib/ContestStatus/ContestStatus.scss
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
@import "../../styles/variables";

.c4conteststatus {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 0.75rem;
color: $color__white;
font-size: $font-size__text;

.statusindicator {
width: $spacing__s;
height: $spacing__s;
border-radius: 50%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 0.75rem;
color: $color__white;
font-size: $font-size__text;

&.ended {
display: none;
}
&.upcoming {
display: block;
background: white;
}
&.live {
display: block;
background: $color__green;
}
.statusindicator {
width: $spacing__s;
height: $spacing__s;
border-radius: 50%;

&.ended {
display: none;
}
&.upcoming {
display: block;
background: white;
}
&.live {
display: block;
background: $color__green;
}
}

p { margin: 0; }
}
p {
margin: 0;
}
}
31 changes: 31 additions & 0 deletions src/lib/ContestStatus/ContestStatus.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export enum Status {
export interface ContestStatusProps {
/** Status indicator for the current contest. */
status?: Status;
/** Audit status. */
auditStatus?: AuditStatus | null;
/** String of custom classes to extend the default styling of the component. */
className?: string;
/** HTML element identifier */
Expand All @@ -32,3 +34,32 @@ export const AuditStatus = {
} as const;
// Take the AuditStatus object, and make a string literal type of the values
export type AuditStatus = (typeof AuditStatus)[keyof typeof AuditStatus];

export enum AuditPublicStage {
Active = "Active",
Upcoming = "Upcoming",
SubsClosed = "Submissions closed",
Completed = "Completed",
}

// Grouping mapping of the audit statuses to the public stages
export const MapAuditStatusToAuditPublicStage: Record<
AuditStatus,
AuditPublicStage | null
> = {
[AuditStatus.PreAudit]: AuditPublicStage.Upcoming,
[AuditStatus.Active]: AuditPublicStage.Active,
[AuditStatus.Awarding]: AuditPublicStage.SubsClosed,
[AuditStatus.Judging]: AuditPublicStage.SubsClosed,
[AuditStatus.PJQA]: AuditPublicStage.SubsClosed,
[AuditStatus.Reporting]: AuditPublicStage.SubsClosed,
[AuditStatus.Review]: AuditPublicStage.SubsClosed,
[AuditStatus.Triage]: AuditPublicStage.SubsClosed,
[AuditStatus.Restricted]: AuditPublicStage.SubsClosed,
[AuditStatus.JudgingComplete]: AuditPublicStage.SubsClosed,
[AuditStatus.Paused]: AuditPublicStage.SubsClosed,
[AuditStatus.Completed]: AuditPublicStage.Completed,
// Excluded statuses:
[AuditStatus.LostDeal]: null,
[AuditStatus.Booking]: null,
};
Loading