Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
f4dcdc0
fix: modify api.ts
Benjtalkshow Oct 14, 2025
b90d4ed
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Oct 16, 2025
9ea81a5
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Oct 16, 2025
5be269f
fix: remove google auth buttom
Benjtalkshow Oct 16, 2025
490dcb2
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Oct 20, 2025
842fd48
fix: fixes responsive fixes on organization
Benjtalkshow Oct 20, 2025
596a7f8
fix: minor fixes
Benjtalkshow Nov 6, 2025
96fee24
fix: minor fixes
Benjtalkshow Nov 6, 2025
9dfb149
fix: modify create organization
Benjtalkshow Nov 7, 2025
a194d90
fix: modify create organization
Benjtalkshow Nov 7, 2025
b2ceee0
fix: fix organization permission
Benjtalkshow Nov 8, 2025
9ea97d1
fix: merge into main
Benjtalkshow Nov 8, 2025
adb4629
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 8, 2025
0c11420
fix: merge into main
Benjtalkshow Nov 8, 2025
1f5ec24
feat: hackathon overview page
Benjtalkshow Nov 11, 2025
bca1ef6
feat: hackathon overview page
Benjtalkshow Nov 11, 2025
9202b4f
feat: implement participant overview
Benjtalkshow Nov 12, 2025
096f265
feat: implement participant overview
Benjtalkshow Nov 12, 2025
b3478d0
feat: implement resources tab
Benjtalkshow Nov 12, 2025
e83a0be
feat: implement the submission tab
Benjtalkshow Nov 12, 2025
398be9b
feat: implement comment tab
Benjtalkshow Nov 12, 2025
de546b1
fix: implement provider for hackathon
Benjtalkshow Nov 14, 2025
0fd2690
fix: implement provider for hackathon
Benjtalkshow Nov 14, 2025
0d7417f
fix: minor fixes
Benjtalkshow Nov 15, 2025
4d3efee
fix: merge branch 'main' of https://github.com/Benjtalkshow/boundless…
Benjtalkshow Nov 15, 2025
83893e4
fix: hackathon banner
Benjtalkshow Nov 15, 2025
8013d62
fix: hackathon banner
Benjtalkshow Nov 15, 2025
3fb1323
fix: fix hackthon conflict
Benjtalkshow Nov 15, 2025
b7fc94f
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 15, 2025
0594ac8
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 15, 2025
7e08bc1
fix: fix organization page
Benjtalkshow Nov 15, 2025
22c12c1
fix: fix organization page
Benjtalkshow Nov 16, 2025
08e5be5
fix: fix organization page
Benjtalkshow Nov 16, 2025
8e6bded
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 16, 2025
5e6c9e6
fix: use transform
Benjtalkshow Nov 17, 2025
9135f2e
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 17, 2025
c2409f4
fix: add tagline
Benjtalkshow Nov 18, 2025
b08579a
fix: add tagline
Benjtalkshow Nov 18, 2025
4b8ef58
fix: fix conflict
Benjtalkshow Nov 18, 2025
0ee756e
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 19, 2025
ab0384d
fix: minor fixes
Benjtalkshow Nov 20, 2025
91b5c18
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 20, 2025
67c9fee
fix: minor fixes
Benjtalkshow Nov 21, 2025
9b2029b
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 21, 2025
1fca425
fix: fix timeline and prizes
Benjtalkshow Nov 23, 2025
3c057a0
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 23, 2025
a75f5ee
fix: correct timeline events
Benjtalkshow Nov 26, 2025
da0d27f
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 26, 2025
3a0c19b
fix: implement registration deadline policy
Benjtalkshow Nov 27, 2025
c616182
fix: implement registration deadline policy
Benjtalkshow Nov 27, 2025
9a98077
fix: implement registration deadline policy
Benjtalkshow Nov 27, 2025
3ac17f8
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 28, 2025
386a338
feat: implement leave hackathon
Benjtalkshow Nov 28, 2025
f4ed466
feat: implement leave hackathon
Benjtalkshow Nov 28, 2025
12a1705
feat: implement leave hackathon
Benjtalkshow Nov 28, 2025
dd60321
fix: delete hackathon
Benjtalkshow Nov 28, 2025
a4e22b0
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 28, 2025
19dd6ee
fix: implement invite participants
Benjtalkshow Nov 30, 2025
8211c60
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Nov 30, 2025
fdebd0b
fix: implement participant profile viewing
Benjtalkshow Dec 1, 2025
f7fad5c
feat: fetch participants team
Benjtalkshow Dec 1, 2025
c110717
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Dec 1, 2025
68b1a2d
fix: redesign hackathon banner
Benjtalkshow Dec 1, 2025
9cf2a9e
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Dec 1, 2025
d662148
fix: fix hackthon card
Benjtalkshow Dec 4, 2025
6037e99
fix: Resolve conflict: delete middleware.ts
Benjtalkshow Dec 5, 2025
bc80ddb
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Dec 5, 2025
5569590
fix: Resolve conflict in ProfileHeader.tsx
Benjtalkshow Dec 9, 2025
dc1be4f
fix: fix search bar in blog page
Benjtalkshow Dec 24, 2025
67abffb
fix: fix search bar in blog page
Benjtalkshow Dec 24, 2025
a40739f
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Dec 24, 2025
1c4965f
fix: fix search bar in blog page
Benjtalkshow Dec 24, 2025
43a4b9b
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Dec 24, 2025
54bd42e
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Jan 4, 2026
b79d391
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Jan 7, 2026
5c906a5
fix: fix error in fetching team posts
Benjtalkshow Jan 10, 2026
b000eb7
feat: implement create team, get my team
Benjtalkshow Jan 10, 2026
8e95934
feat: implement create team, get my team
Benjtalkshow Jan 11, 2026
59586e4
feat: implement hackathon project submission flow
Benjtalkshow Jan 12, 2026
d812fec
feat: implement voting for submission
Benjtalkshow Jan 14, 2026
d501680
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Jan 20, 2026
e62d570
fix: team formation updates
Benjtalkshow Feb 3, 2026
15a581b
fix: fix conflict
Benjtalkshow Feb 3, 2026
4d89af9
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Feb 3, 2026
2589770
fix: implement team invitation
Benjtalkshow Feb 5, 2026
ed26db6
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Feb 5, 2026
1d20c57
feat: hackathon submissions bulk actions, ranking and ui refinements
Benjtalkshow Feb 5, 2026
aa259cd
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Feb 5, 2026
18747e4
fix: implement empty state for hackathons page
Benjtalkshow Feb 6, 2026
76a9124
feat: Implement submission visibility and explore submissions
Benjtalkshow Feb 6, 2026
e0fb888
feat: Implement winners tab for hackathon winners display
Benjtalkshow Feb 7, 2026
2775ba8
feat: implement hackathon analytics
Benjtalkshow Feb 11, 2026
199c698
fix: fix coderabbit corrections
Benjtalkshow Feb 11, 2026
e7e78dd
fix: fix coderabbit corrections
Benjtalkshow Feb 11, 2026
736b900
fix: fix coderabbit corrections
Benjtalkshow Feb 11, 2026
e92b634
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Feb 11, 2026
e289ce6
fix: fix conflict
Benjtalkshow Feb 11, 2026
4e45528
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Feb 11, 2026
29c281d
fix: fix organization settings data persistence, hackathondrafts and …
Benjtalkshow Feb 11, 2026
20a31e2
feat: fix user profile
Benjtalkshow Feb 12, 2026
415434e
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Feb 12, 2026
8497f6d
feat: Implemented the participant-facing and hackathon organizers ann…
Benjtalkshow Feb 13, 2026
5f53c32
feat: Implement judging dashboard and detailed breakdowns
Benjtalkshow Feb 15, 2026
5ab6866
feat: Implement judging dashboard and detailed breakdowns
Benjtalkshow Feb 15, 2026
a135b9a
fix: fix coderabbit corrections
Benjtalkshow Feb 15, 2026
9a90100
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Feb 16, 2026
8bfcb29
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Feb 16, 2026
94f4f2e
fix: submission management ux refinements
Benjtalkshow Feb 16, 2026
fc9899e
fix: submission management ux refinements
Benjtalkshow Feb 16, 2026
35449a2
fix: submission management ux refinements
Benjtalkshow Feb 16, 2026
dbda687
fix: submission management ux refinements
Benjtalkshow Feb 16, 2026
a37a0c0
fix: submission management ux refinements
Benjtalkshow Feb 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useEffect, useState, useCallback } from 'react';
import MetricsCard from '@/components/organization/cards/MetricsCard';
import JudgingParticipant from '@/components/organization/cards/JudgingParticipant';
import EmptyState from '@/components/EmptyState';
import { useParams } from 'next/navigation';
import {
getJudgingSubmissions,
Expand All @@ -16,6 +17,7 @@ import {
type JudgingCriterion,
type JudgingSubmission,
type JudgingResult,
type AggregatedJudgingResults,
} from '@/lib/api/hackathons/judging';
import { getSubmissionDetails } from '@/lib/api/hackathons/participants';
import { getOrganizationMembers } from '@/lib/api/organization';
Expand Down Expand Up @@ -49,8 +51,12 @@ export default function JudgingPage() {
'owner' | 'admin' | 'member' | null
>(null);
const [judgingResults, setJudgingResults] = useState<JudgingResult[]>([]);
const [judgingSummary, setJudgingSummary] =
useState<AggregatedJudgingResults | null>(null);
const [isFetchingResults, setIsFetchingResults] = useState(false);
const [winners, setWinners] = useState<JudgingResult[]>([]);
const [winnersSummary, setWinnersSummary] =
useState<AggregatedJudgingResults | null>(null);
const [isFetchingWinners, setIsFetchingWinners] = useState(false);
const [isPublishing, setIsPublishing] = useState(false);
const [isCurrentUserJudge, setIsCurrentUserJudge] = useState(false);
Expand Down Expand Up @@ -151,16 +157,21 @@ export default function JudgingPage() {
setIsFetchingResults(true);
try {
const res = await getJudgingResults(organizationId, hackathonId);
console.log('Judging Results Response:', res);
if (res.success) {
setJudgingResults(res.data || []);

if (res.success && res.data) {
setJudgingResults(res.data.results || []);
setJudgingSummary(res.data);
} else {
setJudgingResults([]);
toast.error(res.message || 'Failed to load judging results');
setJudgingSummary(null);
if (!res.success) {
toast.error((res as any).message || 'Failed to load judging results');
}
}
} catch (error: any) {
console.error('Error fetching results:', error);
setJudgingResults([]);
setJudgingSummary(null);
toast.error(
error.response?.data?.message ||
error.message ||
Expand Down Expand Up @@ -340,8 +351,9 @@ export default function JudgingPage() {
setIsFetchingWinners(true);
try {
const res = await getJudgingWinners(organizationId, hackathonId);
if (res.success) {
setWinners(res.data || []);
if (res.success && res.data) {
setWinners(res.data.results || []);
setWinnersSummary(res.data);
}
} catch (error) {
console.error('Error fetching winners:', error);
Expand All @@ -368,16 +380,28 @@ export default function JudgingPage() {
}
};

// Calculate statistics safely
const gradedCount = judgingResults.length;
const averageHackathonScore =
judgingResults.length > 0
// Use pre-calculated statistics from the API if available, otherwise fallback to local calculation
const gradedCount = judgingSummary
? judgingSummary.submissionsScoredCount
: judgingResults.length;

const totalPossibleSubmissions = judgingSummary
? judgingSummary.totalSubmissions
: submissions.length;

const averageHackathonScore = judgingSummary
? judgingSummary.averageScoreAcrossAll
: judgingResults.length > 0
? judgingResults.reduce(
(acc, curr) => acc + (curr.averageScore || 0),
0
) / judgingResults.length
: 0;

const assignedJudgesCount = judgingSummary
? judgingSummary.judgesAssigned
: currentJudges.length;

return (
<AuthGuard redirectTo='/auth?mode=signin' fallback={<Loading />}>
<div className='bg-background min-h-screen space-y-6 p-8 text-white'>
Expand All @@ -392,8 +416,8 @@ export default function JudgingPage() {
<div className='flex gap-4'>
<MetricsCard
title='Graded Projects'
value={`${gradedCount} / ${submissions.length}`}
subtitle={`${submissions.length > 0 ? Math.round((gradedCount / submissions.length) * 100) : 0}% Completion`}
value={`${gradedCount} / ${totalPossibleSubmissions}`}
subtitle={`${totalPossibleSubmissions > 0 ? Math.round((gradedCount / totalPossibleSubmissions) * 100) : 0}% Completion`}
/>
<MetricsCard
title='Avg. Hackathon Score'
Expand All @@ -402,7 +426,7 @@ export default function JudgingPage() {
/>
<MetricsCard
title='Assigned Judges'
value={currentJudges.length}
value={assignedJudgesCount}
subtitle='on this hackathon'
/>
</div>
Expand Down Expand Up @@ -451,7 +475,7 @@ export default function JudgingPage() {
<div className='flex items-center justify-center py-12'>
<Loader2 className='h-8 w-8 animate-spin text-gray-400' />
</div>
) : (
) : submissions.length > 0 ? (
<div className='flex flex-col gap-4'>
{submissions.map(submission => (
<JudgingParticipant
Expand All @@ -470,6 +494,11 @@ export default function JudgingPage() {
/>
))}
</div>
) : (
<EmptyState
title='No Submissions Yet'
description='There are currently no submissions to judge.'
/>
)}
</TabsContent>

Expand All @@ -489,11 +518,14 @@ export default function JudgingPage() {
</h3>
<div className='space-y-4'>
{currentJudges.length === 0 ? (
<p className='py-4 text-sm text-gray-400 italic'>
No judges assigned yet.
</p>
<EmptyState
title='No Judges Assigned'
description='No judges assigned yet.'
type='compact'
className='py-8'
/>
) : (
currentJudges.map((judge: any) => (
currentJudges.map((judge: any, index: number) => (
<div
key={judge.id}
className='flex items-center justify-between rounded-md border border-white/10 bg-white/5 p-3'
Expand All @@ -517,7 +549,7 @@ export default function JudgingPage() {
{judge.name}
</p>
<p className='text-xs text-gray-500'>
{judge.userId}
Judge {index + 1}
</p>
</div>
</div>
Expand Down Expand Up @@ -605,9 +637,12 @@ export default function JudgingPage() {
);
})}
{orgMembers.length === 0 && !isRefreshingJudges && (
<p className='py-8 text-center text-sm text-gray-400'>
No organization members found.
</p>
<EmptyState
title='No Members Found'
description='No organization members found.'
type='compact'
className='py-8'
/>
)}
</div>
</div>
Expand Down Expand Up @@ -648,6 +683,7 @@ export default function JudgingPage() {
organizationId={organizationId}
hackathonId={hackathonId}
totalJudges={currentJudges.length}
criteria={criteria}
/>
</div>
)}
Expand All @@ -660,12 +696,18 @@ export default function JudgingPage() {
<div className='flex items-center justify-center py-12'>
<Loader2 className='h-8 w-8 animate-spin text-gray-400' />
</div>
) : (
) : judgingResults.length > 0 ? (
<JudgingResultsTable
results={judgingResults}
organizationId={organizationId}
hackathonId={hackathonId}
totalJudges={currentJudges.length}
criteria={criteria}
/>
) : (
<EmptyState
title='No Results Yet'
description='No judging results available yet. Results appear once judges submit scores.'
/>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
'use client';

import { useParams } from 'next/navigation';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { Loader2, AlertCircle } from 'lucide-react';
import { useHackathonSubmissions } from '@/hooks/hackathon/use-hackathon-submissions';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { AuthGuard } from '@/components/auth';
import Loading from '@/components/Loading';
import { SubmissionsManagement } from '@/components/organization/hackathons/submissions/SubmissionsManagement';
import { authClient } from '@/lib/auth-client';
import { getHackathon, type Hackathon } from '@/lib/api/hackathons';

export default function SubmissionsPage() {
const params = useParams();
Expand All @@ -26,12 +28,40 @@ export default function SubmissionsPage() {
refresh,
} = useHackathonSubmissions(hackathonId);

const [currentUserId, setCurrentUserId] = useState<string | null>(null);
const [hackathon, setHackathon] = useState<Hackathon | null>(null);

useEffect(() => {
if (hackathonId) {
fetchSubmissions();
const fetchHackathonDetails = async () => {
try {
const res = await getHackathon(hackathonId);
if (res.success && res.data) {
setHackathon(res.data);
}
} catch (err) {
console.error('Failed to fetch hackathon details:', err);
}
};
fetchHackathonDetails();
}
}, [hackathonId, fetchSubmissions]);

useEffect(() => {
const fetchSession = async () => {
try {
const { data: session } = await authClient.getSession();
if (session?.user?.id) {
setCurrentUserId(session.user.id);
}
} catch (err) {
console.error('Failed to fetch session:', err);
}
};
fetchSession();
}, []);

if (error) {
return (
<div className='flex min-h-screen items-center justify-center bg-black p-6'>
Expand Down Expand Up @@ -84,6 +114,8 @@ export default function SubmissionsPage() {
onRefresh={refresh}
organizationId={organizationId}
hackathonId={hackathonId}
currentUserId={currentUserId || undefined}
hackathon={hackathon || undefined}
/>
)}
</div>
Expand Down
51 changes: 27 additions & 24 deletions components/hackathons/hackathonNavTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
'use client';
import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area';

interface HackathonNavTab {
id: string;
Expand All @@ -24,30 +24,33 @@ export function HackathonNavTabs({
return (
<div className='w-full border-b border-[#a7f950]/20'>
<div className='mx-auto max-w-7xl px-6'>
<div className='flex items-center gap-1 overflow-x-auto'>
{tabs.map(tab => {
const isActive = activeTab === tab.id;
return (
<button
key={tab.id}
onClick={() => handleTabChange(tab.id)}
className={`relative px-4 py-4 text-sm font-medium whitespace-nowrap transition-colors duration-200 ${isActive ? 'text-[#a7f950]' : 'text-gray-400 hover:text-white'} `}
>
<div className='flex items-center gap-2'>
<span>{tab.label}</span>
{tab.badge !== undefined && (
<span className='rounded-full bg-[#a7f950]/20 px-2 py-1 text-xs text-[#a7f950]'>
{tab.badge}
</span>
<ScrollArea className='w-full whitespace-nowrap'>
<div className='flex items-center gap-1'>
{tabs.map(tab => {
const isActive = activeTab === tab.id;
return (
<button
key={tab.id}
onClick={() => handleTabChange(tab.id)}
className={`relative px-4 py-4 text-sm font-medium whitespace-nowrap transition-colors duration-200 ${isActive ? 'text-[#a7f950]' : 'text-gray-400 hover:text-white'} `}
>
<div className='flex items-center gap-2'>
<span>{tab.label}</span>
{tab.badge !== undefined && (
<span className='rounded-full bg-[#a7f950]/20 px-2 py-1 text-xs text-[#a7f950]'>
{tab.badge}
</span>
)}
</div>
{isActive && (
<div className='absolute right-0 bottom-0 left-0 h-0.5 bg-[#a7f950]' />
)}
</div>
{isActive && (
<div className='absolute right-0 bottom-0 left-0 h-0.5 bg-[#a7f950]' />
)}
</button>
);
})}
</div>
</button>
);
})}
</div>
<ScrollBar orientation='horizontal' />
</ScrollArea>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,18 @@ export const useScoreForm = ({
setShowSuccess(false);
onClose();
}, 2000);
} else {
// Handle API error response
const errorMessage =
response.message || 'Failed to submit grade. Please try again.';
toast.error(errorMessage);
}
} catch (error) {
} catch (error: any) {
// Handle network or unexpected errors
const errorMessage =
error instanceof Error
? error.message
: 'Failed to submit grade. Please try again.';
error?.response?.data?.message ||
error?.message ||
'Failed to submit grade. Please try again.';
toast.error(errorMessage);
} finally {
setIsLoading(false);
Expand Down
Loading
Loading