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
2 changes: 1 addition & 1 deletion lib/services/analytics-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export class AnalyticsService {
/**
* Increment a specific field in company analytics
*/
private static async incrementCompanyAnalytics(
static async incrementCompanyAnalytics(
companyId: string,
field: string,
increment: number
Expand Down
37 changes: 25 additions & 12 deletions lib/services/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { createClient } from '@/lib/supabase/server'
import { Event, EventsFilters, EventsResponse } from '@/types/events'
import { companyService } from './company-service'
import { AnalyticsService } from './analytics-service'

// Re-export types for convenience
export type { Event, EventsFilters, EventsResponse }
Expand Down Expand Up @@ -57,7 +58,7 @@ class EventsService {
}

const supabase = await createClient()

let query = supabase
.from('events')
.select(`
Expand Down Expand Up @@ -160,7 +161,7 @@ class EventsService {
}

const supabase = await createClient()

const { data: event, error } = await supabase
.from('events')
.select(`
Expand Down Expand Up @@ -204,7 +205,7 @@ class EventsService {
}

const supabase = await createClient()

const { data: event, error } = await supabase
.from('events')
.select(`
Expand Down Expand Up @@ -244,7 +245,7 @@ class EventsService {

try {
const supabase = await createClient()

const { data: events, error } = await supabase
.from('events')
.select(`
Expand Down Expand Up @@ -356,12 +357,12 @@ class EventsService {
// Payment constraint: if price is "Free", payment must be "Not Required"
// if price is not "Free", payment must be "Required"
const paymentValue = eventData.price === 'Free' ? 'Not Required' : (eventData.payment || 'Required')

// Remove status field - events start as draft by default
// They can be submitted for approval later via submitForApproval
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { status, ...eventDataWithoutStatus } = eventData

const eventPayload = {
...eventDataWithoutStatus,
company_id: companyId,
Expand Down Expand Up @@ -393,6 +394,18 @@ class EventsService {
throw new EventError('Failed to create event', EventErrorCodes.NOT_FOUND, 500)
}

// Track analytics for event creation
try {
await AnalyticsService.incrementCompanyAnalytics(
companyId,
'events_created',
1
)
} catch (analyticsError) {
console.error('Error tracking event creation analytics:', analyticsError)
// Don't fail the event creation if analytics tracking fails
}

clearCache()
return event
}
Expand Down Expand Up @@ -438,7 +451,7 @@ class EventsService {
// Check if this is an approved event being edited
// If so, reset to pending status for re-approval
const needsReapproval = existingEvent.approval_status === 'approved'

// Update the updated_at timestamp
const updatePayload: Record<string, unknown> = {
...updateData,
Expand Down Expand Up @@ -479,17 +492,17 @@ class EventsService {
// Import notification service dynamically to avoid circular dependencies
const { NotificationService } = await import('./notification-service')
const { moderationService } = await import('./moderation-service')

// Log the edit action
await moderationService.logModerationAction('edited', id, undefined, userId, 'Event edited after approval - requires re-approval')

// Notify admins about the updated event
// Get all admin users
const { data: adminUsers } = await supabase
.from('profiles')
.select('id')
.eq('role', 'admin')

if (adminUsers && adminUsers.length > 0) {
// Create notifications for all admins
const notifications = adminUsers.map(admin => ({
Expand All @@ -506,11 +519,11 @@ class EventsService {
event_slug: existingEvent.slug
}
}))

await supabase.from('notifications').insert(notifications)
console.log(`📧 Notified ${adminUsers.length} admin(s) about updated event`)
}

// Notify company members about the status change
if (existingEvent.company_id) {
await NotificationService.notifyCompanyMembers(existingEvent.company_id, {
Expand Down
43 changes: 29 additions & 14 deletions lib/services/hackathons.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Server-side service for hackathons
import { createClient } from '@/lib/supabase/server'
import { Hackathon, HackathonsFilters, HackathonsResponse } from '@/types/hackathons'
import { AnalyticsService } from './analytics-service'

// Re-export types for convenience
export type { Hackathon, HackathonsFilters, HackathonsResponse }
Expand Down Expand Up @@ -30,7 +31,7 @@ class HackathonsService {
}

const supabase = await createClient()

let query = supabase
.from('hackathons')
.select(`
Expand Down Expand Up @@ -118,7 +119,7 @@ class HackathonsService {
}

const supabase = await createClient()

const { data: hackathon, error } = await supabase
.from('hackathons')
.select(`
Expand Down Expand Up @@ -149,7 +150,7 @@ class HackathonsService {

try {
const supabase = await createClient()

const { data: hackathons, error } = await supabase
.from('hackathons')
.select(`
Expand Down Expand Up @@ -180,7 +181,7 @@ class HackathonsService {

async createHackathon(hackathonData: Omit<Hackathon, 'id' | 'created_at' | 'updated_at'>): Promise<Hackathon> {
const supabase = await createClient()

const { data: hackathon, error } = await supabase
.from('hackathons')
.insert([hackathonData])
Expand All @@ -192,18 +193,32 @@ class HackathonsService {
throw new Error('Failed to create hackathon')
}

// Track analytics for hackathon creation
if (hackathonData.company_id) {
try {
await AnalyticsService.incrementCompanyAnalytics(
hackathonData.company_id,
'hackathons_created',
1
)
} catch (analyticsError) {
console.error('Error tracking hackathon creation analytics:', analyticsError)
// Don't fail the hackathon creation if analytics tracking fails
}
}

// Clear cache after creating new hackathon
cache.clear()
return hackathon
}

async updateHackathon(
slug: string,
slug: string,
hackathonData: Partial<Omit<Hackathon, 'id' | 'created_at' | 'updated_at'>>,
userId?: string
): Promise<Hackathon> {
const supabase = await createClient()

// Get existing hackathon first
const existingHackathon = await this.getHackathonBySlug(slug)
if (!existingHackathon) {
Expand All @@ -212,7 +227,7 @@ class HackathonsService {

// Check if this is an approved hackathon being edited
const needsReapproval = existingHackathon.approval_status === 'approved'

// Prepare update payload
const updatePayload: Record<string, unknown> = {
...hackathonData,
Expand Down Expand Up @@ -253,16 +268,16 @@ class HackathonsService {
// Import services dynamically to avoid circular dependencies
const { NotificationService } = await import('./notification-service')
const { moderationService } = await import('./moderation-service')

// Log the edit action
await moderationService.logModerationAction('edited', undefined, hackathonId, userId, 'Hackathon edited after approval - requires re-approval')

// Notify admins about the updated hackathon
const { data: adminUsers } = await supabase
.from('profiles')
.select('id')
.eq('role', 'admin')

if (adminUsers && adminUsers.length > 0) {
const notifications = adminUsers.map(admin => ({
user_id: admin.id,
Expand All @@ -278,11 +293,11 @@ class HackathonsService {
hackathon_slug: existingHackathon.slug
}
}))

await supabase.from('notifications').insert(notifications)
console.log(`📧 Notified ${adminUsers.length} admin(s) about updated hackathon`)
}

// Notify company members about the status change
if (existingHackathon.company_id) {
await NotificationService.notifyCompanyMembers(existingHackathon.company_id, {
Expand All @@ -309,9 +324,9 @@ class HackathonsService {

async deleteHackathon(slug: string) {
const supabase = await createClient()

console.log('🗑️ Deleting hackathon with slug:', slug)

const { data, error } = await supabase
.from('hackathons')
.delete()
Expand Down
29 changes: 29 additions & 0 deletions lib/services/moderation-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Event } from '@/types/events'
import { Hackathon } from '@/types/hackathons'
import { companyService } from './company-service'
import { SUBSCRIPTION_LIMITS } from '@/types/company'
import { AnalyticsService } from './analytics-service'

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

// Track analytics for event publication
if (updatedEvent.company_id) {
try {
await AnalyticsService.incrementCompanyAnalytics(
updatedEvent.company_id,
'events_published',
1
)
} catch (analyticsError) {
console.error('Error tracking event publication analytics:', analyticsError)
// Don't fail the approval if analytics tracking fails
}
}

return updatedEvent as Event
}

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

// Track analytics for hackathon publication
if (updatedHackathon.company_id) {
try {
await AnalyticsService.incrementCompanyAnalytics(
updatedHackathon.company_id,
'hackathons_published',
1
)
} catch (analyticsError) {
console.error('Error tracking hackathon publication analytics:', analyticsError)
// Don't fail the approval if analytics tracking fails
}
}

return updatedHackathon as Hackathon
}

Expand Down
Loading