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 || '',