Skip to content

Commit 369f4e2

Browse files
TechQueryCopilot
andcommitted
[refactor] use Time & Time Range components to render all Date Time texts
Co-authored-by: Copilot <copilot@github.com>
1 parent 5924dde commit 369f4e2

9 files changed

Lines changed: 156 additions & 84 deletions

File tree

components/Activity/Hackathon/ActionHub.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { FC, PropsWithChildren } from 'react';
1+
import type { FC, PropsWithChildren, ReactNode } from 'react';
22
import { Col, Container, Row } from 'react-bootstrap';
33

44
import { HackathonHeroAction } from './Hero';
@@ -14,7 +14,7 @@ export interface HackathonActionHubEntry {
1414

1515
export interface HackathonActionHubProps {
1616
entries: HackathonActionHubEntry[];
17-
facts: string[];
17+
facts: ReactNode[];
1818
primaryAction?: HackathonHeroAction;
1919
primaryDescription: string;
2020
primaryTitle: string;
@@ -90,8 +90,8 @@ export const HackathonActionHub: FC<PropsWithChildren<HackathonActionHubProps>>
9090
</nav>
9191

9292
<ul className={`list-unstyled ${styles.regFacts} d-flex flex-wrap gap-2 mt-4`}>
93-
{facts.map(fact => (
94-
<li key={fact}>{fact}</li>
93+
{facts.map((fact, index) => (
94+
<li key={index}>{fact}</li>
9595
))}
9696
</ul>
9797
</div>

components/Activity/Hackathon/Hero.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { TableCellValue } from 'mobx-lark';
22
import dynamic from 'next/dynamic';
3-
import { FC } from 'react';
3+
import { FC, ReactNode } from 'react';
44
import { Container } from 'react-bootstrap';
55
import { TimeUnit } from 'idea-react';
66

@@ -28,9 +28,9 @@ export interface HackathonHeroProps extends Record<
2828
string
2929
> {
3030
agendaItems: Agenda[];
31-
badges: string[];
31+
badges: ReactNode[];
3232
bottomCard?: HackathonHeroCard;
33-
chips?: string[];
33+
chips?: ReactNode[];
3434
countdownUnits: TimeUnit[];
3535
endTime?: TableCellValue;
3636
image?: TableCellValue;
@@ -150,7 +150,7 @@ export const HackathonHero: FC<HackathonHeroProps> = ({
150150
<ul className={`list-unstyled ${styles.heroEyebrow} d-flex flex-wrap gap-3 m-0`}>
151151
{badges.map((badge, index) => (
152152
<li
153-
key={`${badge}-${index}`}
153+
key={index}
154154
className={`${styles.heroBadge} d-inline-flex align-items-center ${BadgeToneClass[index % BadgeToneClass.length]}`}
155155
>
156156
{badge}
@@ -182,8 +182,8 @@ export const HackathonHero: FC<HackathonHeroProps> = ({
182182
<ul
183183
className={`list-unstyled ${styles.heroStats} d-flex flex-wrap gap-2 gap-md-3 m-0`}
184184
>
185-
{chips.map(chip => (
186-
<li key={chip} className={`${styles.statChip} d-inline-flex align-items-center`}>
185+
{chips.map((chip, index) => (
186+
<li key={index} className={`${styles.statChip} d-inline-flex align-items-center`}>
187187
{chip}
188188
</li>
189189
))}

components/Activity/Hackathon/Schedule.tsx

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { FC } from 'react';
1+
import { FC, ReactNode } from 'react';
22
import { Container } from 'react-bootstrap';
3+
import { TimeData } from 'web-utility';
34

5+
import { TimeRange } from '../../TimeRange';
46
import type { HackathonAwardsMeta } from './Awards';
57
import styles from './Schedule.module.less';
68

@@ -16,11 +18,13 @@ export interface HackathonScheduleFact extends HackathonAwardsMeta {
1618
}
1719

1820
export interface HackathonScheduleItem extends Record<
19-
'id' | 'phase' | 'title' | 'dateText' | 'description',
21+
'id' | 'phase' | 'title' | 'description',
2022
string
2123
> {
2224
facts: HackathonScheduleFact[];
2325

26+
endedAt?: TimeData;
27+
startedAt?: TimeData;
2428
stageGoal?: string;
2529
tone: HackathonScheduleTone;
2630
}
@@ -30,16 +34,21 @@ export interface HackathonScheduleProps extends Record<
3034
string
3135
> {
3236
items: HackathonScheduleItem[];
33-
keyDates?: Record<'date' | 'label', string>[];
34-
overviewPills: string[];
37+
keyDates?: {
38+
label: string;
39+
startedAt?: TimeData;
40+
endedAt?: TimeData;
41+
}[];
42+
overviewPills: ReactNode[];
3543
}
3644

3745
const ScheduleCard: FC<HackathonScheduleItem & Record<'phaseLabel' | 'stageGoalLabel', string>> = ({
38-
dateText,
3946
description,
47+
endedAt,
4048
facts,
4149
phase,
4250
phaseLabel,
51+
startedAt,
4352
stageGoal,
4453
stageGoalLabel,
4554
title,
@@ -52,7 +61,9 @@ const ScheduleCard: FC<HackathonScheduleItem & Record<'phaseLabel' | 'stageGoalL
5261
<span className={`${styles.dayNo} d-inline-flex align-items-center`}>
5362
{phaseLabel} {phase}
5463
</span>
55-
<time className={styles.dayDate}>{dateText}</time>
64+
<span className={styles.dayDate}>
65+
<TimeRange start={startedAt} end={endedAt} />
66+
</span>
5667
</div>
5768

5869
<h3 className={`${styles.dayTitle} mt-3 mb-2`}>{title}</h3>
@@ -108,8 +119,8 @@ export const HackathonSchedule: FC<HackathonScheduleProps & { phaseLabel: string
108119
<ul
109120
className={`list-unstyled ${styles.scheduleOverview} d-flex flex-wrap justify-content-center gap-3 mb-4`}
110121
>
111-
{overviewPills.map(pill => (
112-
<li key={pill} className={styles.schedulePill}>
122+
{overviewPills.map((pill, index) => (
123+
<li key={index} className={styles.schedulePill}>
113124
{pill}
114125
</li>
115126
))}
@@ -128,12 +139,14 @@ export const HackathonSchedule: FC<HackathonScheduleProps & { phaseLabel: string
128139

129140
{keyDates?.[0] && (
130141
<ul className={`list-unstyled ${styles.keyDates} mt-4`}>
131-
{keyDates.map(({ date, label }, index) => (
142+
{keyDates.map(({ startedAt, endedAt, label }, index) => (
132143
<li
133-
key={`${date}-${label}`}
144+
key={`${startedAt || ''}-${endedAt || ''}-${label}`}
134145
className={`${styles.keyDateCard} d-grid gap-1 ${index % 2 ? styles.keyDateCardWarn : ''}`}
135146
>
136-
<strong className={styles.keyDateValue}>{date}</strong>
147+
<strong className={styles.keyDateValue}>
148+
<TimeRange start={startedAt} end={endedAt} format="MM-DD" />
149+
</strong>
137150
<span className={styles.keyDateLabel}>{label}</span>
138151
</li>
139152
))}

components/Activity/Hackathon/constant.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
formatPeriod,
1313
normalizeAgendaType,
1414
previewText,
15+
timeOf,
1516
} from './utility';
1617

1718
export const RequiredTableKeys = [
@@ -194,13 +195,16 @@ export const buildScheduleItems = (
194195
const description = compactSummaryOf((summary as string) || reasonText, reasonText, 120);
195196
const windowValue = formatPeriod(startedAt, endedAt) || '-';
196197
const focusValue = compactSummaryOf((summary as string) || focusText, focusText, 92);
198+
const startedAtTime = timeOf(startedAt);
199+
const endedAtTime = timeOf(endedAt);
197200

198201
return {
199202
id: id as string,
200203
phase: String(index + 1).padStart(2, '0'),
201-
dateText: formatPeriod(startedAt, endedAt),
202204
title: name as string,
203205
description,
206+
startedAt: Number.isFinite(startedAtTime) ? startedAtTime : undefined,
207+
endedAt: Number.isFinite(endedAtTime) ? endedAtTime : undefined,
204208
stageGoal: stageGoalText,
205209
tone: agendaToneClassOf(type, index),
206210
facts: [

components/Activity/ProductCard.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { observer } from 'mobx-react';
22
import { FilePreview } from 'mobx-restful-table';
33
import { FC } from 'react';
44
import { CardProps, Card, Button } from 'react-bootstrap';
5-
import { formatDate } from 'web-utility';
5+
import { Time } from 'idea-react';
66

77
import { Product } from '../../models/Hackathon';
88
import styles from '../../styles/Hackathon.module.less';
@@ -68,13 +68,9 @@ export const ProductCard: FC<ProductCardProps> = observer(
6868
</div>
6969
)}
7070

71-
<time
72-
suppressHydrationWarning
73-
className="text-dark opacity-75 small"
74-
dateTime={new Date(createdAt as number).toJSON()}
75-
>
76-
📅 {formatDate(createdAt as number)}
77-
</time>
71+
<span className="text-dark opacity-75 small">
72+
📅 <Time dateTime={createdAt as number} format="YYYY-MM-DD" />
73+
</span>
7874
</Card.Body>
7975
</Card>
8076
),

components/Finance/FundCard.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { FC, useContext, useMemo } from 'react';
22
import { Badge, Card } from 'react-bootstrap';
3+
import { Time } from 'idea-react';
34

45
import { INDEX_CATEGORY_LABEL_KEYS, INDEX_RISK_LABEL_KEYS } from '../../constants/finance';
56
import { I18nContext } from '../../models/Translation';
@@ -34,8 +35,6 @@ export const FundCard: FC<IndexFundSnapshot> = ({
3435
}) => {
3536
const { currentLanguage, t } = useContext(I18nContext);
3637
const sparklineId = useMemo(() => `fund-${symbol}`, [symbol]);
37-
const updatedAtISO = updatedAt ? new Date(updatedAt).toJSON() : undefined;
38-
3938
return (
4039
<Card className={`${styles.fundCard} h-100`}>
4140
<Card.Body className="d-flex flex-column gap-3">
@@ -96,9 +95,10 @@ export const FundCard: FC<IndexFundSnapshot> = ({
9695
))}
9796
</ul>
9897

99-
<time className="small text-muted" dateTime={updatedAtISO}>
100-
{t('updated_at')} {updatedAt || '--'}
101-
</time>
98+
<p className="small text-muted mb-0">
99+
{t('updated_at')}{' '}
100+
{updatedAt ? <Time dateTime={updatedAt} format="YYYY-MM-DD HH:mm:ss" /> : '--'}
101+
</p>
102102

103103
<a href={`/finance/${symbol}`} className="text-primary fw-semibold">
104104
{t('view_details')}

components/Git/Issue/Card.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Icon, Nameplate, text2color } from 'idea-react';
1+
import { Icon, Nameplate, Time, text2color } from 'idea-react';
22
import { marked } from 'marked';
33
import { Issue } from 'mobx-github';
44
import { FC } from 'react';
@@ -21,10 +21,7 @@ export const IssueCard: FC<IssueCardProps> = ({
2121
...props
2222
}) => (
2323
<Card {...{ ...props, bg, text }}>
24-
<Card.Header
25-
as="h4"
26-
className="d-flex justify-content-between align-items-center gap-3"
27-
>
24+
<Card.Header as="h4" className="d-flex justify-content-between align-items-center gap-3">
2825
<a
2926
className="text-decoration-none text-secondary text-truncate"
3027
title={title}
@@ -56,10 +53,7 @@ export const IssueCard: FC<IssueCardProps> = ({
5653
)}
5754
</Stack>
5855
</Card.Header>
59-
<Card.Body
60-
as="article"
61-
dangerouslySetInnerHTML={{ __html: marked(body || '') as string }}
62-
/>
56+
<Card.Body as="article" dangerouslySetInnerHTML={{ __html: marked(body || '') as string }} />
6357
<Card.Footer className="d-flex justify-content-between align-items-center">
6458
{user && <Nameplate name={user.name || ''} avatar={user.avatar_url} />}
6559

@@ -68,7 +62,7 @@ export const IssueCard: FC<IssueCardProps> = ({
6862
{comments}
6963
</Stack>
7064

71-
<time dateTime={created_at}>{new Date(created_at).toLocaleString()}</time>
65+
<Time dateTime={created_at} format="YYYY-MM-DD HH:mm:ss" />
7266
</Card.Footer>
7367
</Card>
7468
);

components/TimeRange.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Time } from 'idea-react';
2+
import { FC, ReactNode } from 'react';
3+
import { TimeData } from 'web-utility';
4+
5+
const normalizeTimeData = (value?: TimeData | null) =>
6+
value != null ? (Number.isFinite(+new Date(value)) ? value : undefined) : undefined;
7+
8+
export interface TimeTextProps {
9+
value?: TimeData | null;
10+
format?: string;
11+
fallback?: ReactNode;
12+
}
13+
14+
export const TimeText: FC<TimeTextProps> = ({ value, format = 'YYYY-MM-DD', fallback }) => {
15+
const dateTime = normalizeTimeData(value);
16+
17+
return dateTime ? <Time dateTime={dateTime} format={format} /> : <>{fallback}</>;
18+
};
19+
20+
export interface TimeRangeProps {
21+
start?: TimeData | null;
22+
end?: TimeData | null;
23+
format?: string;
24+
fallback?: ReactNode;
25+
separator?: ReactNode;
26+
}
27+
28+
export const TimeRange: FC<TimeRangeProps> = ({
29+
start,
30+
end,
31+
format = 'MM-DD HH:mm',
32+
fallback,
33+
separator = ' - ',
34+
}) => {
35+
const startTime = normalizeTimeData(start);
36+
const endTime = normalizeTimeData(end);
37+
38+
return !startTime && !endTime ? (
39+
<>{fallback}</>
40+
) : (
41+
<>
42+
{startTime && <Time dateTime={startTime} format={format} />}
43+
{startTime && endTime && separator}
44+
{endTime && <Time dateTime={endTime} format={format} />}
45+
</>
46+
);
47+
};

0 commit comments

Comments
 (0)