Skip to content

Commit bcbc5cc

Browse files
Merging production to main (#386)
* Judging dashboard (#382) * fix: modify api.ts * fix: remove google auth buttom * fix: fixes responsive fixes on organization * fix: minor fixes * fix: modify create organization * fix: modify create organization * fix: fix organization permission * fix: merge into main * feat: hackathon overview page * feat: hackathon overview page * feat: implement participant overview * feat: implement participant overview * feat: implement resources tab * feat: implement the submission tab * feat: implement comment tab * fix: implement provider for hackathon * fix: implement provider for hackathon * fix: minor fixes * fix: hackathon banner * fix: hackathon banner * fix: fix organization page * fix: fix organization page * fix: use transform * fix: add tagline * fix: add tagline * fix: minor fixes * fix: minor fixes * fix: fix timeline and prizes * fix: correct timeline events * fix: implement registration deadline policy * fix: implement registration deadline policy * feat: implement leave hackathon * feat: implement leave hackathon * fix: delete hackathon * fix: implement invite participants * fix: implement participant profile viewing * feat: fetch participants team * fix: redesign hackathon banner * fix: fix hackthon card * fix: fix search bar in blog page * fix: fix search bar in blog page * fix: fix search bar in blog page * fix: fix error in fetching team posts * feat: implement create team, get my team * feat: implement create team, get my team * feat: implement hackathon project submission flow * feat: implement voting for submission * fix: team formation updates * fix: implement team invitation * feat: hackathon submissions bulk actions, ranking and ui refinements * fix: implement empty state for hackathons page * feat: Implement submission visibility and explore submissions * feat: Implement winners tab for hackathon winners display * feat: implement hackathon analytics * fix: fix coderabbit corrections * fix: fix coderabbit corrections * fix: fix coderabbit corrections * fix: fix organization settings data persistence, hackathondrafts and population * feat: fix user profile * feat: Implemented the participant-facing and hackathon organizers announcement features * feat: Implement judging dashboard and detailed breakdowns * feat: Implement judging dashboard and detailed breakdowns * fix: fix coderabbit corrections * Refactor/updated the timeline tab (#383) * feat: enhance hackathon timeline management and publishing flow - Updated HackathonPublishedModal to utilize new publish response structure. - Refactored TimelineSection to display additional timeline details including judging start/end and winner announcement dates. - Introduced DateTimeInput component for better date and time selection. - Added FieldLabel component for improved form labeling with tooltips. - Created timelineConstants for standardized tooltips and timezone options. - Modified timelineSchema to include new fields for judging start, end, and winner announcement dates with validation. - Enhanced TimelineSettingsTab to support new timeline fields and improved user experience with time inputs. - Updated use-hackathon-publish hook to handle new publish response format and manage state accordingly. - Adjusted use-hackathons hook to ensure proper handling of publish responses. - Updated API interfaces and utility functions to accommodate new timeline structure. - Improved validation logic for hackathon steps to ensure all necessary dates are set correctly. - Enhanced timeline calculation logic to reflect new judging and announcement phases. * chore: update markdown-it to version 14.1.1 and remove framer-motion dependency * refactor: clean up ReviewTab and HackathonPublishedModal components, improve DateTimeInput logic, and update timeline constants * refactor: update timeline calculation logic to ensure phases are only created with non-zero duration and add endDate to HackathonTimeline interface * Judging dashboard (#384) * fix: modify api.ts * fix: remove google auth buttom * fix: fixes responsive fixes on organization * fix: minor fixes * fix: modify create organization * fix: modify create organization * fix: fix organization permission * fix: merge into main * feat: hackathon overview page * feat: hackathon overview page * feat: implement participant overview * feat: implement participant overview * feat: implement resources tab * feat: implement the submission tab * feat: implement comment tab * fix: implement provider for hackathon * fix: implement provider for hackathon * fix: minor fixes * fix: hackathon banner * fix: hackathon banner * fix: fix organization page * fix: fix organization page * fix: use transform * fix: add tagline * fix: add tagline * fix: minor fixes * fix: minor fixes * fix: fix timeline and prizes * fix: correct timeline events * fix: implement registration deadline policy * fix: implement registration deadline policy * feat: implement leave hackathon * feat: implement leave hackathon * fix: delete hackathon * fix: implement invite participants * fix: implement participant profile viewing * feat: fetch participants team * fix: redesign hackathon banner * fix: fix hackthon card * fix: fix search bar in blog page * fix: fix search bar in blog page * fix: fix search bar in blog page * fix: fix error in fetching team posts * feat: implement create team, get my team * feat: implement create team, get my team * feat: implement hackathon project submission flow * feat: implement voting for submission * fix: team formation updates * fix: implement team invitation * feat: hackathon submissions bulk actions, ranking and ui refinements * fix: implement empty state for hackathons page * feat: Implement submission visibility and explore submissions * feat: Implement winners tab for hackathon winners display * feat: implement hackathon analytics * fix: fix coderabbit corrections * fix: fix coderabbit corrections * fix: fix coderabbit corrections * fix: fix organization settings data persistence, hackathondrafts and population * feat: fix user profile * feat: Implemented the participant-facing and hackathon organizers announcement features * feat: Implement judging dashboard and detailed breakdowns * feat: Implement judging dashboard and detailed breakdowns * fix: fix coderabbit corrections * fix: submission management ux refinements * fix: submission management ux refinements * fix: submission management ux refinements * fix: submission management ux refinements * fix: submission management ux refinements * feat: implement organizer override functionality for judging scores a… (#385) * feat: implement organizer override functionality for judging scores and enhance judging UI * fix: update response handling in score submission and enhance submissions list styling * fix: update eslint dependencies to latest versions for improved linting * fix: update ajv and json-schema-traverse dependencies in package-lock.json * fix: update * fix: update security audit level to high in pre-push checks * fix: update eslint dependencies and ensure security audit fails on error --------- Co-authored-by: Collins Ikechukwu <collinschristroa@gmail.com>
1 parent 70040d4 commit bcbc5cc

75 files changed

Lines changed: 12103 additions & 3506 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.husky/pre-push

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ echo "🚀 Running pre-push checks..."
88

99
# Run security audit
1010
echo "🔒 Running security audit..."
11-
npm audit --audit-level=moderate
11+
npm audit --omit=dev --audit-level=high
1212

1313
# Run build check one more time
1414
# echo "🏗️ Final build check..."
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
'use client';
2+
3+
import React, { useState, useEffect } from 'react';
4+
import { useParams, useRouter } from 'next/navigation';
5+
import { Megaphone, ArrowLeft, Calendar, User, Pin } from 'lucide-react';
6+
import {
7+
getAnnouncementDetails,
8+
GetHackathonBySlug,
9+
type HackathonAnnouncement,
10+
} from '@/lib/api/hackathons/index';
11+
import { useMarkdown } from '@/hooks/use-markdown';
12+
import { BoundlessButton } from '@/components/buttons';
13+
import Loading from '@/components/Loading';
14+
import { Badge } from '@/components/ui/badge';
15+
16+
export default function AnnouncementDetailPage() {
17+
const params = useParams();
18+
const router = useRouter();
19+
const announcementId = params.announcementId as string;
20+
const slug = params.slug as string;
21+
22+
const [announcement, setAnnouncement] =
23+
useState<HackathonAnnouncement | null>(null);
24+
const [hackathonName, setHackathonName] = useState<string>('');
25+
const [loading, setLoading] = useState(true);
26+
const [error, setError] = useState<string | null>(null);
27+
28+
useEffect(() => {
29+
async function fetchDetails() {
30+
if (!announcementId) return;
31+
try {
32+
setLoading(true);
33+
const [announcementData, hackathonData] = await Promise.all([
34+
getAnnouncementDetails(announcementId),
35+
GetHackathonBySlug(slug),
36+
]);
37+
setAnnouncement(announcementData);
38+
setHackathonName(hackathonData.data.name);
39+
} catch (err) {
40+
console.error('Failed to fetch details:', err);
41+
setError(
42+
'Failed to load announcement. It may have been deleted or moved.'
43+
);
44+
} finally {
45+
setLoading(false);
46+
}
47+
}
48+
fetchDetails();
49+
}, [announcementId, slug]);
50+
51+
const { styledContent, loading: markdownLoading } = useMarkdown(
52+
announcement?.content || '',
53+
{
54+
breaks: true,
55+
gfm: true,
56+
}
57+
);
58+
59+
if (loading) {
60+
return (
61+
<div className='flex min-h-screen items-center justify-center bg-black'>
62+
<Loading />
63+
</div>
64+
);
65+
}
66+
67+
if (error || !announcement) {
68+
return (
69+
<div className='flex min-h-screen flex-col items-center justify-center bg-black p-6 text-center'>
70+
<div className='mb-6 flex h-20 w-20 items-center justify-center rounded-full bg-red-500/10'>
71+
<Megaphone className='h-10 w-10 text-red-500' />
72+
</div>
73+
<h1 className='mb-2 text-2xl font-bold text-white'>
74+
Announcement Not Found
75+
</h1>
76+
<p className='mb-8 text-zinc-400'>
77+
{error || "We couldn't find the announcement you're looking for."}
78+
</p>
79+
<BoundlessButton onClick={() => router.back()} variant='outline'>
80+
<ArrowLeft className='mr-2 h-4 w-4' />
81+
Go Back
82+
</BoundlessButton>
83+
</div>
84+
);
85+
}
86+
87+
return (
88+
<div className='min-h-screen bg-black pb-24'>
89+
{/* Top Header */}
90+
<div className='sticky top-0 z-10 border-b border-zinc-900 bg-black/80 backdrop-blur-md'>
91+
<div className='mx-auto max-w-4xl items-center justify-between px-6 py-4'>
92+
<button
93+
onClick={() => window.close()}
94+
className='flex items-center gap-2 text-sm text-zinc-400 transition-colors hover:text-white'
95+
>
96+
<ArrowLeft className='h-4 w-4' />
97+
Close Tab
98+
</button>
99+
<div className='flex items-center gap-2 text-zinc-500'>
100+
<Megaphone className='text-primary h-4 w-4' />
101+
<span className='text-xs font-medium tracking-widest uppercase'>
102+
{hackathonName ? `${hackathonName} • ` : ''}Announcement
103+
</span>
104+
</div>
105+
</div>
106+
</div>
107+
108+
<div className='mx-auto max-w-4xl px-6 pt-12'>
109+
{/* Title & Metadata */}
110+
<div className='mb-12 space-y-6 border-b border-zinc-900 pb-12'>
111+
<div className='flex flex-wrap items-center gap-3'>
112+
{hackathonName && (
113+
<Badge
114+
variant='outline'
115+
className='border-primary/30 text-primary bg-primary/5 font-bold tracking-tighter uppercase'
116+
>
117+
{hackathonName}
118+
</Badge>
119+
)}
120+
{announcement.isPinned && (
121+
<Badge
122+
variant='secondary'
123+
className='bg-primary/10 text-primary border-primary/20 flex items-center gap-1 tracking-tighter uppercase'
124+
>
125+
<Pin className='h-3 w-3 fill-current' />
126+
Pinned
127+
</Badge>
128+
)}
129+
<Badge
130+
variant='outline'
131+
className='tracking-tighter text-zinc-500 uppercase'
132+
>
133+
Published
134+
</Badge>
135+
</div>
136+
137+
<h1 className='text-2xl font-bold tracking-tight text-white'>
138+
{announcement.title}
139+
</h1>
140+
141+
<div className='flex flex-wrap items-center gap-6 text-sm text-zinc-400'>
142+
<div className='flex items-center gap-2'>
143+
<div className='flex h-8 w-8 items-center justify-center rounded-full bg-zinc-800 text-zinc-500 uppercase'>
144+
{announcement.author?.name?.charAt(0) || (
145+
<User className='h-4 w-4' />
146+
)}
147+
</div>
148+
<span className='font-medium text-zinc-200'>
149+
{announcement.author?.name || 'Organizer'}
150+
</span>
151+
</div>
152+
153+
<div className='flex items-center gap-2'>
154+
<Calendar className='h-4 w-4 text-zinc-600' />
155+
<span>
156+
{new Date(
157+
announcement.publishedAt || announcement.createdAt
158+
).toLocaleDateString(undefined, {
159+
weekday: 'long',
160+
month: 'long',
161+
day: 'numeric',
162+
year: 'numeric',
163+
})}
164+
</span>
165+
</div>
166+
</div>
167+
</div>
168+
169+
{/* Content */}
170+
<div className='prose prose-invert prose-primary max-w-none'>
171+
{markdownLoading ? (
172+
<div className='flex h-32 items-center justify-center'>
173+
<div className='border-primary h-6 w-6 animate-spin rounded-full border-2 border-t-transparent' />
174+
</div>
175+
) : (
176+
<div className='text-lg leading-relaxed text-zinc-200'>
177+
{styledContent}
178+
</div>
179+
)}
180+
</div>
181+
182+
{/* Footer */}
183+
<div className='mt-24 rounded-2xl border border-zinc-900 bg-zinc-950/50 p-8 text-center'>
184+
<h3 className='mb-2 font-bold text-white'>End of Announcement</h3>
185+
<p className='mb-6 text-sm text-zinc-500'>
186+
This announcement was published by the hackathon organizers.
187+
</p>
188+
<BoundlessButton
189+
onClick={() => window.close()}
190+
variant='outline'
191+
size='sm'
192+
>
193+
Return to Hackathon
194+
</BoundlessButton>
195+
</div>
196+
</div>
197+
</div>
198+
);
199+
}

app/(landing)/hackathons/[slug]/page.tsx

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ import { HackathonParticipants } from '@/components/hackathons/participants/hack
2323
import { useCommentSystem } from '@/hooks/use-comment-system';
2424
import { CommentEntityType } from '@/types/comment';
2525
import { useTeamPosts } from '@/hooks/hackathon/use-team-posts';
26+
import {
27+
listAnnouncements,
28+
type HackathonAnnouncement,
29+
} from '@/lib/api/hackathons/index';
2630
import { HackathonWinner } from '@/lib/api/hackathons';
31+
import { Megaphone } from 'lucide-react';
32+
import { AnnouncementsTab } from '@/components/hackathons/announcements/AnnouncementsTab';
2733

2834
export default function HackathonPage() {
2935
const router = useRouter();
@@ -63,6 +69,29 @@ export default function HackathonPage() {
6369
autoFetch: !!hackathonId,
6470
});
6571

72+
// Fetch announcements for public view
73+
const [announcements, setAnnouncements] = useState<HackathonAnnouncement[]>(
74+
[]
75+
);
76+
const [announcementsLoading, setAnnouncementsLoading] = useState(false);
77+
78+
useEffect(() => {
79+
async function fetchAnnouncements() {
80+
if (!hackathonId) return;
81+
try {
82+
setAnnouncementsLoading(true);
83+
const data = await listAnnouncements(hackathonId);
84+
// Only show published announcements for public view
85+
setAnnouncements(data.filter(a => !a.isDraft));
86+
} catch (error) {
87+
console.error('Failed to fetch announcements:', error);
88+
} finally {
89+
setAnnouncementsLoading(false);
90+
}
91+
}
92+
fetchAnnouncements();
93+
}, [hackathonId]);
94+
6695
const hackathonTabs = useMemo(() => {
6796
const hasParticipants =
6897
Array.isArray(currentHackathon?.participants) &&
@@ -71,9 +100,7 @@ export default function HackathonPage() {
71100
const hasResources = currentHackathon?.resources?.[0];
72101
const participantType = currentHackathon?.participantType;
73102
const isTeamHackathon =
74-
participantType === 'TEAM' ||
75-
participantType === 'TEAM_OR_INDIVIDUAL' ||
76-
participantType === 'INDIVIDUAL';
103+
participantType === 'TEAM' || participantType === 'TEAM_OR_INDIVIDUAL';
77104
const isTabEnabled =
78105
currentHackathon?.enabledTabs?.includes('joinATeamTab') !== false;
79106

@@ -110,6 +137,16 @@ export default function HackathonPage() {
110137
},
111138
]
112139
: []),
140+
...(announcements.length > 0
141+
? [
142+
{
143+
id: 'announcements',
144+
label: 'Announcements',
145+
badge: announcements.length,
146+
icon: Megaphone,
147+
},
148+
]
149+
: []),
113150
{
114151
id: 'submission',
115152
label: 'Submissions',
@@ -149,6 +186,7 @@ export default function HackathonPage() {
149186
teamPosts.length,
150187
hackathonId,
151188
winners,
189+
announcements,
152190
]);
153191

154192
// Refresh hackathon data
@@ -202,8 +240,7 @@ export default function HackathonPage() {
202240
// Team formation availability
203241
const isTeamHackathon =
204242
currentHackathon?.participantType === 'TEAM' ||
205-
currentHackathon?.participantType === 'TEAM_OR_INDIVIDUAL' ||
206-
currentHackathon?.participantType === 'INDIVIDUAL';
243+
currentHackathon?.participantType === 'TEAM_OR_INDIVIDUAL';
207244
const isTeamFormationEnabled =
208245
isTeamHackathon &&
209246
currentHackathon?.enabledTabs?.includes('joinATeamTab') !== false;
@@ -300,6 +337,7 @@ export default function HackathonPage() {
300337
isTeamFormationEnabled,
301338
registrationDeadlinePolicy: currentHackathon.registrationDeadlinePolicy, // Now matches API casing
302339
registrationDeadline: currentHackathon.registrationDeadline,
340+
participantType: currentHackathon.participantType,
303341
onJoinClick: handleJoinClick,
304342
onLeaveClick: handleLeaveClick,
305343
isLeaving,
@@ -342,7 +380,7 @@ export default function HackathonPage() {
342380
currency: tier.currency,
343381
passMark: tier.passMark,
344382
description: tier.description,
345-
prizeAmount: tier.prizeAmount, // Keep as string to match PrizeTier interface
383+
prizeAmount: tier.prizeAmount,
346384
}))}
347385
totalPrizePool={currentHackathon.prizeTiers
348386
.reduce(
@@ -371,6 +409,13 @@ export default function HackathonPage() {
371409
<HackathonParticipants />
372410
)}
373411

412+
{activeTab === 'announcements' && announcements.length > 0 && (
413+
<AnnouncementsTab
414+
announcements={announcements}
415+
hackathonSlug={hackathonId}
416+
/>
417+
)}
418+
374419
{activeTab === 'submission' && (
375420
<SubmissionTab
376421
organizationId={currentHackathon.organizationId}

app/(landing)/hackathons/preview/[orgId]/[draftId]/page.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,15 @@ export default function DraftPreviewPage({ params }: PreviewPageProps) {
132132
timezone: draft.data.timeline?.timezone || 'UTC',
133133

134134
startDate: draft.data.timeline?.startDate || '',
135-
endDate: draft.data.timeline?.winnerAnnouncementDate || '',
135+
endDate:
136+
draft.data.timeline?.winnersAnnouncedAt ||
137+
draft.data.timeline?.winnerAnnouncementDate ||
138+
draft.data.timeline?.judgingEnd ||
139+
draft.data.timeline?.judgingDate ||
140+
draft.data.timeline?.judgingStart ||
141+
draft.data.timeline?.submissionDeadline ||
142+
draft.data.timeline?.startDate ||
143+
'',
136144
submissionDeadline: draft.data.timeline?.submissionDeadline || '',
137145
registrationDeadline:
138146
draft.data.participation?.registrationDeadline || '',

0 commit comments

Comments
 (0)