diff --git a/components/landing-page/hackathon/HackathonCard.tsx b/components/landing-page/hackathon/HackathonCard.tsx
index c14435d3b..9d0dd1da1 100644
--- a/components/landing-page/hackathon/HackathonCard.tsx
+++ b/components/landing-page/hackathon/HackathonCard.tsx
@@ -20,6 +20,7 @@ type HackathonCardProps = {
categories: string[];
location?: string;
venueType?: 'virtual' | 'physical';
+ participantType?: 'individual' | 'team' | 'team_or_individual';
participants?: {
current: number;
goal?: number;
@@ -76,26 +77,97 @@ function HackathonCard({
};
const getDeadlineInfo = () => {
- if (deadlineInDays <= 0)
+ // Handle completed or cancelled status
+ if (status === 'Completed') {
return { text: 'Ended', className: 'text-gray-500' };
- if (deadlineInDays <= 3)
- return { text: `${deadlineInDays} days left`, className: 'text-red-400' };
- if (deadlineInDays <= 15)
+ }
+ if (status === 'Cancelled') {
+ return { text: 'Cancelled', className: 'text-gray-500' };
+ }
+
+ // Handle cases where deadline has passed
+ if (deadlineInDays <= 0) {
+ if (status === 'Ongoing') {
+ return { text: 'Ending soon', className: 'text-red-400' };
+ }
+ return { text: 'Started', className: 'text-gray-500' };
+ }
+
+ // Handle different statuses with contextual messages
+ if (status === 'Published') {
+ // Upcoming hackathons - show "starting in X days"
+ if (deadlineInDays <= 3) {
+ return {
+ text: `starting in ${deadlineInDays} day${deadlineInDays !== 1 ? 's' : ''}`,
+ className: 'text-red-400',
+ };
+ }
+ if (deadlineInDays <= 15) {
+ return {
+ text: `starting in ${deadlineInDays} days`,
+ className: 'text-yellow-400',
+ };
+ }
+ return {
+ text: `starting in ${deadlineInDays} days`,
+ className: 'text-green-400',
+ };
+ }
+
+ if (status === 'Ongoing') {
+ // Ongoing hackathons - show "ending in X days"
+ if (deadlineInDays <= 3) {
+ return {
+ text: `ending in ${deadlineInDays} day${deadlineInDays !== 1 ? 's' : ''}`,
+ className: 'text-red-400',
+ };
+ }
+ if (deadlineInDays <= 15) {
+ return {
+ text: `ending in ${deadlineInDays} days`,
+ className: 'text-yellow-400',
+ };
+ }
+ return {
+ text: `ending in ${deadlineInDays} days`,
+ className: 'text-green-400',
+ };
+ }
+
+ // Fallback for any other status
+ if (deadlineInDays <= 3) {
+ return {
+ text: `${deadlineInDays} day${deadlineInDays !== 1 ? 's' : ''} left`,
+ className: 'text-red-400',
+ };
+ }
+ if (deadlineInDays <= 15) {
return {
text: `${deadlineInDays} days left`,
className: 'text-yellow-400',
};
- return { text: `${deadlineInDays} days left`, className: 'text-green-400' };
+ }
+ return {
+ text: `${deadlineInDays} days left`,
+ className: 'text-green-400',
+ };
};
const deadlineInfo = getDeadlineInfo();
- const locationText =
- location ||
- (venueType === 'virtual'
- ? 'Virtual'
- : venueType === 'physical'
- ? 'Physical'
- : 'TBD');
+ const locationText = (() => {
+ // If location is provided (from transform hook), use it
+ if (location) {
+ return location;
+ }
+ // Otherwise, fall back to venueType
+ if (venueType === 'virtual') {
+ return 'Virtual';
+ }
+ if (venueType === 'physical') {
+ return 'Physical';
+ }
+ return undefined;
+ })();
const CategoriesDisplay = ({
categoriesList,
@@ -211,7 +283,9 @@ function HackathonCard({
-
+
{deadlineInfo.text}
{participants?.goal && (
diff --git a/hooks/hackathon/use-hackathon-transform.ts b/hooks/hackathon/use-hackathon-transform.ts
index 3a386b5d1..7d380cc96 100644
--- a/hooks/hackathon/use-hackathon-transform.ts
+++ b/hooks/hackathon/use-hackathon-transform.ts
@@ -25,6 +25,7 @@ interface TransformedHackathon {
categories: string[];
location?: string;
venueType?: 'virtual' | 'physical';
+ participantType?: 'individual' | 'team' | 'team_or_individual';
participants?: {
current: number;
goal?: number;
@@ -76,10 +77,20 @@ export function useHackathonTransform() {
const venue = hackathon.information?.venue;
let locationText: string | undefined;
if (venue) {
- if (venue.type === 'physical' && venue.city && venue.country) {
- locationText = `${venue.city}, ${venue.country}`;
- } else if (venue.type === 'physical' && venue.country) {
- locationText = venue.country;
+ if (venue.type === 'physical') {
+ // For physical venues, prioritize city + country, then country, then state, then venue name
+ if (venue.city && venue.country) {
+ locationText = `${venue.city}, ${venue.country}`;
+ } else if (venue.country) {
+ locationText = venue.country;
+ } else if (venue.state) {
+ locationText = venue.state;
+ } else if (venue.venueName) {
+ locationText = venue.venueName;
+ } else if (venue.venueAddress) {
+ locationText = venue.venueAddress;
+ }
+ // If still no location, we'll rely on venueType in the card component
} else if (venue.type === 'virtual') {
locationText = 'Virtual';
}
@@ -136,6 +147,9 @@ export function useHackathonTransform() {
categories.push('Other');
}
+ // Extract participantType
+ const participantType = hackathon.participation?.participantType;
+
return {
hackathonId: hackathon._id,
organizationName: orgName,
@@ -154,7 +168,14 @@ export function useHackathonTransform() {
deadlineInDays: Math.max(0, deadlineInDays),
categories: categories,
location: locationText,
- venueType: venue?.type,
+ venueType: venue?.type
+ ? venue.type === 'virtual'
+ ? 'virtual'
+ : 'physical'
+ : undefined,
+ participantType: participantType
+ ? (participantType as 'individual' | 'team' | 'team_or_individual')
+ : undefined,
participants: {
current: extendedHackathon.participants || 0, // Use participants from public API if available
// goal could be calculated from participation settings
diff --git a/lib/api/hackathons.ts b/lib/api/hackathons.ts
index 81439687a..a0471861c 100644
--- a/lib/api/hackathons.ts
+++ b/lib/api/hackathons.ts
@@ -44,7 +44,7 @@ export interface HackathonInformation {
description: string;
category?: HackathonCategory; // Legacy format (single category)
categories?: HackathonCategory[]; // New format (array of categories)
- venue: HackathonVenue;
+ venue?: HackathonVenue;
}
// Timeline Tab Types
@@ -84,7 +84,7 @@ export interface TabVisibility {
}
export interface HackathonParticipation {
- participantType: ParticipantType;
+ participantType?: ParticipantType;
teamMin?: number;
teamMax?: number;
about?: string;
@@ -654,6 +654,15 @@ export interface PublicHackathon {
organizer: string;
featured: boolean;
resources: string[];
+ venue?: {
+ type: 'virtual' | 'physical';
+ country?: string;
+ state?: string;
+ city?: string;
+ venueName?: string;
+ venueAddress?: string;
+ };
+ participantType?: 'individual' | 'team' | 'team_or_individual';
}
export interface PublicHackathonsListData {
@@ -778,9 +787,7 @@ const transformHackathonResponse = (
? [flat.category as HackathonCategory]
: [HackathonCategory.OTHER],
category: (flat.category as HackathonCategory) || HackathonCategory.OTHER,
- venue: flat.venue || {
- type: VenueType.VIRTUAL,
- },
+ venue: flat.venue,
},
timeline: {
startDate: flat.startDate || '',
@@ -791,8 +798,7 @@ const transformHackathonResponse = (
phases: flat.phases || [],
},
participation: {
- participantType:
- (flat.participantType as ParticipantType) || ParticipantType.INDIVIDUAL,
+ participantType: flat.participantType as ParticipantType | undefined,
teamMin: flat.teamMin,
teamMax: flat.teamMax,
about: flat.about,
@@ -1348,11 +1354,20 @@ export const transformPublicHackathonToHackathon = (
const prizePoolAmount =
parseFloat(publicHackathon.totalPrizePool.replace(/,/g, '')) || 0;
- // Determine venue type from location or default to virtual
- // Since API doesn't provide venue details, we'll default to virtual
- const venue: HackathonVenue = {
- type: VenueType.VIRTUAL,
- };
+ // Extract venue from API response or default to virtual
+ const venue: HackathonVenue | undefined = publicHackathon.venue
+ ? {
+ type:
+ publicHackathon.venue.type === 'physical'
+ ? VenueType.PHYSICAL
+ : VenueType.VIRTUAL,
+ country: publicHackathon.venue.country,
+ state: publicHackathon.venue.state,
+ city: publicHackathon.venue.city,
+ venueName: publicHackathon.venue.venueName,
+ venueAddress: publicHackathon.venue.venueAddress,
+ }
+ : undefined;
// Map API status to internal status
// API uses: upcoming, ongoing, ended
@@ -1406,7 +1421,13 @@ export const transformPublicHackathonToHackathon = (
phases: [],
},
participation: {
- participantType: ParticipantType.TEAM_OR_INDIVIDUAL,
+ participantType: publicHackathon.participantType
+ ? publicHackathon.participantType === 'individual'
+ ? ParticipantType.INDIVIDUAL
+ : publicHackathon.participantType === 'team'
+ ? ParticipantType.TEAM
+ : ParticipantType.TEAM_OR_INDIVIDUAL
+ : undefined,
teamMin: undefined,
teamMax: undefined,
about: publicHackathon.subtitle,