From eafcbad6d6cf500e874a5897d32394ffef1292fc Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 15 Nov 2025 13:13:14 +0530 Subject: [PATCH] feat(hackathons): Replace default placeholder content with "Coming Soon" states - Replace default rules with "Rules Coming Soon" empty state when no rules are provided - Replace default schedule with "Schedule Coming Soon" empty state when no schedule data exists - Replace default prize information with "Prize Details Coming Soon" empty state when no prize data is provided - Remove hardcoded placeholder prize categories and additional rewards sections - Simplify prize display to only show actual prize amount and details when available - Add conditional rendering to display empty states with appropriate icons and messaging - Improve UX by not showing misleading default content for incomplete hackathon listings --- app/hackathons/[id]/page.tsx | 226 ++++++-------- components/dashboard/HackathonForm.tsx | 392 +++++++++++++++++++++++-- 2 files changed, 461 insertions(+), 157 deletions(-) diff --git a/app/hackathons/[id]/page.tsx b/app/hackathons/[id]/page.tsx index 6330ac6c..7ee9bb8c 100644 --- a/app/hackathons/[id]/page.tsx +++ b/app/hackathons/[id]/page.tsx @@ -235,20 +235,21 @@ export default function HackathonDetailPage() { rulesArray = Object.values(hackathon.rules).map(String); } - // Default rules if no data is provided + // Don't show rules if no data is provided if (rulesArray.length === 0) { - rulesArray = [ - "All team members must be registered participants", - "Original work only - no plagiarism or pre-built solutions", - "Teams must work independently without external help", - "All code must be written during the hackathon period", - "Presentations must be completed within the allocated time", - "Judges' decisions are final and binding", - "Respect all participants and maintain professional conduct", - "Follow the specified submission format and deadlines", - "No use of proprietary or licensed software without permission", - "Teams must be present for the entire duration of the event" - ]; + return ( +
+
+ + + +

Rules Coming Soon

+

+ The rules and guidelines for this hackathon will be announced soon. Stay tuned! +

+
+
+ ); } return ( @@ -319,17 +320,19 @@ export default function HackathonDetailPage() { scheduleArray = Object.entries(hackathon.schedule).map(([date, label]) => ({ date, label: String(label) })); } - // Default schedule if no data is provided + // Don't show schedule if no data is provided if (scheduleArray.length === 0) { - scheduleArray = [ - { date: "Day 1 - Opening", label: "Registration & Team Formation" }, - { date: "Day 1 - Morning", label: "Opening Ceremony & Problem Statement Release" }, - { date: "Day 1 - Afternoon", label: "Coding & Development Phase" }, - { date: "Day 1 - Evening", label: "Mentorship Sessions & Networking" }, - { date: "Day 2 - Morning", label: "Continued Development & Prototyping" }, - { date: "Day 2 - Afternoon", label: "Final Submissions & Presentations" }, - { date: "Day 2 - Evening", label: "Judging & Award Ceremony" } - ]; + return ( +
+
+ +

Schedule Coming Soon

+

+ The detailed schedule for this hackathon will be announced soon. Stay tuned! +

+
+
+ ); } return ( @@ -383,8 +386,23 @@ export default function HackathonDetailPage() { ) } - const prizeAmount = hackathon?.prize || hackathon?.price || "₹50,000+"; - const prizeDetails = hackathon?.prize_details || "Exciting rewards, sponsor goodies, and recognition."; + const prizeAmount = hackathon?.prize || hackathon?.price || ""; + const prizeDetails = hackathon?.prize_details || ""; + + // If no prize information is provided at all + if (!prizeAmount && !prizeDetails) { + return ( +
+
+ +

Prize Details Coming Soon

+

+ Prize information will be announced soon. Stay tuned for exciting rewards! +

+
+
+ ); + } return (
@@ -396,72 +414,16 @@ export default function HackathonDetailPage() { {/* Main Prize */}
-
{prizeAmount}
-

{prizeDetails}

-
- - {/* Prize Categories */} -
-
-
🥇
-

1st Place

-

Grand Prize Winner

-
-
-
🥈
-

2nd Place

-

Runner Up

-
-
-
🥉
-

3rd Place

-

Second Runner Up

-
-
- - {/* Additional Rewards */} -
-

Additional Rewards

-
-
-
🏆
-
-
Trophy & Certificates
-
Official recognition
-
-
-
-
💼
-
-
Internship Opportunities
-
With partner companies
-
-
-
-
🎁
-
-
Sponsor Goodies
-
Swag bags & merchandise
-
-
-
-
🌟
-
-
Networking
-
Connect with industry experts
-
+ {prizeAmount && ( +
{prizeAmount}
+ )} + {prizeDetails ? ( +
+ {prizeDetails}
-
-
- -
-

💡 Pro Tips:

-
    -
  • • Focus on innovation and problem-solving
  • -
  • • Present your solution clearly and confidently
  • -
  • • Network with other participants and mentors
  • -
  • • Have fun and learn from the experience!
  • -
+ ) : prizeAmount && ( +

Detailed prize breakdown coming soon!

+ )}
@@ -492,50 +454,19 @@ export default function HackathonDetailPage() { faqArray = Object.entries(hackathon.faq).map(([question, answer]) => ({ question, answer: String(answer) })); } - // Default FAQ if no data is provided + // Don't show FAQ if no data is provided if (faqArray.length === 0) { - faqArray = [ - { - question: "Who can participate in this hackathon?", - answer: "This hackathon is open to all students, professionals, and coding enthusiasts. Whether you're a beginner or an expert, everyone is welcome to participate and showcase their skills." - }, - { - question: "What is the team size requirement?", - answer: "Teams can consist of 1-5 members. You can participate individually or form a team with friends or colleagues. Team formation will be facilitated during the opening ceremony." - }, - { - question: "What technologies can I use?", - answer: "You can use any programming language, framework, or technology stack of your choice. The focus is on innovation and problem-solving rather than specific technologies." - }, - { - question: "Do I need to bring my own equipment?", - answer: "Yes, please bring your own laptop and any necessary peripherals. We'll provide power outlets and internet connectivity. Some hardware components may be available on request." - }, - { - question: "How will the judging process work?", - answer: "Projects will be evaluated based on innovation, technical implementation, user experience, and presentation. A panel of industry experts will judge the final submissions." - }, - { - question: "What if I have dietary restrictions?", - answer: "We'll provide meals and snacks throughout the event. Please inform us about any dietary restrictions during registration, and we'll accommodate your needs." - }, - { - question: "Can I work on a pre-existing project?", - answer: "No, all work must be original and created during the hackathon period. You can plan and research beforehand, but coding and development must start after the problem statement is released." - }, - { - question: "What happens if I need help during the hackathon?", - answer: "Mentors will be available throughout the event to provide guidance and answer questions. You can also reach out to the organizing team for any technical or logistical support." - }, - { - question: "How do I submit my project?", - answer: "Detailed submission guidelines will be provided during the opening ceremony. Generally, you'll need to submit your code repository, a demo video, and present your solution to the judges." - }, - { - question: "What are the prizes and rewards?", - answer: "Winners will receive cash prizes, trophies, certificates, and potential internship opportunities with partner companies. All participants will receive certificates and networking opportunities." - } - ]; + return ( +
+
+ +

FAQ Coming Soon

+

+ Frequently asked questions will be added soon. For now, feel free to contact the organizers with any questions! +

+
+
+ ); } return ( @@ -829,8 +760,28 @@ export default function HackathonDetailPage() {
Have questions about this hackathon? Contact the organizers or check the FAQ section.
{/* Social Icons Row */} - {hackathon?.socials && ( -
+ {hackathon?.socials && Object.keys(hackathon.socials).length > 0 && ( +
+ {hackathon.socials.linkedin && ( + + + + )} + {hackathon.socials.twitter && ( + + + + )} + {hackathon.socials.discord && ( + + + + )} + {hackathon.socials.website && ( + + + + )} {hackathon.socials.whatsapp && ( @@ -841,11 +792,6 @@ export default function HackathonDetailPage() { )} - {hackathon.socials.linkedin && ( - - - - )}
)} diff --git a/components/dashboard/HackathonForm.tsx b/components/dashboard/HackathonForm.tsx index 84d29028..189c0ac8 100644 --- a/components/dashboard/HackathonForm.tsx +++ b/components/dashboard/HackathonForm.tsx @@ -24,6 +24,24 @@ interface HackathonFormProps { export function HackathonForm({ company, hackathon, mode, onSuccess }: HackathonFormProps) { const [loading, setLoading] = useState(false) const [companyInfoOpen, setCompanyInfoOpen] = useState(false) + + // Parse existing data for dynamic fields + const parseSchedule = (schedule: unknown): { date: string; label: string }[] => { + if (Array.isArray(schedule)) return schedule + if (schedule && typeof schedule === 'object') { + return Object.entries(schedule).map(([date, label]) => ({ date, label: String(label) })) + } + return [] + } + + const parseFAQ = (faq: unknown): { question: string; answer: string }[] => { + if (Array.isArray(faq)) return faq + if (faq && typeof faq === 'object') { + return Object.entries(faq).map(([question, answer]) => ({ question, answer: String(answer) })) + } + return [] + } + const [formData, setFormData] = useState({ title: hackathon?.title || '', slug: hackathon?.slug || '', @@ -44,9 +62,35 @@ export function HackathonForm({ company, hackathon, mode, onSuccess }: Hackathon prize_details: hackathon?.prize_details || '', team_size_min: (hackathon?.team_size as { min?: number; max?: number } | undefined)?.min || 1, team_size_max: (hackathon?.team_size as { min?: number; max?: number } | undefined)?.max || 5, + // High priority fields + image: hackathon?.image || '', + tags: hackathon?.tags?.join(', ') || '', + event_type: hackathon?.event_type?.[0] || 'Offline', + user_types: hackathon?.user_types || [], + organizer_email: (hackathon?.organizer_contact as { email?: string })?.email || '', + organizer_phone: (hackathon?.organizer_contact as { phone?: string })?.phone || '', + }) + + // Medium priority fields - dynamic arrays + const [rules, setRules] = useState( + Array.isArray(hackathon?.rules) ? hackathon.rules : [] + ) + const [schedule, setSchedule] = useState<{ date: string; label: string }[]>( + parseSchedule(hackathon?.schedule) + ) + const [faq, setFAQ] = useState<{ question: string; answer: string }[]>( + parseFAQ(hackathon?.faq) + ) + const [socials, setSocials] = useState({ + linkedin: (hackathon?.socials as Record | undefined)?.linkedin || '', + twitter: (hackathon?.socials as Record | undefined)?.twitter || '', + discord: (hackathon?.socials as Record | undefined)?.discord || '', + website: (hackathon?.socials as Record | undefined)?.website || '', + whatsapp: (hackathon?.socials as Record | undefined)?.whatsapp || '', + instagram: (hackathon?.socials as Record | undefined)?.instagram || '', }) - const handleChange = (field: string, value: string | number | boolean) => { + const handleChange = (field: string, value: string | number | boolean | string[]) => { setFormData(prev => ({ ...prev, [field]: value })) } @@ -74,30 +118,36 @@ export function HackathonForm({ company, hackathon, mode, onSuccess }: Hackathon .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, '') - // Remove team_size_min and team_size_max from formData before spreading - const { team_size_min, team_size_max, ...restFormData } = formData + // Remove team_size_min, team_size_max, and other UI-only fields from formData before spreading + const { team_size_min, team_size_max, tags, organizer_email, organizer_phone, user_types, event_type, ...restFormData } = formData const hackathonData = { ...restFormData, slug, categories: [formData.category], - tags: [], + tags: tags ? tags.split(',').map(t => t.trim()).filter(Boolean) : [], locations: [formData.location], registered: hackathon?.registered || 0, - event_type: ['Offline'], - user_types: ['Professionals', 'College Students'], + event_type: [event_type], + user_types: user_types.length > 0 ? user_types : ['Professionals', 'College Students'], featured: false, status: 'draft', - rules: [], - schedule: [], - faq: [], - socials: {}, + rules: rules.filter(Boolean), + schedule: schedule.filter(s => s.date && s.label), + faq: faq.filter(f => f.question && f.answer), + socials: Object.fromEntries( + Object.entries(socials).filter(([, value]) => value) + ), sponsors: [], company_id: company.id, team_size: { min: team_size_min, max: team_size_max, }, + organizer_contact: { + email: organizer_email || undefined, + phone: organizer_phone || undefined, + }, views: hackathon?.views || 0, clicks: hackathon?.clicks || 0, approval_status: 'draft', @@ -265,6 +315,32 @@ export function HackathonForm({ company, hackathon, mode, onSuccess }: Hackathon rows={6} />
+ +
+ + handleChange('image', e.target.value)} + placeholder="https://example.com/banner.jpg" + /> +

+ Recommended size: 1200x600px +

+
+ +
+ + handleChange('tags', e.target.value)} + placeholder="AI, ML, Web3, Blockchain (comma-separated)" + /> +

+ Separate tags with commas for better discoverability +

+
@@ -342,13 +418,20 @@ export function HackathonForm({ company, hackathon, mode, onSuccess }: Hackathon
- - handleChange('organizer', e.target.value)} - placeholder="Organizer name" - /> + +
@@ -361,6 +444,72 @@ export function HackathonForm({ company, hackathon, mode, onSuccess }: Hackathon />
+ +
+ +
+ {['Students', 'Professionals', 'College Students', 'School Students', 'Working Professionals', 'Freelancers'].map((type) => ( + { + const newTypes = formData.user_types.includes(type) + ? formData.user_types.filter(t => t !== type) + : [...formData.user_types, type] + handleChange('user_types', newTypes) + }} + > + {type} + + ))} +
+

+ Click to select/deselect target audience types +

+
+ + + + + + Organizer Contact + + Contact information for participants to reach out + + + +
+
+ + handleChange('organizer', e.target.value)} + placeholder="Organizer name" + /> +
+
+ + handleChange('organizer_email', e.target.value)} + placeholder="contact@example.com" + /> +
+
+
+ + handleChange('organizer_phone', e.target.value)} + placeholder="+91 98765 43210" + /> +
@@ -466,6 +615,215 @@ export function HackathonForm({ company, hackathon, mode, onSuccess }: Hackathon + {/* Social Media Links */} + + + Social Media & Links + + Add social media links for participants to connect + + + +
+
+ + setSocials(prev => ({ ...prev, linkedin: e.target.value }))} + placeholder="https://linkedin.com/company/..." + /> +
+
+ + setSocials(prev => ({ ...prev, twitter: e.target.value }))} + placeholder="https://twitter.com/..." + /> +
+
+ + setSocials(prev => ({ ...prev, discord: e.target.value }))} + placeholder="https://discord.gg/..." + /> +
+
+ + setSocials(prev => ({ ...prev, website: e.target.value }))} + placeholder="https://example.com" + /> +
+
+ + setSocials(prev => ({ ...prev, whatsapp: e.target.value }))} + placeholder="https://wa.me/..." + /> +
+
+ + setSocials(prev => ({ ...prev, instagram: e.target.value }))} + placeholder="https://instagram.com/..." + /> +
+
+
+
+ + {/* Rules */} + + + Rules & Guidelines + + Add rules for participants to follow + + + + {rules.map((rule, index) => ( +
+ { + const newRules = [...rules] + newRules[index] = e.target.value + setRules(newRules) + }} + placeholder={`Rule ${index + 1}`} + /> + +
+ ))} + +
+
+ + {/* Schedule */} + + + Schedule + + Add schedule items for the hackathon + + + + {schedule.map((item, index) => ( +
+ { + const newSchedule = [...schedule] + newSchedule[index].date = e.target.value + setSchedule(newSchedule) + }} + placeholder="Day 1 - Morning" + /> + { + const newSchedule = [...schedule] + newSchedule[index].label = e.target.value + setSchedule(newSchedule) + }} + placeholder="Activity description" + className="col-span-2" + /> + +
+ ))} + +
+
+ + {/* FAQ */} + + + FAQ + + Add frequently asked questions + + + + {faq.map((item, index) => ( +
+ { + const newFAQ = [...faq] + newFAQ[index].question = e.target.value + setFAQ(newFAQ) + }} + placeholder="Question" + /> +