Skip to content

Commit 2b8401a

Browse files
committed
Merge branch 'main' of github.com:boundlessfi/boundless
2 parents f7dc1d0 + 609b63c commit 2b8401a

4 files changed

Lines changed: 81 additions & 31 deletions

File tree

app/(landing)/organizations/[id]/hackathons/page.tsx

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export default function HackathonsPage() {
137137
const [hackathonToDelete, setHackathonToDelete] = useState<{
138138
id: string;
139139
title: string;
140+
type: 'draft' | 'hackathon';
140141
} | null>(null);
141142

142143
const { hackathons, hackathonsLoading, drafts, draftsLoading, refetchAll } =
@@ -148,13 +149,12 @@ export default function HackathonsPage() {
148149
// Use the separate delete hook
149150
const { isDeleting, deleteHackathon } = useDeleteHackathon({
150151
organizationId,
151-
hackathonId: hackathonToDelete?.id || '', // This will be set when we have a hackathon to delete
152+
hackathonId: hackathonToDelete?.id || '',
153+
type: hackathonToDelete?.type ?? 'hackathon',
154+
suppressToast: true,
152155
onSuccess: () => {
153156
// Refresh the hackathons list after successful deletion
154157
refetchAll();
155-
toast.success('Hackathon deleted successfully', {
156-
description: `"${hackathonToDelete?.title}" has been permanently deleted.`,
157-
});
158158
},
159159
onError: error => {
160160
toast.error('Failed to delete hackathon', {
@@ -217,37 +217,47 @@ export default function HackathonsPage() {
217217
return { published, drafts: drafts.length, total };
218218
}, [hackathons, drafts]);
219219

220-
const handleDeleteClick = (hackathonId: string) => {
221-
// Try to find in published hackathons
222-
const published = publishedHackathons.find(h => h.id === hackathonId);
223-
if (published) {
224-
setHackathonToDelete({
225-
id: hackathonId,
226-
title: published.name || 'Untitled Hackathon',
227-
});
228-
setDeleteDialogOpen(true);
229-
return;
230-
}
231-
// Try to find in drafts
232-
const draft = draftHackathons.find(d => d.id === hackathonId);
233-
if (draft) {
234-
setHackathonToDelete({
235-
id: hackathonId,
236-
title: draft.data.information?.name || 'Untitled Hackathon',
237-
});
238-
setDeleteDialogOpen(true);
220+
const handleDeleteClick = (
221+
hackathonId: string,
222+
type: 'draft' | 'hackathon'
223+
) => {
224+
if (type === 'hackathon') {
225+
const published = publishedHackathons.find(h => h.id === hackathonId);
226+
if (published) {
227+
setHackathonToDelete({
228+
id: hackathonId,
229+
title: published.name || 'Untitled Hackathon',
230+
type: 'hackathon',
231+
});
232+
setDeleteDialogOpen(true);
233+
}
234+
} else {
235+
const draft = draftHackathons.find(d => d.id === hackathonId);
236+
if (draft) {
237+
setHackathonToDelete({
238+
id: hackathonId,
239+
title: draft.data.information?.name || 'Untitled Hackathon',
240+
type: 'draft',
241+
});
242+
setDeleteDialogOpen(true);
243+
}
239244
}
240245
};
241246

242247
const handleDeleteConfirm = async () => {
243248
if (!hackathonToDelete) return;
244249

250+
const { title } = hackathonToDelete; // ✅ snapshot before state clears
251+
245252
setDeleteDialogOpen(false);
246253

247254
try {
248255
await deleteHackathon();
256+
toast.success('Hackathon deleted successfully', {
257+
description: `"${title}" has been permanently deleted.`,
258+
});
249259
} catch {
250-
// Error handled by toast in deleteHackathon hook
260+
// error toast handled by onError in hook
251261
} finally {
252262
setHackathonToDelete(null);
253263
}
@@ -520,7 +530,7 @@ export default function HackathonsPage() {
520530
<button
521531
onClick={e => {
522532
e.stopPropagation();
523-
handleDeleteClick(hackathon.id);
533+
handleDeleteClick(hackathon.id, 'hackathon');
524534
}}
525535
className='flex h-9 w-9 items-center justify-center rounded-lg border border-zinc-800 bg-zinc-900/70 text-zinc-400 hover:border-red-600 hover:text-red-500'
526536
title='Delete Hackathon'
@@ -636,7 +646,7 @@ export default function HackathonsPage() {
636646
<button
637647
onClick={e => {
638648
e.stopPropagation();
639-
handleDeleteClick(draft.id);
649+
handleDeleteClick(draft.id, 'draft');
640650
}}
641651
className='flex h-9 w-9 items-center justify-center rounded-lg border border-zinc-800 bg-zinc-900/70 text-zinc-400 hover:border-red-600 hover:text-red-500'
642652
title='Delete Draft'

hooks/hackathon/use-delete-hackathon.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,28 @@
22

33
import { useState, useCallback } from 'react';
44
import { deleteHackathon } from '@/lib/api/hackathons';
5+
import { deleteDraft } from '@/lib/api/hackathons/draft';
56
import { useAuthStatus } from '@/hooks/use-auth';
67
import { toast } from 'sonner';
78

9+
type DeleteType = 'draft' | 'hackathon';
10+
811
interface UseDeleteHackathonOptions {
912
organizationId: string;
1013
hackathonId: string;
14+
type: DeleteType;
1115
onSuccess?: () => void;
1216
onError?: (error: string) => void;
17+
suppressToast?: boolean;
1318
}
1419

1520
export function useDeleteHackathon({
1621
organizationId,
1722
hackathonId,
23+
type,
1824
onSuccess,
1925
onError,
26+
suppressToast = false,
2027
}: UseDeleteHackathonOptions) {
2128
const { isAuthenticated } = useAuthStatus();
2229
const [isDeleting, setIsDeleting] = useState(false);
@@ -37,10 +44,18 @@ export function useDeleteHackathon({
3744
setError(null);
3845

3946
try {
40-
const response = await deleteHackathon(hackathonId);
47+
let response;
48+
49+
if (type === 'draft') {
50+
response = await deleteDraft(hackathonId, organizationId);
51+
} else {
52+
response = await deleteHackathon(hackathonId);
53+
}
4154

4255
if (response.success) {
43-
toast.success('Hackathon deleted successfully');
56+
if (!suppressToast) {
57+
toast.success('Hackathon deleted successfully');
58+
}
4459
onSuccess?.();
4560
return response.data;
4661
} else {
@@ -50,13 +65,15 @@ export function useDeleteHackathon({
5065
const errorMessage =
5166
err instanceof Error ? err.message : 'Failed to delete hackathon';
5267
setError(errorMessage);
53-
toast.error(errorMessage);
68+
if (!suppressToast) {
69+
toast.error(errorMessage);
70+
}
5471
onError?.(errorMessage);
5572
throw err;
5673
} finally {
5774
setIsDeleting(false);
5875
}
59-
}, [organizationId, hackathonId, isAuthenticated, onSuccess, onError]);
76+
}, [organizationId, hackathonId, isAuthenticated, type, onSuccess, onError]);
6077

6178
return {
6279
isDeleting,

lib/api/hackathons/draft.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ export interface PublishHackathonResponse extends ApiResponse<Hackathon> {
3838
message: string;
3939
}
4040

41+
export interface DeleteHackathonResponse extends ApiResponse<null> {
42+
success: true;
43+
message: string;
44+
data: null;
45+
}
46+
4147
/**
4248
* Initialize a new hackathon draft
4349
*/
@@ -86,6 +92,24 @@ export const publishDraft = async (
8692
return res.data.data as PublishHackathonResponse;
8793
};
8894

95+
/**
96+
* Delete Draft Hackathon
97+
*/
98+
export const deleteDraft = async (
99+
draftId: string,
100+
organizationId: string
101+
): Promise<DeleteHackathonResponse> => {
102+
const res = await api.delete<DeleteHackathonResponse>(
103+
`/organizations/${organizationId}/hackathons/draft/${draftId}`
104+
);
105+
106+
if (res.status === 204) {
107+
return { success: true, message: 'Deleted successfully', data: null };
108+
}
109+
110+
return res.data as DeleteHackathonResponse;
111+
};
112+
89113
/**
90114
* Get a single hackathon draft by ID
91115
*/

lib/api/hackathons/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ export {
7171

7272
// Re-export all API functions and response types
7373
export * from './core';
74-
export * from './draft';
7574
export * from './participants';
7675
export * from './judging';
7776
export * from './rewards';

0 commit comments

Comments
 (0)