diff --git a/package.json b/package.json
index c91af3c9..661c3053 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@code4rena/components-library",
- "version": "3.0.2",
+ "version": "3.1.0",
"description": "Code4rena's official components library ",
"types": "./dist/lib.d.ts",
"exports": {
diff --git a/src/lib/ContestTile/CompactTemplate.tsx b/src/lib/ContestTile/CompactTemplate.tsx
index ea4ebd2a..f0772ae8 100644
--- a/src/lib/ContestTile/CompactTemplate.tsx
+++ b/src/lib/ContestTile/CompactTemplate.tsx
@@ -1,10 +1,13 @@
import React, { useCallback, useEffect, useState } from 'react';
import clsx from "clsx";
import { BountyTileData, ContestSchedule, ContestTileData, ContestTileProps, ContestTileVariant } from "./ContestTile.types";
-import { Status } from '../types';
+import { Status, TagSize, TagVariant } from '../types';
import { ContestStatus } from '../ContestStatus';
import { Countdown } from './ContestTile';
import { getDates } from '../../utils/time';
+import { Tag } from '../Tag';
+import { Icon } from '../Icon';
+import wolfbotIcon from "../../../public/icons/wolfbot.svg";
export default function CompactTemplate({
variant,
@@ -19,6 +22,7 @@ export default function CompactTemplate({
"compact--light": variant === ContestTileVariant.COMPACT_LIGHT,
"compact--dark": variant === ContestTileVariant.COMPACT_DARK,
});
+ const isDarkTile = variant === ContestTileVariant.DARK || variant === ContestTileVariant.COMPACT_DARK;
const tileClasses = clsx({
c4contesttile: true,
@@ -29,11 +33,13 @@ export default function CompactTemplate({
{contestData && }
{bountyData && {
- const { startDate, endDate, amount, contestUrl, contestType } = contestData;
+ const { startDate, endDate, amount, contestUrl, contestType, ecosystem, languages } = contestData;
const [contestTimelineObject, setContestTimelineObject] = useState();
+ const [hasBotRace, setHasBotRace] = useState(contestData ? !!contestData.botFindingsRepo : false);
+ let ecosystemLogoName: string = "";
+ if (ecosystem && (ecosystem === "Polkadot" || ecosystem === "Ethereum")) {
+ ecosystemLogoName = `logo-${ecosystem.toLowerCase()}`
+ }
const updateContestTileStatus = useCallback(() => {
if (contestData) {
@@ -137,16 +149,48 @@ const IsContest = ({title, contestData, sponsorUrl, sponsorImage}: {
{amount}
+ {((hasBotRace && contestTimelineObject && (contestTimelineObject.botRaceStatus === Status.UPCOMING ||
+ contestTimelineObject.botRaceStatus === Status.LIVE)) || ecosystem || languages?.length > 0) &&
+ {hasBotRace && contestTimelineObject &&
+ (contestTimelineObject.botRaceStatus === Status.UPCOMING ||
+ contestTimelineObject.botRaceStatus === Status.LIVE) && (
+
+ )}
+ {ecosystem && : undefined}
+ size={TagSize.NARROW}
+ />}
+ {languages
+ && languages.length > 0
+ && languages.map((language) =>
+ )}
+
}
)}
-const IsBounty = ({title, bountyData, sponsorUrl, sponsorImage}: {
+const IsBounty = ({title, isDarkTile = true, bountyData, sponsorUrl, sponsorImage}: {
title: string;
+ isDarkTile: boolean;
bountyData: BountyTileData;
sponsorUrl: string | undefined;
sponsorImage: string | undefined;
}) => {
- const { amount, bountyUrl } = bountyData;
+ const { amount, bountyUrl, ecosystem, languages } = bountyData;
+ let ecosystemLogoName: string = "";
+ if (ecosystem && (ecosystem === "Polkadot" || ecosystem === "Ethereum")) {
+ ecosystemLogoName = `logo-${ecosystem.toLowerCase()}`
+ }
return (
@@ -189,5 +233,20 @@ const IsBounty = ({title, bountyData, sponsorUrl, sponsorImage}: {
Max Bounty
{amount}
+ {(ecosystem || languages?.length > 0) &&
+ {ecosystem && : undefined}
+ size={TagSize.NARROW}
+ />}
+ {languages
+ && languages.length > 0
+ && languages.map((language) =>
+ )}
+
}
)}
\ No newline at end of file
diff --git a/src/lib/ContestTile/ContestTile.scss b/src/lib/ContestTile/ContestTile.scss
index 472aae7f..5fc10582 100644
--- a/src/lib/ContestTile/ContestTile.scss
+++ b/src/lib/ContestTile/ContestTile.scss
@@ -437,6 +437,39 @@
}
}
+ .tags {
+ padding: 0.24rem 0.5rem 0.5rem;
+ display: flex;
+ flex-direction: row;
+ gap: 0.5rem;
+ flex-wrap: wrap;
+
+ img {
+ width: 0.75rem !important;
+ height: 0.75rem !important;
+ }
+
+ .icon {
+ svg {
+ min-width: 1.125rem;
+ min-height: 1.125rem;
+ width: 1.125rem;
+ height: 1.125rem;
+ }
+ }
+
+
+ .separator {
+ height: 26px;
+ }
+
+ p {
+ padding-top: 5px;
+ padding-bottom: 5px;
+ font-size: 0.75rem !important;
+ }
+ }
+
.amount {
margin: 0px;
margin-left: auto;
@@ -506,7 +539,16 @@
display: block;
text-align: center;
}
-
+
+ .tags {
+ margin-top: 0.5rem;
+ display: flex;
+ flex-direction: row;
+ gap: 0.5rem;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+
.title {
margin: 0rem;
color: $color__white;
@@ -514,16 +556,6 @@
font-size: $headline-font-size__xxs;
}
- .bot-race-status {
- display: flex;
- gap: 0.5rem;
- margin-top: 0.25rem;
- align-items: center;
- justify-content: center;
- color: $color__white;
- font-weight: 700;
- }
-
.description {
margin: $spacing__s 0rem 0rem;
}
@@ -587,7 +619,7 @@
text-align: left;
}
- .bot-race-status {
+ .tags {
justify-content: flex-start;
}
}
diff --git a/src/lib/ContestTile/ContestTile.stories.tsx b/src/lib/ContestTile/ContestTile.stories.tsx
index 6c994c0b..9ff53b13 100644
--- a/src/lib/ContestTile/ContestTile.stories.tsx
+++ b/src/lib/ContestTile/ContestTile.stories.tsx
@@ -1,7 +1,7 @@
import React, { Fragment } from "react";
import { ContestTile } from "./ContestTile";
import { Meta, StoryObj } from "@storybook/react";
-import { ContestTileVariant } from "./ContestTile.types";
+import { CodingLanguage, ContestEcosystem, ContestTileVariant } from "./ContestTile.types";
const meta: Meta = {
component: ContestTile,
@@ -39,9 +39,12 @@ const defaultArgs = {
contestType: "Open Audit",
isUserCertified: false,
contestId: 321,
+ ecosystem: "Ethereum" as ContestEcosystem,
+ languages: ["Rust"] as CodingLanguage[],
contestUrl: "https://code4rena.com/audits/2023-07-axelar-network#top",
contestRepo: "https://github.com/code-423n4/2023-07-axelar",
findingsRepo: "https://github.com/code-423n4/2023-07-axelar",
+ botFindingsRepo: "https://github.com/code-423n4/2023-07-axelar",
amount: "$80,000 USDC",
startDate: "2030-07-12T18:00:00Z",
endDate: "2030-07-21T18:00:00.000Z",
@@ -162,6 +165,8 @@ BountyTile.args = {
startDate: "2023-07-12T18:00:00Z",
repoUrl: "https://github.com/code-423n4/2023-07-axelar",
bountyUrl: "https://code4rena.com/audits/2023-07-axelar-network#top",
+ ecosystem: "Polkadot" as ContestEcosystem,
+ languages: ["Rust"] as CodingLanguage[],
},
variant: ContestTileVariant.LIGHT,
sponsorImage: "/logos/apple-touch-icon.png",
diff --git a/src/lib/ContestTile/ContestTile.types.ts b/src/lib/ContestTile/ContestTile.types.ts
index a9299dd7..2e38e512 100644
--- a/src/lib/ContestTile/ContestTile.types.ts
+++ b/src/lib/ContestTile/ContestTile.types.ts
@@ -8,6 +8,10 @@ export enum ContestTileVariant {
COMPACT_DARK = "COMPACT_DARK"
}
+export type ContestEcosystem = "Algorand" | "Aptos" | "Blast" | "Cosmos" | "Ethereum" | "EVM" | "NEAR" | "Polkadot" | "Scroll" | "Sei" | "Solana" | "StarkNet" | "Stellar" | "Sui" | "Other";
+
+export type CodingLanguage = "Cairo" | "GO" | "HUFF" | "Ink" | "Move" | "Noir" | "Other" | "Rain" | "Rust" | "Rust evm" | "Solidity" | "Vyper" | "Yui";
+
export interface ContestTileProps {
/** An html `id` for the contest tile's wrapping div. */
htmlId?: string;
@@ -38,6 +42,10 @@ export interface BountyTileData {
bountyUrl: string;
/** Absolute url to the bounty's source code. */
repoUrl: string;
+ /** Ecosystem being deployed to for the current contest. */
+ ecosystem: ContestEcosystem;
+ /** Coding language for the current contest. */
+ languages: CodingLanguage[];
/** Callback function to be triggered on bounty time/status changes. */
updateBountyStatus?: () => void;
}
@@ -57,6 +65,10 @@ export interface ContestTileData {
findingsRepo: string;
/** Absolute url to the contest's findings. */
botFindingsRepo?: string;
+ /** Ecosystem being deployed to for the current contest. */
+ ecosystem: ContestEcosystem;
+ /** Coding language for the current contest. */
+ languages: CodingLanguage[];
/** Reward pool for the current contest. */
amount: string;
/** Callback function to be triggered on contest time/status changes. */
diff --git a/src/lib/ContestTile/DefaultTemplate.tsx b/src/lib/ContestTile/DefaultTemplate.tsx
index 51e068ce..662d75b8 100644
--- a/src/lib/ContestTile/DefaultTemplate.tsx
+++ b/src/lib/ContestTile/DefaultTemplate.tsx
@@ -2,13 +2,14 @@ import React, { Fragment, useCallback, useEffect, useState } from "react";
import clsx from "clsx";
import wolfbotIcon from "../../../public/icons/wolfbot.svg";
import { BountyTileData, ContestSchedule, ContestTileData, ContestTileProps, ContestTileVariant } from "./ContestTile.types";
-import { DropdownLink, Status } from "../types";
+import { DropdownLink, Status, TagSize, TagVariant } from "../types";
import { ContestStatus } from "../ContestStatus";
import { Countdown } from "./ContestTile";
import { getDates } from "../../utils/time";
import { isBefore } from "date-fns";
import { Dropdown } from "../Dropdown";
import { Icon } from "../Icon";
+import { Tag } from "../Tag";
export default function DefaultTemplate({
@@ -26,6 +27,7 @@ export default function DefaultTemplate({
"tile--light": variant === ContestTileVariant.LIGHT,
"tile--dark": variant === ContestTileVariant.DARK,
});
+ const isDarkTile = variant === ContestTileVariant.DARK || variant === ContestTileVariant.COMPACT_DARK
const [hasBotRace, setHasBotRace] = useState(false);
const [canViewContest, setCanViewContest] = useState(false);
const [dropdownLinks, setDropdownLinks] = useState([]);
@@ -148,6 +150,7 @@ export default function DefaultTemplate({
{contestData &&
}
{bountyData &&
void;
contestTimelineObject: ContestSchedule | undefined;
}) {
- const { contestUrl, amount, findingsRepo, startDate, endDate } = contestData;
+ const { contestUrl, amount, findingsRepo, startDate, endDate, ecosystem, languages } = contestData;
+ let ecosystemLogoName: string = "";
+ if (ecosystem && (ecosystem === "Polkadot" || ecosystem === "Ethereum")) {
+ ecosystemLogoName = `logo-${ecosystem.toLowerCase()}`
+ }
return (
@@ -296,23 +306,35 @@ function IsContest({
{/* Contest description */}
- {description}{" "}
- {hasBotRace && contestTimelineObject &&
- (contestTimelineObject.botRaceStatus === Status.UPCOMING ||
- contestTimelineObject.botRaceStatus === Status.LIVE) && (
-
-
- {contestTimelineObject.botRaceStatus ===
- Status.UPCOMING && "1st hour: Bot Race"}
- {contestTimelineObject.botRaceStatus === Status.LIVE &&
- "Bot Race live"}
-
- )}
+ {description}{" "}
+ {((hasBotRace && contestTimelineObject && (contestTimelineObject.botRaceStatus === Status.UPCOMING ||
+ contestTimelineObject.botRaceStatus === Status.LIVE))
+ || ecosystem
+ || languages?.length > 0) &&
+ {hasBotRace && contestTimelineObject &&
+ (contestTimelineObject.botRaceStatus === Status.UPCOMING ||
+ contestTimelineObject.botRaceStatus === Status.LIVE) && (
+
+ )}
+ {ecosystem && : undefined}
+ size={TagSize.NARROW}
+ />}
+ {languages
+ && languages.length > 0
+ && languages.map((language) =>
+ )}
+
}
@@ -365,6 +387,7 @@ function IsBounty({
sponsorUrl,
sponsorImage,
bountyData,
+ isDarkTile = true,
updateBountyTileStatus,
bountyTimelineObject
}: {
@@ -373,94 +396,114 @@ function IsBounty({
sponsorUrl?: string;
sponsorImage?: string;
bountyData: BountyTileData;
+ isDarkTile: boolean;
updateBountyTileStatus?: () => void;
bountyTimelineObject?: ContestSchedule | undefined;
}) {
- const { bountyUrl, amount, startDate } = bountyData;
+ const { bountyUrl, amount, startDate, ecosystem, languages } = bountyData;
const endDate = "2999-01-01T00:00:00Z"
+ let ecosystemLogoName: string = "";
+ if (ecosystem && (ecosystem === "Polkadot" || ecosystem === "Ethereum")) {
+ ecosystemLogoName = `logo-${ecosystem.toLowerCase()}`
+ }
- return (
-
-
-
- {/* Sponsor Image */}
- {sponsorUrl ? (
+ return (
+
+
+
+ {/* Reward pool amount */}
+
+
+ {/* Contest tile footer */}
+
- )
+
+
+
+ )
}
\ No newline at end of file
diff --git a/src/lib/Icon/iconList.tsx b/src/lib/Icon/iconList.tsx
index 615b7ed0..a1ec991b 100644
--- a/src/lib/Icon/iconList.tsx
+++ b/src/lib/Icon/iconList.tsx
@@ -13,6 +13,10 @@ export const icons = (size: string, color: string, className?: string): Record
,
+ "bell": ,
"book": ,
+ "eye-closed": ,
+ "eye-open": ,
"findings": ,
+ "grab-vertical": ,
"heart": ,
+ "logo-ethereum": ,
+ "logo-polkadot": ,
"menu":