diff --git a/app/(landing)/organizations/[id]/hackathons/page.tsx b/app/(landing)/organizations/[id]/hackathons/page.tsx
index 9a9935184..bae47be3d 100644
--- a/app/(landing)/organizations/[id]/hackathons/page.tsx
+++ b/app/(landing)/organizations/[id]/hackathons/page.tsx
@@ -32,7 +32,7 @@ const calculateDraftCompletion = (draft: HackathonDraft): number => {
draft.information?.title,
draft.information?.banner,
draft.information?.description,
- draft.information?.category,
+ draft.information?.categories,
draft.timeline?.startDate,
draft.timeline?.submissionDeadline,
draft.timeline?.judgingDate,
@@ -123,8 +123,9 @@ export default function HackathonsPage() {
if (categoryFilter !== 'all') {
filtered = filtered.filter(item => {
- const category = item.data.information?.category?.toLowerCase() || '';
- return category === categoryFilter.toLowerCase();
+ const category =
+ item.data.information?.categories?.join(',')?.toLowerCase() || '';
+ return category.includes(categoryFilter.toLowerCase());
});
}
diff --git a/components/organization/hackathons/new/NewHackathonTab.tsx b/components/organization/hackathons/new/NewHackathonTab.tsx
index c4ceba7ce..175a13972 100644
--- a/components/organization/hackathons/new/NewHackathonTab.tsx
+++ b/components/organization/hackathons/new/NewHackathonTab.tsx
@@ -113,6 +113,7 @@ export default function NewHackathonTab({
const { isPublishing, publish } = useHackathonPublish({
organizationId: derivedOrgId || '',
stepData,
+ draftId,
publishHackathonAction,
});
diff --git a/components/organization/hackathons/new/tabs/InfoTab.tsx b/components/organization/hackathons/new/tabs/InfoTab.tsx
index 6292bde18..6c48054f5 100644
--- a/components/organization/hackathons/new/tabs/InfoTab.tsx
+++ b/components/organization/hackathons/new/tabs/InfoTab.tsx
@@ -52,11 +52,9 @@ export default function InfoTab({
name: initialData?.name || '',
banner: initialData?.banner || '',
description: initialData?.description || '',
- category: Array.isArray(initialData?.category)
- ? initialData.category
- : initialData?.category
- ? [initialData.category]
- : [],
+ categories: Array.isArray(initialData?.categories)
+ ? initialData.categories
+ : [],
venueType: initialData?.venueType || 'physical',
country: initialData?.country || '',
state: initialData?.state || '',
@@ -145,7 +143,7 @@ export default function InfoTab({
)}
/>
-
+
diff --git a/components/organization/hackathons/new/tabs/schemas/infoSchema.ts b/components/organization/hackathons/new/tabs/schemas/infoSchema.ts
index 0fba03a13..87d055722 100644
--- a/components/organization/hackathons/new/tabs/schemas/infoSchema.ts
+++ b/components/organization/hackathons/new/tabs/schemas/infoSchema.ts
@@ -19,7 +19,7 @@ export const infoSchema = z
.min(10, 'Description must be at least 10 characters')
.max(5000, 'Description must be less than 5000 characters'),
- category: z
+ categories: z
.array(
z.enum([
'DeFi',
diff --git a/components/organization/hackathons/settings/GeneralSettingsTab.tsx b/components/organization/hackathons/settings/GeneralSettingsTab.tsx
index f98481b00..835958ceb 100644
--- a/components/organization/hackathons/settings/GeneralSettingsTab.tsx
+++ b/components/organization/hackathons/settings/GeneralSettingsTab.tsx
@@ -78,11 +78,9 @@ export default function GeneralSettingsTab({
name: initialData?.name || '',
banner: initialData?.banner || '',
description: initialData?.description || '',
- category: Array.isArray(initialData?.category)
- ? initialData.category
- : initialData?.category
- ? [initialData.category]
- : [],
+ categories: Array.isArray(initialData?.categories)
+ ? initialData.categories
+ : [],
venueType: initialData?.venueType || 'virtual',
country: initialData?.country || '',
state: initialData?.state || '',
@@ -205,7 +203,7 @@ export default function GeneralSettingsTab({
)}
/>
-
+
Promise<{ _id: string }>;
@@ -48,6 +49,7 @@ interface UseHackathonPublishProps {
export const useHackathonPublish = ({
organizationId,
stepData,
+ draftId,
publishHackathonAction,
}: UseHackathonPublishProps) => {
const router = useRouter();
@@ -196,6 +198,9 @@ export const useHackathonPublish = ({
toast.info('Publishing hackathon...');
const apiData = transformToApiFormat(stepData) as PublishHackathonRequest;
+ if (draftId) {
+ apiData.draftId = draftId;
+ }
apiData.contractId = contractId;
apiData.escrowAddress = escrowAddress;
apiData.transactionHash = transactionHash;
diff --git a/lib/api/hackathons.ts b/lib/api/hackathons.ts
index 778880906..614694f57 100644
--- a/lib/api/hackathons.ts
+++ b/lib/api/hackathons.ts
@@ -42,10 +42,9 @@ export interface HackathonInformation {
title: string;
banner: string;
description: string;
- category?: HackathonCategory; // Legacy format (single category)
- categories?: HackathonCategory[]; // New format (array of categories)
- venue?: HackathonVenue;
+ categories: HackathonCategory[]; // New format (array of categories)
slug: string;
+ venue?: HackathonVenue;
}
// Timeline Tab Types
@@ -177,6 +176,7 @@ export type CreateDraftRequest = Partial;
export type UpdateDraftRequest = Partial;
export interface PublishHackathonRequest extends HackathonData {
+ draftId?: string;
contractId?: string;
escrowAddress?: string;
transactionHash?: string;
@@ -709,7 +709,6 @@ interface FlatHackathonData {
// Flat fields that map to nested structure
banner?: string;
description?: string;
- category?: string | HackathonCategory; // Legacy format
categories?: HackathonCategory[]; // New format
venue?: HackathonVenue;
startDate?: string;
@@ -786,10 +785,7 @@ const transformHackathonResponse = (
// Support both new format (categories) and legacy format (category)
categories: Array.isArray(flat.categories)
? (flat.categories as HackathonCategory[])
- : flat.category
- ? [flat.category as HackathonCategory]
- : [HackathonCategory.OTHER],
- category: (flat.category as HackathonCategory) || HackathonCategory.OTHER,
+ : [HackathonCategory.OTHER],
venue: flat.venue,
},
timeline: {
@@ -1382,13 +1378,22 @@ export const transformPublicHackathonToHackathon = (
internalStatus = 'completed';
}
- // Get first category or default to OTHER
- const category = publicHackathon.categories?.[0] || HackathonCategory.OTHER;
- const categoryEnum = Object.values(HackathonCategory).includes(
- category as HackathonCategory
- )
- ? (category as HackathonCategory)
- : HackathonCategory.OTHER;
+ // Map all categories from API response
+ const categoriesArray: HackathonCategory[] = publicHackathon.categories
+ ? publicHackathon.categories
+ .map(cat => {
+ // Check if the category string matches any HackathonCategory enum value
+ const matchedCategory = Object.values(HackathonCategory).find(
+ enumCat => enumCat === cat
+ );
+ return matchedCategory || null;
+ })
+ .filter((cat): cat is HackathonCategory => cat !== null)
+ : [];
+
+ // Ensure at least one category
+ const categories: HackathonCategory[] =
+ categoriesArray.length > 0 ? categoriesArray : [HackathonCategory.OTHER];
// Extract resources (telegram, discord, etc.) from resources array
const telegram = publicHackathon.resources?.find(
@@ -1412,7 +1417,7 @@ export const transformPublicHackathonToHackathon = (
title: publicHackathon.title,
banner: publicHackathon.imageUrl,
description: publicHackathon.description,
- category: categoryEnum,
+ categories: categories,
slug: publicHackathon.slug,
venue,
},
diff --git a/lib/utils/hackathon-form-transforms.ts b/lib/utils/hackathon-form-transforms.ts
index 6b7089eb6..e7a429e13 100644
--- a/lib/utils/hackathon-form-transforms.ts
+++ b/lib/utils/hackathon-form-transforms.ts
@@ -28,14 +28,12 @@ export const transformToApiFormat = (stepData: {
const judging = stepData.judging;
const collaboration = stepData.collaboration;
- // Convert form category array to API format
- const categoriesArray: HackathonCategory[] = Array.isArray(info?.category)
- ? (info.category as HackathonCategory[]).filter(cat =>
+ // Convert form categories array to API format
+ const categoriesArray: HackathonCategory[] = Array.isArray(info?.categories)
+ ? (info.categories as HackathonCategory[]).filter(cat =>
Object.values(HackathonCategory).includes(cat)
)
- : info?.category && typeof info.category === 'string'
- ? [info.category as HackathonCategory]
- : [];
+ : [];
return {
information: {
@@ -48,11 +46,6 @@ export const transformToApiFormat = (stepData: {
categoriesArray.length > 0
? categoriesArray
: [HackathonCategory.OTHER],
- // Also include legacy category for backward compatibility (first category)
- category:
- categoriesArray.length > 0
- ? categoriesArray[0]
- : HackathonCategory.OTHER,
venue: {
type: (info?.venueType as VenueType) || VenueType.VIRTUAL,
country: info?.country,
@@ -144,19 +137,15 @@ export const transformFromApiFormat = (draft: HackathonDraft) => {
const judging = draft.judging;
const collaboration = draft.collaboration;
- // Handle both new format (categories array) and legacy format (category string)
- const categoriesArray: string[] = info?.categories
- ? info.categories
- : info?.category
- ? [info.category]
- : [];
+ // Handle categories array
+ const categoriesArray: string[] = info?.categories ? info.categories : [];
return {
information: {
name: info?.title || '',
banner: info?.banner || '',
description: info?.description || '',
- category: categoriesArray,
+ categories: categoriesArray,
venueType: info?.venue?.type || 'physical',
country: info?.venue?.country || '',
state: info?.venue?.state || '',