Skip to content

Commit 621453e

Browse files
authored
Merge pull request #337 from codeunia-dev/feat/companydashboard
feat(hackathons): Add delete functionality with confirmation dialog and enhanced logging
2 parents 81c610f + 4d64cec commit 621453e

File tree

3 files changed

+117
-12
lines changed

3 files changed

+117
-12
lines changed

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

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,27 +113,35 @@ export async function DELETE(_request: NextRequest, { params }: RouteContext) {
113113
try {
114114
const { id } = await params
115115

116+
console.log('🗑️ DELETE request for hackathon:', id)
117+
116118
// Check authentication
117119
const supabase = await createClient()
118-
const { data: { user } } = await supabase.auth.getUser()
120+
const { data: { user }, error: authError } = await supabase.auth.getUser()
119121

120-
if (!user) {
122+
if (authError || !user) {
123+
console.error('❌ Authentication error:', authError)
121124
return NextResponse.json(
122125
{ error: 'Unauthorized: Authentication required' },
123126
{ status: 401 }
124127
)
125128
}
126129

130+
console.log('✅ User authenticated:', user.id)
131+
127132
// Get the existing hackathon to check company_id
128133
const existingHackathon = await hackathonsService.getHackathonBySlug(id)
129134

130135
if (!existingHackathon) {
136+
console.error('❌ Hackathon not found:', id)
131137
return NextResponse.json(
132138
{ error: 'Hackathon not found' },
133139
{ status: 404 }
134140
)
135141
}
136142

143+
console.log('✅ Hackathon found:', existingHackathon.id, existingHackathon.title)
144+
137145
let isAuthorized = false
138146

139147
// Check if user is admin
@@ -145,6 +153,7 @@ export async function DELETE(_request: NextRequest, { params }: RouteContext) {
145153

146154
if (profile?.is_admin) {
147155
isAuthorized = true
156+
console.log('✅ User is admin')
148157
}
149158

150159
// If not admin, check if user is a member of the company
@@ -159,23 +168,30 @@ export async function DELETE(_request: NextRequest, { params }: RouteContext) {
159168

160169
if (membership) {
161170
isAuthorized = true
171+
console.log('✅ User is company member with role:', membership.role)
162172
}
163173
}
164174

165175
if (!isAuthorized) {
176+
console.error('❌ User not authorized to delete hackathon')
166177
return NextResponse.json(
167178
{ error: 'Unauthorized: You must be a company member or admin to delete this hackathon' },
168-
{ status: 401 }
179+
{ status: 403 }
169180
)
170181
}
171182

183+
console.log('🗑️ Attempting to delete hackathon...')
172184
await hackathonsService.deleteHackathon(id)
185+
console.log('✅ Hackathon deleted successfully')
173186

174-
return NextResponse.json({ message: 'Hackathon deleted successfully' })
187+
return NextResponse.json({
188+
success: true,
189+
message: 'Hackathon deleted successfully'
190+
})
175191
} catch (error) {
176-
console.error('Error in DELETE /api/hackathons/[id]:', error)
192+
console.error('Error in DELETE /api/hackathons/[id]:', error)
177193
return NextResponse.json(
178-
{ error: 'Failed to delete hackathon' },
194+
{ error: error instanceof Error ? error.message : 'Failed to delete hackathon' },
179195
{ status: 500 }
180196
)
181197
}

app/dashboard/company/[slug]/hackathons/page.tsx

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,19 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
88
import { Badge } from '@/components/ui/badge'
99
import { Input } from '@/components/ui/input'
1010
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
11-
import { Trophy, Search, Plus, Edit, Eye, Clock, CheckCircle, XCircle, AlertCircle } from 'lucide-react'
11+
import { Trophy, Search, Plus, Edit, Eye, Clock, CheckCircle, XCircle, AlertCircle, Trash2 } from 'lucide-react'
1212
import { toast } from 'sonner'
1313
import Link from 'next/link'
14+
import {
15+
AlertDialog,
16+
AlertDialogAction,
17+
AlertDialogCancel,
18+
AlertDialogContent,
19+
AlertDialogDescription,
20+
AlertDialogFooter,
21+
AlertDialogHeader,
22+
AlertDialogTitle,
23+
} from '@/components/ui/alert-dialog'
1424

1525
interface Hackathon {
1626
id: string
@@ -34,6 +44,9 @@ export default function CompanyHackathonsPage() {
3444
const [hackathons, setHackathons] = useState<Hackathon[]>([])
3545
const [loading, setLoading] = useState(true)
3646
const [searchTerm, setSearchTerm] = useState('')
47+
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
48+
const [hackathonToDelete, setHackathonToDelete] = useState<Hackathon | null>(null)
49+
const [isDeleting, setIsDeleting] = useState(false)
3750

3851
const canManageEvents = userRole && ['owner', 'admin', 'editor'].includes(userRole)
3952

@@ -65,6 +78,40 @@ export default function CompanyHackathonsPage() {
6578
}
6679
}, [currentCompany, fetchHackathons])
6780

81+
const handleDeleteClick = (hackathon: Hackathon) => {
82+
setHackathonToDelete(hackathon)
83+
setDeleteDialogOpen(true)
84+
}
85+
86+
const handleDeleteConfirm = async () => {
87+
if (!hackathonToDelete || !currentCompany) return
88+
89+
try {
90+
setIsDeleting(true)
91+
// Use slug instead of id for the API endpoint
92+
const response = await fetch(`/api/hackathons/${hackathonToDelete.slug}`, {
93+
method: 'DELETE',
94+
})
95+
96+
if (!response.ok) {
97+
const errorData = await response.json()
98+
throw new Error(errorData.error || 'Failed to delete hackathon')
99+
}
100+
101+
toast.success('Hackathon deleted successfully')
102+
setDeleteDialogOpen(false)
103+
setHackathonToDelete(null)
104+
105+
// Refresh the list
106+
fetchHackathons()
107+
} catch (error) {
108+
console.error('Error deleting hackathon:', error)
109+
toast.error(error instanceof Error ? error.message : 'Failed to delete hackathon')
110+
} finally {
111+
setIsDeleting(false)
112+
}
113+
}
114+
68115
if (companyLoading || isPendingInvitation) {
69116
return (
70117
<div className="flex items-center justify-center min-h-[60vh]">
@@ -287,17 +334,26 @@ export default function CompanyHackathonsPage() {
287334
<TableCell>
288335
<div className="flex items-center gap-2">
289336
<Link href={`/dashboard/company/${currentCompany.slug}/hackathons/${hackathon.slug}/edit`}>
290-
<Button variant="outline" size="sm">
337+
<Button variant="outline" size="sm" title="Edit hackathon">
291338
<Edit className="h-4 w-4" />
292339
</Button>
293340
</Link>
294341
{hackathon.approval_status === 'approved' && (
295342
<Link href={`/hackathons/${hackathon.slug}`} target="_blank">
296-
<Button variant="outline" size="sm">
343+
<Button variant="outline" size="sm" title="View public page">
297344
<Eye className="h-4 w-4" />
298345
</Button>
299346
</Link>
300347
)}
348+
<Button
349+
variant="outline"
350+
size="sm"
351+
onClick={() => handleDeleteClick(hackathon)}
352+
className="text-red-600 hover:text-red-700 hover:bg-red-50"
353+
title="Delete hackathon"
354+
>
355+
<Trash2 className="h-4 w-4" />
356+
</Button>
301357
</div>
302358
</TableCell>
303359
) : (
@@ -318,6 +374,29 @@ export default function CompanyHackathonsPage() {
318374
)}
319375
</CardContent>
320376
</Card>
377+
378+
{/* Delete Confirmation Dialog */}
379+
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
380+
<AlertDialogContent>
381+
<AlertDialogHeader>
382+
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
383+
<AlertDialogDescription>
384+
This will permanently delete the hackathon &quot;{hackathonToDelete?.title}&quot;.
385+
This action cannot be undone.
386+
</AlertDialogDescription>
387+
</AlertDialogHeader>
388+
<AlertDialogFooter>
389+
<AlertDialogCancel disabled={isDeleting}>Cancel</AlertDialogCancel>
390+
<AlertDialogAction
391+
onClick={handleDeleteConfirm}
392+
disabled={isDeleting}
393+
className="bg-red-600 hover:bg-red-700"
394+
>
395+
{isDeleting ? 'Deleting...' : 'Delete'}
396+
</AlertDialogAction>
397+
</AlertDialogFooter>
398+
</AlertDialogContent>
399+
</AlertDialog>
321400
</div>
322401
)
323402
}

lib/services/hackathons.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,16 +310,26 @@ class HackathonsService {
310310
async deleteHackathon(slug: string) {
311311
const supabase = await createClient()
312312

313-
const { error } = await supabase
313+
console.log('🗑️ Deleting hackathon with slug:', slug)
314+
315+
const { data, error } = await supabase
314316
.from('hackathons')
315317
.delete()
316318
.eq('slug', slug)
319+
.select()
317320

318321
if (error) {
319-
console.error('Error deleting hackathon:', error)
320-
throw new Error('Failed to delete hackathon')
322+
console.error('Error deleting hackathon from database:', error)
323+
throw new Error(`Failed to delete hackathon: ${error.message}`)
321324
}
322325

326+
if (!data || data.length === 0) {
327+
console.warn('⚠️ No hackathon was deleted. Slug might not exist or RLS policy blocked deletion.')
328+
throw new Error('Hackathon not found or you do not have permission to delete it')
329+
}
330+
331+
console.log('✅ Hackathon deleted from database:', data)
332+
323333
// Clear cache after deleting hackathon
324334
cache.clear()
325335
return true

0 commit comments

Comments
 (0)