Skip to content

Commit 6d3896c

Browse files
CopilotTechQuery
andcommitted
Apply hackathon homepage styling and enhance ProductCard with cloud IDE buttons
- Imported Hackathon.module.less styles for consistent UI design - Added hero section with gradient background to project detail page - Restructured layout to use styled sections (team members, products, creator) - Applied darkCard style to member cards with purple gradient - Applied lightCard style to creator section with warm gradient - Updated ProductCard to render null when no sourceLink - Added multiple cloud IDE buttons (GitHub, GitHub.dev, Codespaces, GitPod) - Removed tabs layout in favor of sectioned content display - Enhanced visual consistency with main hackathon page Co-authored-by: TechQuery <[email protected]>
1 parent 7a53794 commit 6d3896c

File tree

2 files changed

+179
-106
lines changed

2 files changed

+179
-106
lines changed

components/Activity/ProductCard.tsx

Lines changed: 84 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,89 @@ import { Product } from '../../models/Hackathon';
99
export type ProductCardProps = Product & Omit<CardProps, 'id' | 'title'>;
1010

1111
export const ProductCard: FC<ProductCardProps> = observer(
12-
({ className = '', id, createdAt, name, sourceLink, link = sourceLink, summary, ...props }) => (
13-
<Card className={`border-success ${className}`} {...props}>
14-
<Card.Body className="d-flex flex-column">
15-
<Card.Title
16-
as="a"
17-
className="text-primary"
18-
title={name as string}
19-
target="_blank"
20-
href={link as string}
21-
>
22-
{(name || link) as string}
23-
</Card.Title>
24-
<p className="border-bottom p-2 text-muted text-truncate">{summary as string}</p>
25-
<div className="border-bottom py-2 my-2 flex-fill">
26-
<FilePreview className="w-100" path={link as string} />
12+
({ className = '', id, createdAt, name, sourceLink, link = sourceLink, summary, ...props }) => {
13+
if (!sourceLink) return null;
2714

28-
{sourceLink && (
29-
<Button variant="success" size="sm" href={sourceLink as string}>
30-
🔗 Git
31-
</Button>
32-
)}
33-
</div>
34-
<time
35-
className="d-block p-2 text-truncate"
36-
dateTime={new Date(createdAt as number).toJSON()}
37-
>
38-
📅
39-
{formatDate(createdAt as number)}
40-
</time>
41-
</Card.Body>
42-
</Card>
43-
),
15+
return (
16+
<Card className={`border-success ${className}`} {...props}>
17+
<Card.Body className="d-flex flex-column">
18+
<Card.Title
19+
as="a"
20+
className="text-primary"
21+
title={name as string}
22+
target="_blank"
23+
href={link as string}
24+
>
25+
{(name || link) as string}
26+
</Card.Title>
27+
<p className="border-bottom p-2 text-muted text-truncate">{summary as string}</p>
28+
<div className="border-bottom py-2 my-2 flex-fill">
29+
<FilePreview className="w-100" path={link as string} />
30+
31+
<div className="d-flex flex-wrap gap-2 mt-2">
32+
<Button
33+
variant="dark"
34+
size="sm"
35+
href={sourceLink as string}
36+
target="_blank"
37+
rel="noreferrer"
38+
>
39+
<img
40+
src="https://img.shields.io/badge/GitHub-181717?logo=github"
41+
alt="GitHub"
42+
style={{ height: '20px' }}
43+
/>
44+
</Button>
45+
<Button
46+
variant="primary"
47+
size="sm"
48+
href={`https://github.dev/${(sourceLink as string).replace('https://github.com/', '')}`}
49+
target="_blank"
50+
rel="noreferrer"
51+
>
52+
<img
53+
src="https://img.shields.io/badge/GitHub.dev-blue?logo=visualstudio"
54+
alt="GitHub.dev"
55+
style={{ height: '20px' }}
56+
/>
57+
</Button>
58+
<Button
59+
variant="dark"
60+
size="sm"
61+
href={`https://codespaces.new/${(sourceLink as string).replace('https://github.com/', '')}`}
62+
target="_blank"
63+
rel="noreferrer"
64+
>
65+
<img
66+
src="https://img.shields.io/badge/GitHub_codespaces-black?logo=github"
67+
alt="GitHub Codespaces"
68+
style={{ height: '20px' }}
69+
/>
70+
</Button>
71+
<Button
72+
variant="warning"
73+
size="sm"
74+
href={`https://gitpod.io/#${sourceLink as string}`}
75+
target="_blank"
76+
rel="noreferrer"
77+
>
78+
<img
79+
src="https://img.shields.io/badge/GitPod.io-orange?logo=git"
80+
alt="GitPod"
81+
style={{ height: '20px' }}
82+
/>
83+
</Button>
84+
</div>
85+
</div>
86+
<time
87+
className="d-block p-2 text-truncate"
88+
dateTime={new Date(createdAt as number).toJSON()}
89+
>
90+
📅
91+
{formatDate(createdAt as number)}
92+
</time>
93+
</Card.Body>
94+
</Card>
95+
);
96+
},
4497
);

pages/hackathon/[id]/team/[tid].tsx

Lines changed: 95 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
ProjectModel,
3030
} from '../../../../models/Hackathon';
3131
import { I18nContext } from '../../../../models/Translation';
32+
import styles from '../../../../styles/Hackathon.module.less';
3233

3334
export const getServerSideProps = compose<Record<'id' | 'tid', string>>(
3435
cache(),
@@ -77,48 +78,66 @@ const ProjectPage: FC<ProjectPageProps> = observer(({ activity, project, members
7778
];
7879

7980
return (
80-
<Container as="main" className="mt-4">
81+
<>
8182
<PageHead title={`${displayName} - ${activityName}`} />
8283

83-
<Breadcrumb aria-label="breadcrumb">
84-
{currentRoute.map(({ title, href }, index, { length }) => {
85-
const isActive = index === length - 1;
86-
87-
return (
88-
<Breadcrumb.Item key={title} href={isActive ? undefined : href} active={isActive}>
89-
{title}
90-
</Breadcrumb.Item>
91-
);
92-
})}
93-
</Breadcrumb>
94-
95-
<Row className="mt-4 g-3">
96-
<Col xs={12} sm={4}>
97-
<Card>
98-
<Card.Header className="bg-white">
99-
<h1 className="h3 my-2">{displayName as string}</h1>
100-
<p className="text-muted">{description as string}</p>
101-
{score != null && (
102-
<div className="text-center mt-3">
103-
<Button variant="danger" className="fs-5" onClick={() => setShowScoreModal(true)}>
104-
{t('score')}: {score as number}
105-
</Button>
106-
</div>
107-
)}
108-
</Card.Header>
109-
<Card.Body>
110-
<h2 className="text-dark fw-bold h6 mb-3">👥 {t('team_members')}</h2>
111-
<ul className="list-unstyled">
112-
{members.map(({ id, person, githubAccount }) => (
113-
<li key={id as string} className="d-flex gap-3 align-items-center">
84+
{/* Hero Section */}
85+
<section className={styles.hero}>
86+
<Container>
87+
<Breadcrumb aria-label="breadcrumb" className="mb-4">
88+
{currentRoute.map(({ title, href }, index, { length }) => {
89+
const isActive = index === length - 1;
90+
91+
return (
92+
<Breadcrumb.Item
93+
key={title}
94+
href={isActive ? undefined : href}
95+
active={isActive}
96+
className="text-white"
97+
>
98+
{title}
99+
</Breadcrumb.Item>
100+
);
101+
})}
102+
</Breadcrumb>
103+
104+
<h1 className={`text-center ${styles.title}`}>{displayName as string}</h1>
105+
<p className={`text-center ${styles.description}`}>{description as string}</p>
106+
107+
{score != null && (
108+
<div className="text-center mt-4">
109+
<Button
110+
variant="light"
111+
size="lg"
112+
className="shadow-lg"
113+
onClick={() => setShowScoreModal(true)}
114+
>
115+
{t('score')}: <strong>{score as number}</strong>
116+
</Button>
117+
</div>
118+
)}
119+
</Container>
120+
</section>
121+
122+
<Container className="my-5">
123+
{/* Team Members Section */}
124+
<section className={styles.section}>
125+
<h2 className={styles.sectionTitle}>👥 {t('team_members')}</h2>
126+
<Row as="ul" className="list-unstyled mt-4 g-4" xs={1} md={2} lg={3} xl={4}>
127+
{members.map(({ id, person, githubAccount }) => (
128+
<Col as="li" key={id as string}>
129+
<Card className={styles.darkCard} body>
130+
<div className="d-flex gap-3 align-items-center">
114131
{/* @ts-expect-error Upstream compatibility */}
115-
<Avatar src={(person as TableCellUser).avatar_url} />
116-
<div>
117-
<h3 className="fs-6 m-0 fw-bold">{(person as TableCellUser).name}</h3>
132+
<Avatar src={(person as TableCellUser).avatar_url} size={60} />
133+
<div className="flex-grow-1">
134+
<h3 className="fs-6 m-0 fw-bold text-white">
135+
{(person as TableCellUser).name}
136+
</h3>
118137

119138
{githubAccount && (
120139
<a
121-
className="text-muted small"
140+
className="text-white-50 small"
122141
target="_blank"
123142
rel="noreferrer"
124143
href={`https://github.com/${githubAccount}`}
@@ -127,45 +146,46 @@ const ProjectPage: FC<ProjectPageProps> = observer(({ activity, project, members
127146
</a>
128147
)}
129148
</div>
130-
</li>
131-
))}
132-
</ul>
133-
</Card.Body>
149+
</div>
150+
</Card>
151+
</Col>
152+
))}
153+
</Row>
154+
</section>
155+
156+
{/* Team Products Section */}
157+
<section className={styles.section}>
158+
<h2 className={styles.sectionTitle}>💡 {t('team_works')}</h2>
159+
160+
{products && products.length > 0 ? (
161+
<Row as="ul" className="list-unstyled mt-4 g-4" xs={1} md={2} lg={3}>
162+
{products.map(product => (
163+
<Col as="li" key={product.id as string}>
164+
<ProductCard {...product} />
165+
</Col>
166+
))}
167+
</Row>
168+
) : (
169+
<div className="h1 my-5 text-center text-muted">{t('no_news_yet')}</div>
170+
)}
171+
</section>
172+
173+
{/* Creator Information Section */}
174+
<section className={styles.section}>
175+
<h2 className={styles.sectionTitle}>👤 {t('created_by')}</h2>
176+
<Card className={styles.lightCard} body>
177+
<div className="fw-bold fs-5">{(createdBy as TableCellUser).name}</div>
178+
<a
179+
href={`mailto:${(createdBy as TableCellUser).email}`}
180+
className="text-dark text-decoration-underline"
181+
>
182+
{(createdBy as TableCellUser).email}
183+
</a>
134184
</Card>
135-
</Col>
136-
<Col xs={12} sm={8}>
137-
<Tabs variant="pills" defaultActiveKey="update" id="project-detail-tabs">
138-
<Tab className="pt-2" eventKey="update" title={t('latest_news')}>
139-
<div className="h1 my-5 text-center text-muted">{t('no_news_yet')}</div>
140-
</Tab>
141-
<Tab eventKey="teamWork" title={t('team_works')} className="pt-2">
142-
{products && products.length > 0 ? (
143-
<Row as="ul" className="list-unstyled g-3" xs={2}>
144-
{products.map(product => (
145-
<Col as="li" key={product.id as string}>
146-
<ProductCard {...product} />
147-
</Col>
148-
))}
149-
</Row>
150-
) : (
151-
<div className="text-center text-muted my-5">{t('no_news_yet')}</div>
152-
)}
153-
</Tab>
154-
</Tabs>
155-
</Col>
156-
</Row>
157-
158-
<Card className="my-4" body>
159-
<h3 className="h5">{t('created_by')}</h3>
160-
<div className="mt-3">
161-
<div className="fw-bold">{(createdBy as TableCellUser).name}</div>
162-
<a href={`mailto:${(createdBy as TableCellUser).email}`} className="text-muted">
163-
{(createdBy as TableCellUser).email}
164-
</a>
165-
</div>
166-
</Card>
167-
168-
<CommentBox />
185+
</section>
186+
187+
<CommentBox />
188+
</Container>
169189

170190
<Modal size="lg" centered show={showScoreModal} onHide={() => setShowScoreModal(false)}>
171191
<Modal.Header closeButton>
@@ -181,7 +201,7 @@ const ProjectPage: FC<ProjectPageProps> = observer(({ activity, project, members
181201
</Ratio>
182202
</Modal.Body>
183203
</Modal>
184-
</Container>
204+
</>
185205
);
186206
});
187207

0 commit comments

Comments
 (0)