Skip to content

Commit 7ca79e9

Browse files
committed
feat(hackathons): Add registration management with API endpoints and dashboard page
- Create GET endpoint to fetch hackathon registrations with search, filtering, and pagination support - Implement CSV export functionality for hackathon registrations with proper authorization checks - Add registrations dashboard page for hackathon management - Support filtering by registration status and payment status - Include search capability across registration fields (name, email, phone, institution) - Implement proper authorization checks to ensure only company members can access registrations - Use service role client for master_registrations table to bypass RLS policies - Add pagination support with limit and offset parameters - Generate timestamped CSV exports with all registration details
1 parent 8f72043 commit 7ca79e9

File tree

3 files changed

+799
-0
lines changed
  • app
    • api/hackathons/[id]/registrations
    • dashboard/company/[slug]/hackathons/[hackathonSlug]/registrations

3 files changed

+799
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { NextRequest, NextResponse } from 'next/server';
2+
import { createClient } from '@/lib/supabase/server';
3+
4+
export const runtime = 'nodejs';
5+
6+
// GET: Export registrations as CSV
7+
export async function GET(
8+
request: NextRequest,
9+
{ params }: { params: Promise<{ id: string }> }
10+
) {
11+
try {
12+
const { id } = await params;
13+
const supabase = await createClient();
14+
15+
// Get the current user
16+
const { data: { user }, error: authError } = await supabase.auth.getUser();
17+
18+
if (authError || !user) {
19+
return NextResponse.json(
20+
{ error: 'Authentication required' },
21+
{ status: 401 }
22+
);
23+
}
24+
25+
// Get the hackathon by slug
26+
const { data: hackathon, error: hackathonError } = await supabase
27+
.from('hackathons')
28+
.select('id, title, company_id, slug')
29+
.eq('slug', id)
30+
.single();
31+
32+
if (hackathonError || !hackathon) {
33+
return NextResponse.json(
34+
{ error: 'Hackathon not found' },
35+
{ status: 404 }
36+
);
37+
}
38+
39+
// Check if user has access to this hackathon's company
40+
const { data: membership } = await supabase
41+
.from('company_members')
42+
.select('role')
43+
.eq('company_id', hackathon.company_id)
44+
.eq('user_id', user.id)
45+
.single();
46+
47+
if (!membership) {
48+
return NextResponse.json(
49+
{ error: 'Unauthorized access' },
50+
{ status: 403 }
51+
);
52+
}
53+
54+
// Get all registrations for this hackathon
55+
const { data: registrations, error: regError } = await supabase
56+
.from('master_registrations')
57+
.select('*')
58+
.eq('activity_type', 'hackathon')
59+
.eq('activity_id', hackathon.id.toString())
60+
.order('created_at', { ascending: false });
61+
62+
if (regError) {
63+
console.error('Error fetching registrations:', regError);
64+
return NextResponse.json(
65+
{ error: 'Failed to fetch registrations' },
66+
{ status: 500 }
67+
);
68+
}
69+
70+
// Convert to CSV
71+
const headers = [
72+
'ID',
73+
'Full Name',
74+
'Email',
75+
'Phone',
76+
'Institution',
77+
'Department',
78+
'Year of Study',
79+
'Experience Level',
80+
'Status',
81+
'Payment Status',
82+
'Payment Amount',
83+
'Registration Date',
84+
'Created At'
85+
];
86+
87+
const csvRows = [headers.join(',')];
88+
89+
registrations?.forEach(reg => {
90+
const row = [
91+
reg.id,
92+
`"${reg.full_name || ''}"`,
93+
`"${reg.email || ''}"`,
94+
`"${reg.phone || ''}"`,
95+
`"${reg.institution || ''}"`,
96+
`"${reg.department || ''}"`,
97+
`"${reg.year_of_study || ''}"`,
98+
`"${reg.experience_level || ''}"`,
99+
reg.status,
100+
reg.payment_status,
101+
reg.payment_amount || '',
102+
reg.registration_date,
103+
reg.created_at
104+
];
105+
csvRows.push(row.join(','));
106+
});
107+
108+
const csv = csvRows.join('\n');
109+
const filename = `${hackathon.slug}-registrations-${new Date().toISOString().split('T')[0]}.csv`;
110+
111+
return new NextResponse(csv, {
112+
headers: {
113+
'Content-Type': 'text/csv',
114+
'Content-Disposition': `attachment; filename="${filename}"`
115+
}
116+
});
117+
118+
} catch (error) {
119+
console.error('Error exporting registrations:', error);
120+
return NextResponse.json(
121+
{ error: 'Internal server error' },
122+
{ status: 500 }
123+
);
124+
}
125+
}

0 commit comments

Comments
 (0)