Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -43,8 +43,8 @@ interface FilterState {

const mapFiltersToParams = (filters: FilterState, searchOverride?: string) => ({
search: searchOverride !== undefined ? searchOverride : filters.search,
status: filters.status === 'all' ? undefined : filters.status,
type: filters.type === 'all' ? undefined : filters.type,
status: filters.status === 'all' ? undefined : filters.status.toUpperCase(),
type: filters.type === 'all' ? undefined : filters.type.toUpperCase(),
});

const ParticipantsPage: React.FC = () => {
Expand Down Expand Up @@ -228,9 +228,45 @@ const ParticipantsPage: React.FC = () => {
}
};

// Frontend-side filtering as per requirement
const filteredParticipants = useMemo(() => {
return participants.filter(participant => {
// Search: filter by name or username
const search = filters.search.toLowerCase();
const matchesSearch = search
? (participant.user?.profile?.name || '')
.toLowerCase()
.includes(search) ||
(participant.user?.profile?.username || '')
.toLowerCase()
.includes(search)
: true;

// Status: filter by participant.submission.status
// Filter values are 'submitted', 'not_submitted', etc.
// ParticipantSubmission.status values are 'submitted', 'shortlisted', etc.
const matchesStatus =
filters.status === 'all'
? true
: filters.status === 'not_submitted'
? !participant.submission
: participant.submission?.status?.toLowerCase() ===
filters.status.toLowerCase();

// Type: filter by participant.participationType
const matchesType =
filters.type === 'all'
? true
: participant.participationType?.toLowerCase() ===
filters.type.toLowerCase();

return matchesSearch && matchesStatus && matchesType;
});
}, [participants, filters.search, filters.status, filters.type]);

// Mock table instance for DataTablePagination
const table = useReactTable({
data: participants,
data: filteredParticipants,
columns: [], // Not used for rendering here
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
Expand Down Expand Up @@ -316,15 +352,15 @@ const ParticipantsPage: React.FC = () => {
<div className='space-y-6'>
{view === 'table' ? (
<ParticipantsTable
data={participants}
data={filteredParticipants}
loading={participantsLoading}
onReview={handleReview}
onViewTeam={handleViewTeam}
onGrade={handleGrade}
/>
) : (
<ParticipantsGrid
data={participants}
data={filteredParticipants}
loading={participantsLoading}
onReview={handleReview}
onViewTeam={handleViewTeam}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
'use client';

import { useParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useEffect, useState, useMemo } from 'react';
import { Loader2, AlertCircle } from 'lucide-react';
import { useOrganizerSubmissions } from '@/hooks/hackathon/use-organizer-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';
import {
getHackathon,
type Hackathon,
type ParticipantSubmission,
} from '@/lib/api/hackathons';
import { reportError } from '@/lib/error-reporting';
import { useReactTable, getCoreRowModel } from '@tanstack/react-table';
import { DataTablePagination } from '@/components/ui/data-table-pagination';

export default function SubmissionsPage() {
const params = useParams();
const hackathonId = params.hackathonId as string;
const organizationId = params.id as string;

const {
submissions,
submissions: allSubmissions,
pagination,
filters,
loading,
Expand All @@ -27,14 +33,14 @@ export default function SubmissionsPage() {
updateFilters,
goToPage,
refresh,
updateLimit,
} = useOrganizerSubmissions(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);
Expand All @@ -50,7 +56,7 @@ export default function SubmissionsPage() {
};
fetchHackathonDetails();
}
}, [hackathonId, fetchSubmissions]);
}, [hackathonId]);

useEffect(() => {
const fetchSession = async () => {
Expand All @@ -66,6 +72,52 @@ export default function SubmissionsPage() {
fetchSession();
}, []);

// Frontend-side filtering
const filteredSubmissions = useMemo(() => {
return allSubmissions.filter(sub => {
const search = filters.search?.toLowerCase() || '';
const matchesSearch = search
? (sub.projectName || '').toLowerCase().includes(search) ||
(sub.participant?.name || '').toLowerCase().includes(search) ||
(sub.participant?.username || '').toLowerCase().includes(search)
: true;

const matchesStatus = !filters.status || sub.status === filters.status;

const matchesType =
!filters.type || sub.participationType === filters.type;

return matchesSearch && matchesStatus && matchesType;
});
}, [allSubmissions, filters.search, filters.status, filters.type]);

const table = useReactTable({
data: filteredSubmissions,
columns: [],
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
pageCount: pagination.totalPages,
state: {
pagination: {
pageIndex: pagination.page - 1,
pageSize: pagination.limit,
},
},
onPaginationChange: updater => {
if (typeof updater === 'function') {
const newState = updater({
pageIndex: pagination.page - 1,
pageSize: pagination.limit,
});
if (newState.pageSize !== pagination.limit) {
updateLimit(newState.pageSize);
} else {
goToPage(newState.pageIndex + 1);
}
}
},
});

if (error) {
return (
<div className='flex min-h-screen items-center justify-center bg-black p-6'>
Expand Down Expand Up @@ -100,27 +152,33 @@ export default function SubmissionsPage() {

{/* Main Content */}
<div className='mx-auto max-w-7xl px-6 py-12 sm:px-8 lg:px-12'>
{loading && submissions.length === 0 ? (
{loading && allSubmissions.length === 0 ? (
<div className='flex items-center justify-center py-20'>
<div className='flex flex-col items-center gap-3'>
<Loader2 className='h-6 w-6 animate-spin text-gray-400' />
<p className='text-sm text-gray-500'>Loading submissions...</p>
</div>
</div>
) : (
<SubmissionsManagement
submissions={submissions}
pagination={pagination}
filters={filters}
loading={loading}
onFilterChange={updateFilters}
onPageChange={goToPage}
onRefresh={refresh}
organizationId={organizationId}
hackathonId={hackathonId}
currentUserId={currentUserId || undefined}
hackathon={hackathon || undefined}
/>
<div className='space-y-6'>
<SubmissionsManagement
submissions={filteredSubmissions}
pagination={pagination}
filters={filters}
loading={loading}
onFilterChange={updateFilters}
onPageChange={goToPage}
onRefresh={refresh}
organizationId={organizationId}
hackathonId={hackathonId}
currentUserId={currentUserId || undefined}
hackathon={hackathon || undefined}
/>

<div className='flex justify-end'>
<DataTablePagination table={table} loading={loading} />
</div>
</div>
)}
</div>
</div>
Expand Down
10 changes: 5 additions & 5 deletions components/hackathons/submissions/SubmissionDetailModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,16 @@ export function SubmissionDetailModal({
<div className='flex items-center gap-4'>
<Badge
className={`${
submission.status === 'shortlisted'
submission.status === 'SHORTLISTED'
? 'border-primary bg-[#E5FFE5] text-[#4E9E00]'
: submission.status === 'disqualified'
: submission.status === 'DISQUALIFIED'
? 'border-[#FF5757] bg-[#FFEAEA] text-[#D33]'
: 'border-[#645D5D] bg-[#E4DBDB] text-[#645D5D]'
}`}
>
{submission.status === 'shortlisted'
{submission.status === 'SHORTLISTED'
? 'Shortlisted'
: submission.status === 'disqualified'
: submission.status === 'DISQUALIFIED'
? 'Disqualified'
: 'Submitted'}
</Badge>
Expand Down Expand Up @@ -295,7 +295,7 @@ export function SubmissionDetailModal({
</div>

{/* Disqualification Reason */}
{submission.status === 'disqualified' &&
{submission.status === 'DISQUALIFIED' &&
submission.disqualificationReason && (
<div className='rounded-lg border border-red-500/50 bg-red-500/10 p-4'>
<h4 className='mb-2 font-semibold text-red-400'>
Expand Down
2 changes: 1 addition & 1 deletion components/organization/cards/Participant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const Participant = ({

// Check if submission is shortlisted
const isShortlisted = useMemo(() => {
return participant.submission?.status === 'shortlisted';
return participant.submission?.status === 'SHORTLISTED';
}, [participant.submission?.status]);

// Fetch criteria when opening judge modal
Expand Down
4 changes: 2 additions & 2 deletions components/organization/hackathons/ParticipantsGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ export const ParticipantsGrid: React.FC<ParticipantsGridProps> = ({
const user = participant.user;
const submission = participant.submission;
const hasSubmission = !!submission;
const isShortlisted = submission?.status === 'shortlisted';
const isDisqualified = submission?.status === 'disqualified';
const isShortlisted = submission?.status === 'SHORTLISTED';
const isDisqualified = submission?.status === 'DISQUALIFIED';
const isTeam = participant.participationType === 'team';

const votesCount = Array.isArray(submission?.votes)
Expand Down
23 changes: 13 additions & 10 deletions components/organization/hackathons/ParticipantsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,22 +110,25 @@ export function ParticipantsTable({
);
}

const status = submission.status;
const status = submission.status as
| 'SHORTLISTED'
| 'DISQUALIFIED'
| 'SUBMITTED';
return (
<Badge
variant='outline'
className={cn(
'capitalize',
status === 'shortlisted'
? 'border-green-500/30 bg-green-500/10 text-green-400'
: status === 'disqualified'
? 'border-red-500/30 bg-red-500/10 text-red-400'
: 'border-yellow-500/30 bg-yellow-500/10 text-yellow-400'
'rounded-full border px-2 py-0.5 text-[10px] font-medium transition-colors',
status === 'SHORTLISTED'
? 'border-primary/30 bg-primary/5 text-primary'
: status === 'DISQUALIFIED'
? 'border-red-500/30 bg-red-500/5 text-red-400'
: 'border-yellow-500/30 bg-yellow-500/5 text-yellow-400'
)}
>
{status === 'shortlisted'
{status === 'SHORTLISTED'
? 'Shortlisted'
: status === 'disqualified'
: status === 'DISQUALIFIED'
? 'Disqualified'
: 'Submitted'}
</Badge>
Expand All @@ -150,7 +153,7 @@ export function ParticipantsTable({
const participant = row.original;
const hasSubmission = !!participant.submission;
const isShortlisted =
participant.submission?.status === 'shortlisted';
participant.submission?.status === 'SHORTLISTED';

return (
<DropdownMenu>
Expand Down
Loading
Loading