From f62a40b821dfd802e7b4e8e66bd62f6ad567a19f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Apr 2026 19:57:11 +0000 Subject: [PATCH 1/8] Initial plan From a76172ef0a2aa39d4c1e0e5e785660880e99f22a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Apr 2026 20:09:58 +0000 Subject: [PATCH 2/8] refactor: replace countdown React hooks with MobX class approach Agent-Logs-Url: https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/sessions/c3d283c3-cef4-454d-bce8-b3bdd8c61cf8 Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com> --- components/Activity/Hackathon/Hero.tsx | 148 +++++++++++------- .../Activity/Hackathon/LiveCountdownStore.ts | 48 ++++++ .../Hackathon/useLiveCountdownState.ts | 44 ------ pages/hackathon/[id].tsx | 66 ++++---- 4 files changed, 173 insertions(+), 133 deletions(-) create mode 100644 components/Activity/Hackathon/LiveCountdownStore.ts delete mode 100644 components/Activity/Hackathon/useLiveCountdownState.ts diff --git a/components/Activity/Hackathon/Hero.tsx b/components/Activity/Hackathon/Hero.tsx index 855aee7..3db02f9 100644 --- a/components/Activity/Hackathon/Hero.tsx +++ b/components/Activity/Hackathon/Hero.tsx @@ -1,5 +1,7 @@ +import { computed, observable } from 'mobx'; import { TableCellValue } from 'mobx-lark'; -import { FC, useEffect, useMemo, useState } from 'react'; +import { observer } from 'mobx-react'; +import { Component, FC } from 'react'; import { Container } from 'react-bootstrap'; import { LarkImage } from '../../LarkImage'; @@ -74,77 +76,106 @@ const FloatingCard: FC<{ ); -const useCountdown = (countdownTo?: string) => { - const target = useMemo(() => { - const value = countdownTo ? new Date(countdownTo).getTime() : NaN; +const splitHeroTitle = (name: string, subtitle: string) => { + const segments = name.split(/\s+/).filter(Boolean); - return Number.isFinite(value) ? value : NaN; - }, [countdownTo]); - const [now, setNow] = useState(null); + if (segments.length < 3) + return { + primary: name, + secondary: subtitle, + }; - useEffect(() => { - if (!Number.isFinite(target)) return; + return { + primary: segments.slice(0, Math.max(1, segments.length - 2)).join(' '), + secondary: segments.slice(-2).join(' '), + }; +}; + +@observer +export class HackathonHero extends Component { + @observable + accessor rest: number | null = null; - setNow(Date.now()); + private timer?: number; + + private get target() { + const { countdownTo } = this.props; + const value = countdownTo ? new Date(countdownTo).getTime() : NaN; - const timer = window.setInterval(() => setNow(Date.now()), 1000); + return Number.isFinite(value) ? value : NaN; + } - return () => window.clearInterval(timer); - }, [target]); + @computed + get countdown(): string[] { + const { rest } = this; - return useMemo(() => { - if (!Number.isFinite(target) || now === null) return ['--', '--', '--', '--']; + if (rest === null) return ['--', '--', '--', '--']; - const rest = Math.max(0, target - now); - const totalSeconds = Math.floor(rest / 1000); + const totalSeconds = Math.floor(Math.max(0, rest) / 1000); const days = Math.floor(totalSeconds / 86400); const hours = Math.floor((totalSeconds % 86400) / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; return [days, hours, minutes, seconds].map(value => String(value).padStart(2, '0')); - }, [now, target]); -}; - -const splitHeroTitle = (name: string, subtitle: string) => { - const segments = name.split(/\s+/).filter(Boolean); + } - if (segments.length < 3) - return { - primary: name, - secondary: subtitle, - }; - - return { - primary: segments.slice(0, Math.max(1, segments.length - 2)).join(' '), - secondary: segments.slice(-2).join(' '), + tick = () => { + this.rest = Math.max(0, this.target - Date.now()); }; -}; -export const HackathonHero: FC = ({ - badges, - bottomCard, - chips, - countdownLabel, - countdownUnitLabels, - countdownTo, - description, - image, - imageFallback, - locationText, - name, - navigation, - primaryAction, - secondaryAction, - subtitle, - topCard, - visualChip, - visualCopy, - visualKicker, - visualTitle, -}) => { - const countdown = useCountdown(countdownTo); - const title = splitHeroTitle(name, subtitle); + componentDidMount() { + if (Number.isFinite(this.target)) { + this.tick(); + this.timer = window.setInterval(this.tick, 1000); + } + } + + componentDidUpdate(prevProps: HackathonHeroProps) { + if (prevProps.countdownTo !== this.props.countdownTo) { + if (this.timer) { + window.clearInterval(this.timer); + this.timer = undefined; + } + + this.rest = null; + + if (Number.isFinite(this.target)) { + this.tick(); + this.timer = window.setInterval(this.tick, 1000); + } + } + } + + componentWillUnmount() { + if (this.timer) window.clearInterval(this.timer); + } + + render() { + const { + badges, + bottomCard, + chips, + countdownLabel, + countdownUnitLabels, + countdownTo, + description, + image, + imageFallback, + locationText, + name, + navigation, + primaryAction, + secondaryAction, + subtitle, + topCard, + visualChip, + visualCopy, + visualKicker, + visualTitle, + } = this.props; + const { countdown } = this; + const title = splitHeroTitle(name, subtitle); return (
@@ -264,5 +295,6 @@ export const HackathonHero: FC = ({
- ); -}; + ); + } +} diff --git a/components/Activity/Hackathon/LiveCountdownStore.ts b/components/Activity/Hackathon/LiveCountdownStore.ts new file mode 100644 index 0000000..27ef64b --- /dev/null +++ b/components/Activity/Hackathon/LiveCountdownStore.ts @@ -0,0 +1,48 @@ +import { computed, observable } from 'mobx'; +import { TableCellValue } from 'mobx-lark'; + +import { CountdownWindow, firstTextOf, resolveCountdownState, timeOf } from './utility'; + +// Maximum safe delay for setTimeout (max 32-bit signed integer in ms) +const MAX_TIMEOUT_DELAY = 2_147_483_647; + +export class LiveCountdownStore { + constructor( + readonly items: T[], + readonly startTime?: TableCellValue, + readonly endTime?: TableCellValue, + ) {} + + @observable + accessor referenceTime: number | null = null; + + @computed + get countdownState() { + const { referenceTime, items, startTime, endTime } = this; + + return referenceTime === null + ? { + nextItem: undefined as T | undefined, + countdownTo: firstTextOf(startTime) || firstTextOf(endTime) || undefined, + } + : resolveCountdownState(items, referenceTime, startTime, endTime); + } + + private timer?: number; + + tick = () => { + this.referenceTime = Date.now(); + + const targetTime = timeOf(this.countdownState.countdownTo); + + if (!Number.isFinite(targetTime)) return; + + const delay = Math.min(MAX_TIMEOUT_DELAY, Math.max(1000, targetTime - Date.now() + 1000)); + + this.timer = window.setTimeout(this.tick, delay); + }; + + dispose() { + window.clearTimeout(this.timer); + } +} diff --git a/components/Activity/Hackathon/useLiveCountdownState.ts b/components/Activity/Hackathon/useLiveCountdownState.ts deleted file mode 100644 index 09850c6..0000000 --- a/components/Activity/Hackathon/useLiveCountdownState.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { TableCellValue } from 'mobx-lark'; -import { useCallback, useEffect, useMemo, useState } from 'react'; - -import { CountdownWindow, firstTextOf, resolveCountdownState, timeOf } from './utility'; - -export const useLiveCountdownState = ( - items: T[], - startTime?: TableCellValue, - endTime?: TableCellValue, -) => { - const [referenceTime, setReferenceTime] = useState(null); - const refreshReferenceTime = useCallback(() => setReferenceTime(Date.now()), []); - - useEffect(() => refreshReferenceTime(), [refreshReferenceTime]); - - const countdownState = useMemo( - () => - referenceTime === null - ? { - nextItem: undefined as T | undefined, - countdownTo: firstTextOf(startTime) || firstTextOf(endTime) || undefined, - } - : resolveCountdownState(items, referenceTime, startTime, endTime), - [endTime, items, referenceTime, startTime], - ); - - useEffect(() => { - if (referenceTime === null) return; - - const targetTime = timeOf(countdownState.countdownTo); - - if (!Number.isFinite(targetTime)) return; - - const delay = Math.min(2_147_483_647, Math.max(1000, targetTime - Date.now() + 1000)); - const timer = window.setTimeout( - refreshReferenceTime, - delay, - ); - - return () => window.clearTimeout(timer); - }, [countdownState.countdownTo, referenceTime, refreshReferenceTime]); - - return countdownState; -}; diff --git a/pages/hackathon/[id].tsx b/pages/hackathon/[id].tsx index df679ed..18e2396 100644 --- a/pages/hackathon/[id].tsx +++ b/pages/hackathon/[id].tsx @@ -1,40 +1,17 @@ import { TableCellLocation, TableFormView } from 'mobx-lark'; import { observer } from 'mobx-react'; import { cache, compose, errorLogger } from 'next-ssr-middleware'; -import { FC, useContext } from 'react'; +import { FC, useContext, useEffect, useState } from 'react'; import { HackathonActionHub, HackathonActionHubLink, } from '../../components/Activity/Hackathon/ActionHub'; import { HackathonAwards } from '../../components/Activity/Hackathon/Awards'; -import { HackathonFAQ } from '../../components/Activity/Hackathon/FAQ'; -import { HackathonHero } from '../../components/Activity/Hackathon/Hero'; -import { HackathonOverview } from '../../components/Activity/Hackathon/Overview'; -import { HackathonParticipants } from '../../components/Activity/Hackathon/Participants'; -import { HackathonResources } from '../../components/Activity/Hackathon/Resources'; -import { HackathonSchedule } from '../../components/Activity/Hackathon/Schedule'; -import { useLiveCountdownState } from '../../components/Activity/Hackathon/useLiveCountdownState'; -import { PageHead } from '../../components/Layout/PageHead'; -import { Activity, ActivityModel } from '../../models/Activity'; -import { - Agenda, - AgendaModel, - Organization, - OrganizationModel, - Person, - PersonModel, - Prize, - PrizeModel, - Project, - ProjectModel, - Template, - TemplateModel, -} from '../../models/Hackathon'; -import { I18nContext } from '../../models/Translation'; import { buildCountdownUnitLabels, buildFAQItems, + buildFormSectionMeta, buildHighlightCards, buildJudgingCriteria, buildOrganizationItems, @@ -46,10 +23,16 @@ import { FormButtonBar, FormGroupKey, FormGroupView, - buildFormSectionMeta, heroNavigation, RequiredTableKeys, } from '../../components/Activity/Hackathon/constant'; +import { HackathonFAQ } from '../../components/Activity/Hackathon/FAQ'; +import { HackathonHero } from '../../components/Activity/Hackathon/Hero'; +import { LiveCountdownStore } from '../../components/Activity/Hackathon/LiveCountdownStore'; +import { HackathonOverview } from '../../components/Activity/Hackathon/Overview'; +import { HackathonParticipants } from '../../components/Activity/Hackathon/Participants'; +import { HackathonResources } from '../../components/Activity/Hackathon/Resources'; +import { HackathonSchedule } from '../../components/Activity/Hackathon/Schedule'; import { agendaTypeLabelOf, compactDateKeyOf, @@ -63,6 +46,23 @@ import { previewText, timeOf, } from '../../components/Activity/Hackathon/utility'; +import { PageHead } from '../../components/Layout/PageHead'; +import { Activity, ActivityModel } from '../../models/Activity'; +import { + Agenda, + AgendaModel, + Organization, + OrganizationModel, + Person, + PersonModel, + Prize, + PrizeModel, + Project, + ProjectModel, + Template, + TemplateModel, +} from '../../models/Hackathon'; +import { I18nContext } from '../../models/Translation'; interface HackathonDetailProps { activity: Activity; @@ -189,11 +189,15 @@ const HackathonDetail: FC = observer(({ activity, hackatho }; }) .filter(({ date, label }) => Boolean(date && label)); - const { nextItem: nextAgendaItem, countdownTo } = useLiveCountdownState( - agendaItems, - startTime, - endTime, - ); + const [countdownStore] = useState(() => new LiveCountdownStore(agendaItems, startTime, endTime)); + + useEffect(() => { + countdownStore.tick(); + + return () => countdownStore.dispose(); + }, [countdownStore]); + + const { nextItem: nextAgendaItem, countdownTo } = countdownStore.countdownState; const countdownLabel = nextAgendaItem ? agendaTypeLabelOf(nextAgendaItem.type, t, t('agenda')) : t('event_duration'); From ac10cfb849d4486c62f3aa3c63e19dc26658882a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Apr 2026 20:28:23 +0000 Subject: [PATCH 3/8] refactor: extract Countdown as independent class component, simplify Hero and [id].tsx Agent-Logs-Url: https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/sessions/7cd3ee7e-073a-494f-b0a1-a5eca022d3e2 Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com> --- components/Activity/Hackathon/Countdown.tsx | 95 ++++++++++++ components/Activity/Hackathon/Hero.tsx | 140 ++++-------------- .../Activity/Hackathon/LiveCountdownStore.ts | 48 ------ pages/hackathon/[id].tsx | 19 +-- 4 files changed, 135 insertions(+), 167 deletions(-) create mode 100644 components/Activity/Hackathon/Countdown.tsx delete mode 100644 components/Activity/Hackathon/LiveCountdownStore.ts diff --git a/components/Activity/Hackathon/Countdown.tsx b/components/Activity/Hackathon/Countdown.tsx new file mode 100644 index 0000000..3811b62 --- /dev/null +++ b/components/Activity/Hackathon/Countdown.tsx @@ -0,0 +1,95 @@ +import { computed, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import { Component } from 'react'; + +import styles from './Hero.module.less'; + +export interface CountdownProps { + countdownTo?: string; + label?: string; + unitLabels: string[]; +} + +@observer +export class Countdown extends Component { + @observable + accessor rest: number | null = null; + + private timer?: number; + + private get target() { + const { countdownTo } = this.props; + const value = countdownTo ? new Date(countdownTo).getTime() : NaN; + + return Number.isFinite(value) ? value : NaN; + } + + @computed + get sections() { + const { rest } = this; + + if (rest === null) return ['--', '--', '--', '--']; + + const totalSeconds = Math.floor(Math.max(0, rest) / 1000); + const days = Math.floor(totalSeconds / 86400); + const hours = Math.floor((totalSeconds % 86400) / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const seconds = totalSeconds % 60; + + return [days, hours, minutes, seconds].map(value => String(value).padStart(2, '0')); + } + + tick = () => { + this.rest = Math.max(0, this.target - Date.now()); + }; + + componentDidMount() { + if (Number.isFinite(this.target)) { + this.tick(); + this.timer = window.setInterval(this.tick, 1000); + } + } + + componentDidUpdate(prevProps: CountdownProps) { + if (prevProps.countdownTo !== this.props.countdownTo) { + if (this.timer) { + window.clearInterval(this.timer); + this.timer = undefined; + } + + this.rest = null; + + if (Number.isFinite(this.target)) { + this.tick(); + this.timer = window.setInterval(this.tick, 1000); + } + } + } + + componentWillUnmount() { + if (this.timer) window.clearInterval(this.timer); + } + + render() { + const { label, unitLabels } = this.props; + const { sections } = this; + + return ( +
+ {label &&

{label}

} + +
    + {sections.map((value, index) => ( +
  1. + {value} + {unitLabels[index]} +
  2. + ))} +
+
+ ); + } +} diff --git a/components/Activity/Hackathon/Hero.tsx b/components/Activity/Hackathon/Hero.tsx index 3db02f9..28a31d2 100644 --- a/components/Activity/Hackathon/Hero.tsx +++ b/components/Activity/Hackathon/Hero.tsx @@ -1,10 +1,9 @@ -import { computed, observable } from 'mobx'; import { TableCellValue } from 'mobx-lark'; -import { observer } from 'mobx-react'; -import { Component, FC } from 'react'; +import { FC } from 'react'; import { Container } from 'react-bootstrap'; import { LarkImage } from '../../LarkImage'; +import { Countdown } from './Countdown'; import styles from './Hero.module.less'; export type HackathonHeroNavItem = Record<'label' | 'href', string>; @@ -91,91 +90,29 @@ const splitHeroTitle = (name: string, subtitle: string) => { }; }; -@observer -export class HackathonHero extends Component { - @observable - accessor rest: number | null = null; - - private timer?: number; - - private get target() { - const { countdownTo } = this.props; - const value = countdownTo ? new Date(countdownTo).getTime() : NaN; - - return Number.isFinite(value) ? value : NaN; - } - - @computed - get countdown(): string[] { - const { rest } = this; - - if (rest === null) return ['--', '--', '--', '--']; - - const totalSeconds = Math.floor(Math.max(0, rest) / 1000); - const days = Math.floor(totalSeconds / 86400); - const hours = Math.floor((totalSeconds % 86400) / 3600); - const minutes = Math.floor((totalSeconds % 3600) / 60); - const seconds = totalSeconds % 60; - - return [days, hours, minutes, seconds].map(value => String(value).padStart(2, '0')); - } - - tick = () => { - this.rest = Math.max(0, this.target - Date.now()); - }; - - componentDidMount() { - if (Number.isFinite(this.target)) { - this.tick(); - this.timer = window.setInterval(this.tick, 1000); - } - } - - componentDidUpdate(prevProps: HackathonHeroProps) { - if (prevProps.countdownTo !== this.props.countdownTo) { - if (this.timer) { - window.clearInterval(this.timer); - this.timer = undefined; - } - - this.rest = null; - - if (Number.isFinite(this.target)) { - this.tick(); - this.timer = window.setInterval(this.tick, 1000); - } - } - } - - componentWillUnmount() { - if (this.timer) window.clearInterval(this.timer); - } - - render() { - const { - badges, - bottomCard, - chips, - countdownLabel, - countdownUnitLabels, - countdownTo, - description, - image, - imageFallback, - locationText, - name, - navigation, - primaryAction, - secondaryAction, - subtitle, - topCard, - visualChip, - visualCopy, - visualKicker, - visualTitle, - } = this.props; - const { countdown } = this; - const title = splitHeroTitle(name, subtitle); +export const HackathonHero: FC = ({ + badges, + bottomCard, + chips, + countdownLabel, + countdownUnitLabels, + countdownTo, + description, + image, + imageFallback, + locationText, + name, + navigation, + primaryAction, + secondaryAction, + subtitle, + topCard, + visualChip, + visualCopy, + visualKicker, + visualTitle, +}) => { + const title = splitHeroTitle(name, subtitle); return (
@@ -224,23 +161,11 @@ export class HackathonHero extends Component {

{description}

{countdownTo && ( -
- {countdownLabel && ( -

{countdownLabel}

- )} - -
    - {countdown.map((value, index) => ( -
  1. - {value} - {countdownUnitLabels[index]} -
  2. - ))} -
-
+ )}
- ); - } -} + ); +}; diff --git a/components/Activity/Hackathon/LiveCountdownStore.ts b/components/Activity/Hackathon/LiveCountdownStore.ts deleted file mode 100644 index 27ef64b..0000000 --- a/components/Activity/Hackathon/LiveCountdownStore.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { computed, observable } from 'mobx'; -import { TableCellValue } from 'mobx-lark'; - -import { CountdownWindow, firstTextOf, resolveCountdownState, timeOf } from './utility'; - -// Maximum safe delay for setTimeout (max 32-bit signed integer in ms) -const MAX_TIMEOUT_DELAY = 2_147_483_647; - -export class LiveCountdownStore { - constructor( - readonly items: T[], - readonly startTime?: TableCellValue, - readonly endTime?: TableCellValue, - ) {} - - @observable - accessor referenceTime: number | null = null; - - @computed - get countdownState() { - const { referenceTime, items, startTime, endTime } = this; - - return referenceTime === null - ? { - nextItem: undefined as T | undefined, - countdownTo: firstTextOf(startTime) || firstTextOf(endTime) || undefined, - } - : resolveCountdownState(items, referenceTime, startTime, endTime); - } - - private timer?: number; - - tick = () => { - this.referenceTime = Date.now(); - - const targetTime = timeOf(this.countdownState.countdownTo); - - if (!Number.isFinite(targetTime)) return; - - const delay = Math.min(MAX_TIMEOUT_DELAY, Math.max(1000, targetTime - Date.now() + 1000)); - - this.timer = window.setTimeout(this.tick, delay); - }; - - dispose() { - window.clearTimeout(this.timer); - } -} diff --git a/pages/hackathon/[id].tsx b/pages/hackathon/[id].tsx index 18e2396..bc060e1 100644 --- a/pages/hackathon/[id].tsx +++ b/pages/hackathon/[id].tsx @@ -1,7 +1,7 @@ import { TableCellLocation, TableFormView } from 'mobx-lark'; import { observer } from 'mobx-react'; import { cache, compose, errorLogger } from 'next-ssr-middleware'; -import { FC, useContext, useEffect, useState } from 'react'; +import { FC, useContext } from 'react'; import { HackathonActionHub, @@ -28,7 +28,6 @@ import { } from '../../components/Activity/Hackathon/constant'; import { HackathonFAQ } from '../../components/Activity/Hackathon/FAQ'; import { HackathonHero } from '../../components/Activity/Hackathon/Hero'; -import { LiveCountdownStore } from '../../components/Activity/Hackathon/LiveCountdownStore'; import { HackathonOverview } from '../../components/Activity/Hackathon/Overview'; import { HackathonParticipants } from '../../components/Activity/Hackathon/Participants'; import { HackathonResources } from '../../components/Activity/Hackathon/Resources'; @@ -44,6 +43,7 @@ import { isPublicForm, normalizeAgendaType, previewText, + resolveCountdownState, timeOf, } from '../../components/Activity/Hackathon/utility'; import { PageHead } from '../../components/Layout/PageHead'; @@ -189,15 +189,12 @@ const HackathonDetail: FC = observer(({ activity, hackatho }; }) .filter(({ date, label }) => Boolean(date && label)); - const [countdownStore] = useState(() => new LiveCountdownStore(agendaItems, startTime, endTime)); - - useEffect(() => { - countdownStore.tick(); - - return () => countdownStore.dispose(); - }, [countdownStore]); - - const { nextItem: nextAgendaItem, countdownTo } = countdownStore.countdownState; + const { nextItem: nextAgendaItem, countdownTo } = resolveCountdownState( + agendaItems, + Date.now(), + startTime, + endTime, + ); const countdownLabel = nextAgendaItem ? agendaTypeLabelOf(nextAgendaItem.type, t, t('agenda')) : t('event_duration'); From 867f0cac3026ec5e0e53aa04c9ae752680f755d0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Apr 2026 20:37:35 +0000 Subject: [PATCH 4/8] refactor: remove label prop from Countdown, move label rendering to Hero Agent-Logs-Url: https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/sessions/d946deb1-3c20-4f85-afa5-f4569b09dd6a Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com> --- components/Activity/Hackathon/Countdown.tsx | 29 +++++++++------------ components/Activity/Hackathon/Hero.tsx | 11 ++++---- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/components/Activity/Hackathon/Countdown.tsx b/components/Activity/Hackathon/Countdown.tsx index 3811b62..9e8acad 100644 --- a/components/Activity/Hackathon/Countdown.tsx +++ b/components/Activity/Hackathon/Countdown.tsx @@ -6,7 +6,6 @@ import styles from './Hero.module.less'; export interface CountdownProps { countdownTo?: string; - label?: string; unitLabels: string[]; } @@ -71,25 +70,21 @@ export class Countdown extends Component { } render() { - const { label, unitLabels } = this.props; + const { unitLabels } = this.props; const { sections } = this; return ( -
- {label &&

{label}

} - -
    - {sections.map((value, index) => ( -
  1. - {value} - {unitLabels[index]} -
  2. - ))} -
-
+
    + {sections.map((value, index) => ( +
  1. + {value} + {unitLabels[index]} +
  2. + ))} +
); } } diff --git a/components/Activity/Hackathon/Hero.tsx b/components/Activity/Hackathon/Hero.tsx index 28a31d2..5599535 100644 --- a/components/Activity/Hackathon/Hero.tsx +++ b/components/Activity/Hackathon/Hero.tsx @@ -161,11 +161,12 @@ export const HackathonHero: FC = ({

{description}

{countdownTo && ( - +
+ {countdownLabel && ( +

{countdownLabel}

+ )} + +
)}