Skip to content

Commit 548b5f4

Browse files
committed
feat(registrations): Use profile data in CSV exports and add service role client for events
- Add service role Supabase client to events export endpoint to bypass RLS when querying registrations - Fetch user profiles for registrations with user_id to populate accurate name and email data - Update CSV export to display profile name and email when available, falling back to registration data - Apply same profile data logic to hackathons registration export for consistency - Rename server client import to avoid naming conflicts with service role client - Improves data accuracy in exported registrations by using authoritative profile information
1 parent 7c0a82e commit 548b5f4

File tree

2 files changed

+73
-9
lines changed
  • app/api
    • events/[slug]/registrations/export
    • hackathons/[id]/registrations/export

2 files changed

+73
-9
lines changed

app/api/events/[slug]/registrations/export/route.ts

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
11
import { NextRequest, NextResponse } from 'next/server';
2-
import { createClient } from '@/lib/supabase/server';
2+
import { createClient as createServerClient } from '@/lib/supabase/server';
3+
import { createClient } from '@supabase/supabase-js';
34

45
export const runtime = 'nodejs';
56

7+
// Create Supabase client with service role key to bypass RLS for master_registrations
8+
const getServiceRoleClient = () => {
9+
return createClient(
10+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
11+
process.env.SUPABASE_SERVICE_ROLE_KEY!,
12+
{
13+
auth: {
14+
autoRefreshToken: false,
15+
persistSession: false
16+
}
17+
}
18+
);
19+
};
20+
621
// GET: Export registrations as CSV
722
export async function GET(
823
request: NextRequest,
924
{ params }: { params: Promise<{ slug: string }> }
1025
) {
1126
try {
1227
const { slug } = await params;
13-
const supabase = await createClient();
14-
15-
// Get the current user
16-
const { data: { user }, error: authError } = await supabase.auth.getUser();
28+
29+
// Use server client for authentication
30+
const serverClient = await createServerClient();
31+
const { data: { user }, error: authError } = await serverClient.auth.getUser();
1732

1833
if (authError || !user) {
1934
return NextResponse.json(
@@ -22,6 +37,9 @@ export async function GET(
2237
);
2338
}
2439

40+
// Use service role client for querying (bypasses RLS)
41+
const supabase = getServiceRoleClient();
42+
2543
// Get the event by slug
2644
const { data: event, error: eventError } = await supabase
2745
.from('events')
@@ -67,6 +85,21 @@ export async function GET(
6785
);
6886
}
6987

88+
// Get user profiles for registrations that have user_id
89+
const userIds = registrations
90+
?.filter(r => r.user_id)
91+
.map(r => r.user_id) || [];
92+
93+
let profiles: { id: string; first_name: string | null; last_name: string | null; email: string | null }[] = [];
94+
if (userIds.length > 0) {
95+
const { data: profilesData } = await supabase
96+
.from('profiles')
97+
.select('id, first_name, last_name, email')
98+
.in('id', userIds);
99+
100+
profiles = profilesData || [];
101+
}
102+
70103
// Convert to CSV
71104
const headers = [
72105
'ID',
@@ -82,6 +115,14 @@ export async function GET(
82115
const csvRows = [headers.join(',')];
83116

84117
registrations?.forEach(reg => {
118+
// Get profile data if available
119+
const profile = profiles.find(p => p.id === reg.user_id);
120+
const profileName = profile
121+
? `${profile.first_name || ''} ${profile.last_name || ''}`.trim()
122+
: null;
123+
const displayName = profileName || reg.full_name || '';
124+
const displayEmail = profile?.email || reg.email || '';
125+
85126
// Format date to be more readable (e.g., "Nov 19 2025")
86127
const registeredDate = reg.created_at
87128
? new Date(reg.created_at).toLocaleDateString('en-US', {
@@ -93,8 +134,8 @@ export async function GET(
93134

94135
const row = [
95136
reg.id,
96-
`"${reg.full_name || ''}"`,
97-
`"${reg.email || ''}"`,
137+
`"${displayName}"`,
138+
`"${displayEmail}"`,
98139
`"${reg.phone || ''}"`,
99140
reg.status,
100141
reg.payment_status,

app/api/hackathons/[id]/registrations/export/route.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,21 @@ export async function GET(
8585
);
8686
}
8787

88+
// Get user profiles for registrations that have user_id
89+
const userIds = registrations
90+
?.filter(r => r.user_id)
91+
.map(r => r.user_id) || [];
92+
93+
let profiles: { id: string; first_name: string | null; last_name: string | null; email: string | null }[] = [];
94+
if (userIds.length > 0) {
95+
const { data: profilesData } = await supabase
96+
.from('profiles')
97+
.select('id, first_name, last_name, email')
98+
.in('id', userIds);
99+
100+
profiles = profilesData || [];
101+
}
102+
88103
// Convert to CSV
89104
const headers = [
90105
'ID',
@@ -100,6 +115,14 @@ export async function GET(
100115
const csvRows = [headers.join(',')];
101116

102117
registrations?.forEach(reg => {
118+
// Get profile data if available
119+
const profile = profiles.find(p => p.id === reg.user_id);
120+
const profileName = profile
121+
? `${profile.first_name || ''} ${profile.last_name || ''}`.trim()
122+
: null;
123+
const displayName = profileName || reg.full_name || '';
124+
const displayEmail = profile?.email || reg.email || '';
125+
103126
// Format date to be more readable (e.g., "Nov 19 2025")
104127
const registeredDate = reg.created_at
105128
? new Date(reg.created_at).toLocaleDateString('en-US', {
@@ -111,8 +134,8 @@ export async function GET(
111134

112135
const row = [
113136
reg.id,
114-
`"${reg.full_name || ''}"`,
115-
`"${reg.email || ''}"`,
137+
`"${displayName}"`,
138+
`"${displayEmail}"`,
116139
`"${reg.phone || ''}"`,
117140
reg.status,
118141
reg.payment_status,

0 commit comments

Comments
 (0)