Skip to content

Commit 85f3179

Browse files
committed
feat: Add analytics tracking for event and hackathon creation and publication
1 parent eebf9ef commit 85f3179

File tree

4 files changed

+84
-27
lines changed

4 files changed

+84
-27
lines changed

lib/services/analytics-service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export class AnalyticsService {
149149
/**
150150
* Increment a specific field in company analytics
151151
*/
152-
private static async incrementCompanyAnalytics(
152+
static async incrementCompanyAnalytics(
153153
companyId: string,
154154
field: string,
155155
increment: number

lib/services/events.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { createClient } from '@/lib/supabase/server'
33
import { Event, EventsFilters, EventsResponse } from '@/types/events'
44
import { companyService } from './company-service'
5+
import { AnalyticsService } from './analytics-service'
56

67
// Re-export types for convenience
78
export type { Event, EventsFilters, EventsResponse }
@@ -57,7 +58,7 @@ class EventsService {
5758
}
5859

5960
const supabase = await createClient()
60-
61+
6162
let query = supabase
6263
.from('events')
6364
.select(`
@@ -160,7 +161,7 @@ class EventsService {
160161
}
161162

162163
const supabase = await createClient()
163-
164+
164165
const { data: event, error } = await supabase
165166
.from('events')
166167
.select(`
@@ -204,7 +205,7 @@ class EventsService {
204205
}
205206

206207
const supabase = await createClient()
207-
208+
208209
const { data: event, error } = await supabase
209210
.from('events')
210211
.select(`
@@ -244,7 +245,7 @@ class EventsService {
244245

245246
try {
246247
const supabase = await createClient()
247-
248+
248249
const { data: events, error } = await supabase
249250
.from('events')
250251
.select(`
@@ -356,12 +357,12 @@ class EventsService {
356357
// Payment constraint: if price is "Free", payment must be "Not Required"
357358
// if price is not "Free", payment must be "Required"
358359
const paymentValue = eventData.price === 'Free' ? 'Not Required' : (eventData.payment || 'Required')
359-
360+
360361
// Remove status field - events start as draft by default
361362
// They can be submitted for approval later via submitForApproval
362363
// eslint-disable-next-line @typescript-eslint/no-unused-vars
363364
const { status, ...eventDataWithoutStatus } = eventData
364-
365+
365366
const eventPayload = {
366367
...eventDataWithoutStatus,
367368
company_id: companyId,
@@ -393,6 +394,18 @@ class EventsService {
393394
throw new EventError('Failed to create event', EventErrorCodes.NOT_FOUND, 500)
394395
}
395396

397+
// Track analytics for event creation
398+
try {
399+
await AnalyticsService.incrementCompanyAnalytics(
400+
companyId,
401+
'events_created',
402+
1
403+
)
404+
} catch (analyticsError) {
405+
console.error('Error tracking event creation analytics:', analyticsError)
406+
// Don't fail the event creation if analytics tracking fails
407+
}
408+
396409
clearCache()
397410
return event
398411
}
@@ -438,7 +451,7 @@ class EventsService {
438451
// Check if this is an approved event being edited
439452
// If so, reset to pending status for re-approval
440453
const needsReapproval = existingEvent.approval_status === 'approved'
441-
454+
442455
// Update the updated_at timestamp
443456
const updatePayload: Record<string, unknown> = {
444457
...updateData,
@@ -479,17 +492,17 @@ class EventsService {
479492
// Import notification service dynamically to avoid circular dependencies
480493
const { NotificationService } = await import('./notification-service')
481494
const { moderationService } = await import('./moderation-service')
482-
495+
483496
// Log the edit action
484497
await moderationService.logModerationAction('edited', id, undefined, userId, 'Event edited after approval - requires re-approval')
485-
498+
486499
// Notify admins about the updated event
487500
// Get all admin users
488501
const { data: adminUsers } = await supabase
489502
.from('profiles')
490503
.select('id')
491504
.eq('role', 'admin')
492-
505+
493506
if (adminUsers && adminUsers.length > 0) {
494507
// Create notifications for all admins
495508
const notifications = adminUsers.map(admin => ({
@@ -506,11 +519,11 @@ class EventsService {
506519
event_slug: existingEvent.slug
507520
}
508521
}))
509-
522+
510523
await supabase.from('notifications').insert(notifications)
511524
console.log(`📧 Notified ${adminUsers.length} admin(s) about updated event`)
512525
}
513-
526+
514527
// Notify company members about the status change
515528
if (existingEvent.company_id) {
516529
await NotificationService.notifyCompanyMembers(existingEvent.company_id, {

lib/services/hackathons.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Server-side service for hackathons
22
import { createClient } from '@/lib/supabase/server'
33
import { Hackathon, HackathonsFilters, HackathonsResponse } from '@/types/hackathons'
4+
import { AnalyticsService } from './analytics-service'
45

56
// Re-export types for convenience
67
export type { Hackathon, HackathonsFilters, HackathonsResponse }
@@ -30,7 +31,7 @@ class HackathonsService {
3031
}
3132

3233
const supabase = await createClient()
33-
34+
3435
let query = supabase
3536
.from('hackathons')
3637
.select(`
@@ -118,7 +119,7 @@ class HackathonsService {
118119
}
119120

120121
const supabase = await createClient()
121-
122+
122123
const { data: hackathon, error } = await supabase
123124
.from('hackathons')
124125
.select(`
@@ -149,7 +150,7 @@ class HackathonsService {
149150

150151
try {
151152
const supabase = await createClient()
152-
153+
153154
const { data: hackathons, error } = await supabase
154155
.from('hackathons')
155156
.select(`
@@ -180,7 +181,7 @@ class HackathonsService {
180181

181182
async createHackathon(hackathonData: Omit<Hackathon, 'id' | 'created_at' | 'updated_at'>): Promise<Hackathon> {
182183
const supabase = await createClient()
183-
184+
184185
const { data: hackathon, error } = await supabase
185186
.from('hackathons')
186187
.insert([hackathonData])
@@ -192,18 +193,32 @@ class HackathonsService {
192193
throw new Error('Failed to create hackathon')
193194
}
194195

196+
// Track analytics for hackathon creation
197+
if (hackathonData.company_id) {
198+
try {
199+
await AnalyticsService.incrementCompanyAnalytics(
200+
hackathonData.company_id,
201+
'hackathons_created',
202+
1
203+
)
204+
} catch (analyticsError) {
205+
console.error('Error tracking hackathon creation analytics:', analyticsError)
206+
// Don't fail the hackathon creation if analytics tracking fails
207+
}
208+
}
209+
195210
// Clear cache after creating new hackathon
196211
cache.clear()
197212
return hackathon
198213
}
199214

200215
async updateHackathon(
201-
slug: string,
216+
slug: string,
202217
hackathonData: Partial<Omit<Hackathon, 'id' | 'created_at' | 'updated_at'>>,
203218
userId?: string
204219
): Promise<Hackathon> {
205220
const supabase = await createClient()
206-
221+
207222
// Get existing hackathon first
208223
const existingHackathon = await this.getHackathonBySlug(slug)
209224
if (!existingHackathon) {
@@ -212,7 +227,7 @@ class HackathonsService {
212227

213228
// Check if this is an approved hackathon being edited
214229
const needsReapproval = existingHackathon.approval_status === 'approved'
215-
230+
216231
// Prepare update payload
217232
const updatePayload: Record<string, unknown> = {
218233
...hackathonData,
@@ -253,16 +268,16 @@ class HackathonsService {
253268
// Import services dynamically to avoid circular dependencies
254269
const { NotificationService } = await import('./notification-service')
255270
const { moderationService } = await import('./moderation-service')
256-
271+
257272
// Log the edit action
258273
await moderationService.logModerationAction('edited', undefined, hackathonId, userId, 'Hackathon edited after approval - requires re-approval')
259-
274+
260275
// Notify admins about the updated hackathon
261276
const { data: adminUsers } = await supabase
262277
.from('profiles')
263278
.select('id')
264279
.eq('role', 'admin')
265-
280+
266281
if (adminUsers && adminUsers.length > 0) {
267282
const notifications = adminUsers.map(admin => ({
268283
user_id: admin.id,
@@ -278,11 +293,11 @@ class HackathonsService {
278293
hackathon_slug: existingHackathon.slug
279294
}
280295
}))
281-
296+
282297
await supabase.from('notifications').insert(notifications)
283298
console.log(`📧 Notified ${adminUsers.length} admin(s) about updated hackathon`)
284299
}
285-
300+
286301
// Notify company members about the status change
287302
if (existingHackathon.company_id) {
288303
await NotificationService.notifyCompanyMembers(existingHackathon.company_id, {
@@ -309,9 +324,9 @@ class HackathonsService {
309324

310325
async deleteHackathon(slug: string) {
311326
const supabase = await createClient()
312-
327+
313328
console.log('🗑️ Deleting hackathon with slug:', slug)
314-
329+
315330
const { data, error } = await supabase
316331
.from('hackathons')
317332
.delete()

lib/services/moderation-service.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Event } from '@/types/events'
44
import { Hackathon } from '@/types/hackathons'
55
import { companyService } from './company-service'
66
import { SUBSCRIPTION_LIMITS } from '@/types/company'
7+
import { AnalyticsService } from './analytics-service'
78

89
// Moderation log entry interface
910
export interface ModerationLog {
@@ -391,6 +392,20 @@ class ModerationService {
391392
// Log the moderation action
392393
await this.logModerationAction('approved', eventId, undefined, adminId, notes)
393394

395+
// Track analytics for event publication
396+
if (updatedEvent.company_id) {
397+
try {
398+
await AnalyticsService.incrementCompanyAnalytics(
399+
updatedEvent.company_id,
400+
'events_published',
401+
1
402+
)
403+
} catch (analyticsError) {
404+
console.error('Error tracking event publication analytics:', analyticsError)
405+
// Don't fail the approval if analytics tracking fails
406+
}
407+
}
408+
394409
return updatedEvent as Event
395410
}
396411

@@ -619,6 +634,20 @@ class ModerationService {
619634
// Log the moderation action
620635
await this.logModerationAction('approved', undefined, hackathonId, adminId, notes)
621636

637+
// Track analytics for hackathon publication
638+
if (updatedHackathon.company_id) {
639+
try {
640+
await AnalyticsService.incrementCompanyAnalytics(
641+
updatedHackathon.company_id,
642+
'hackathons_published',
643+
1
644+
)
645+
} catch (analyticsError) {
646+
console.error('Error tracking hackathon publication analytics:', analyticsError)
647+
// Don't fail the approval if analytics tracking fails
648+
}
649+
}
650+
622651
return updatedHackathon as Hackathon
623652
}
624653

0 commit comments

Comments
 (0)