Skip to content
Closed

Blog #463

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
076ee4e
fix: improve timeline input , ui improvement and fixes for participat…
Benjtalkshow Mar 4, 2026
6a457cd
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Mar 4, 2026
f660875
fix: implement 2fa for email and password login
Benjtalkshow Mar 5, 2026
5460d20
fix: fix conflict
Benjtalkshow Mar 5, 2026
7b8308d
fix: fix conflict
Benjtalkshow Mar 5, 2026
31adf9f
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Mar 5, 2026
b6122c4
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Mar 5, 2026
7e57ddb
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Mar 5, 2026
156b0a8
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Mar 6, 2026
a6599eb
fix: fix submission form
Benjtalkshow Mar 6, 2026
f24e050
Merge branch 'main' of https://github.com/Benjtalkshow/boundless into…
Benjtalkshow Mar 6, 2026
222cd45
fix: fix hackathon submission and participant page
Benjtalkshow Mar 7, 2026
acad424
fix: fix hackathon submission and participant page
Benjtalkshow Mar 7, 2026
efda241
fix: fix auto refresh ib submission page
Benjtalkshow Mar 7, 2026
310a353
fix: fix conflict
Benjtalkshow Mar 7, 2026
dffd842
fix: hackathon submission fixes
Benjtalkshow Mar 8, 2026
a2ddd1d
fix: fix coderabbit corrections
Benjtalkshow Mar 9, 2026
08b85d2
fix: fix coderabbit corrections
Benjtalkshow Mar 9, 2026
db84066
chore: write boundless on x challenge blog
Benjtalkshow Mar 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 28 additions & 7 deletions app/(landing)/hackathons/[slug]/submit/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { use, useEffect } from 'react';
import { use, useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { useHackathonData } from '@/lib/providers/hackathonProvider';
import { useAuthStatus } from '@/hooks/use-auth';
Expand Down Expand Up @@ -70,15 +70,34 @@ export default function SubmitProjectPage({
router.push(`/hackathons/${hackathonSlug}?tab=submission`);
};

if (
isLoading ||
hackathonLoading ||
isLoadingMySubmission ||
!currentHackathon
) {
const [hasInitialLoaded, setHasInitialLoaded] = useState(false);

useEffect(() => {
if (!isLoading && !hackathonLoading && !isLoadingMySubmission) {
setHasInitialLoaded(true);
}
}, [isLoading, hackathonLoading, isLoadingMySubmission]);

if (!hasInitialLoaded) {
return <LoadingScreen />;
}

if (!currentHackathon) {
return (
<div className='flex min-h-screen flex-col items-center justify-center bg-black px-5 py-5 text-white'>
<h1 className='mb-4 text-2xl font-bold'>Hackathon Not Found</h1>
<Button
onClick={handleClose}
variant='ghost'
className='text-gray-400 hover:text-white'
>
<ArrowLeft className='mr-2 h-4 w-4' />
Go Back
</Button>
</div>
);
}

return (
<div className='min-h-screen bg-black px-5 py-5 text-white md:px-[50px] lg:px-[100px]'>
<div className='mx-auto max-w-[1200px] pb-10'>
Expand Down Expand Up @@ -107,6 +126,8 @@ export default function SubmitProjectPage({
introduction: mySubmission.introduction,
links: mySubmission.links,
participationType: (mySubmission as any).participationType,
teamName: (mySubmission as any).teamName,
teamMembers: (mySubmission as any).teamMembers,
}
: undefined
}
Expand Down
42 changes: 23 additions & 19 deletions app/me/projects/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,32 +102,36 @@ export default function MyProjectsPage() {
</Card>
) : (
<div className='grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3'>
{sortedProjects.map(project => (
<ProjectCard
isFullWidth
key={project.id}
data={
{
id: project.id,
slug: project.id,
{sortedProjects.map(project => {
// Find if this project is a hackathon submission
const submission =
meData.user.hackathonSubmissionsAsParticipant?.find(
s => s.projectId === project.id
);

// If it's a submission, use the submission ID for the slug to ensure correct redirection from ProjectCard
const displayId = submission?.id || project.id;

return (
<ProjectCard
isFullWidth
key={project.id}
data={{
id: displayId,
slug: displayId,
project: {
...project,
creator: {
name: authUser?.name || 'You',
image: authUser?.image || '/user.png',
},
},
fundingGoal: 0,
fundingRaised: 0,
fundingCurrency: 'USDC',
fundingEndDate: null,
milestones: [],
voteGoal: 0,
voteProgress: 0,
} as any
}
/>
))}
isSubmission: !!submission,
submissionStatus: submission?.status,
}}
/>
);
})}
</div>
)}
</div>
Expand Down
22 changes: 9 additions & 13 deletions components/hackathons/submissions/SubmissionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
ExpandableScreenContent,
ExpandableScreenTrigger,
useExpandableScreen,
useOptionalExpandableScreen,
} from '@/components/ui/expandable-screen';
import Stepper from '@/components/stepper/Stepper';
import { uploadService } from '@/lib/api/upload';
Expand Down Expand Up @@ -201,16 +202,9 @@ const SubmissionFormContent: React.FC<SubmissionFormContentProps> = ({
onSuccess,
onClose,
}) => {
// Use context carefully since it might not be available when used standalone
let collapse = () => {};
let open = true;
try {
const expandableCtx = useExpandableScreen();
collapse = expandableCtx.collapse;
open = expandableCtx.isExpanded;
} catch (e) {
// Standalone mode, not in ExpandableScreen
}
const expandableCtx = useOptionalExpandableScreen();
const collapse = expandableCtx?.collapse ?? (() => {});
const open = expandableCtx?.isExpanded ?? true;

const { currentHackathon } = useHackathonData();
const { user } = useAuthStatus();
Expand Down Expand Up @@ -416,7 +410,7 @@ const SubmissionFormContent: React.FC<SubmissionFormContentProps> = ({
description:
'An intelligent task management application that uses machine learning to prioritize tasks...',
logo: '',
videoUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
videoUrl: 'https://youtu.be/rOSZhhblE_8?si=Hf_YvPTMmyWTUOKQ',
introduction: 'This project leverages advanced AI algorithms...',
links: [
{ type: 'github', url: 'https://github.com/example/ai-task-manager' },
Expand Down Expand Up @@ -785,12 +779,14 @@ const SubmissionFormContent: React.FC<SubmissionFormContentProps> = ({
} else {
await create(submissionData);
}
if (onClose) {

if (onSuccess) {
onSuccess();
} else if (onClose) {
onClose();
} else {
collapse();
}
onSuccess?.();
} catch {
// Error handled in hook
}
Expand Down
15 changes: 13 additions & 2 deletions components/hackathons/submissions/submissionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ const SubmissionCard = ({
return formatDistanceToNow(new Date(dateString), { addSuffix: true });
};

const handleEditSelect = (e: Event) => {
e.stopPropagation();
onEditClick?.();
};

const handleDeleteSelect = (e: Event) => {
e.stopPropagation();
onDeleteClick?.();
};

return (
<div
onClick={onViewClick}
Expand Down Expand Up @@ -171,6 +181,7 @@ const SubmissionCard = ({
<Button
variant='ghost'
size='sm'
aria-label='More options'
className='h-8 w-8 p-0 text-gray-400 hover:text-white'
>
<MoreHorizontal className='h-4 w-4' />
Expand All @@ -181,14 +192,14 @@ const SubmissionCard = ({
className='border-gray-800 bg-black text-white'
>
<DropdownMenuItem
onSelect={() => onEditClick?.()}
onSelect={handleEditSelect}
className='cursor-pointer text-gray-300 focus:bg-gray-800 focus:text-white'
>
<Edit className='mr-2 h-4 w-4' />
Edit Submission
</DropdownMenuItem>
<DropdownMenuItem
onSelect={() => onDeleteClick?.()}
onSelect={handleDeleteSelect}
className='cursor-pointer text-red-500 focus:bg-red-900/20 focus:text-red-400'
>
<Trash className='mr-2 h-4 w-4' />
Expand Down
2 changes: 2 additions & 0 deletions components/hackathons/submissions/submissionTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ const SubmissionTabContent: React.FC<SubmissionTabContentProps> = ({
setSelectedSort,
setSelectedCategory,
} = useSubmissions();

const { currentHackathon, loading: isHackathonDataLoading } =
useHackathonData();
const { status } = useHackathonStatus(
Expand Down Expand Up @@ -276,6 +277,7 @@ const SubmissionTabContent: React.FC<SubmissionTabContentProps> = ({

{/* Submissions Grid with Create Button if no submission */}
{!isLoadingMySubmission &&
!isHackathonDataLoading &&
!mySubmission &&
isAuthenticated &&
isRegistered &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ interface GeneralSettingsTabProps {
isPublished?: boolean;
}

import TurndownService from 'turndown';

export default function GeneralSettingsTab({
organizationId,
hackathonId,
Expand All @@ -78,14 +80,32 @@ export default function GeneralSettingsTab({
address: string;
} | null>(null);

// Normalize HTML to Markdown for existing descriptions
const normalizedDescription = React.useMemo(() => {
let desc = initialData?.description || '';
if (desc && /<[a-z][\\s\\S]*>/i.test(desc)) {
try {
const turndownService = new TurndownService({
headingStyle: 'atx',
codeBlockStyle: 'fenced',
bulletListMarker: '-',
});
desc = turndownService.turndown(desc);
} catch (err) {
console.error('Failed to convert HTML to Markdown', err);
}
}
return desc;
}, [initialData?.description]);

const form = useForm<InfoFormData>({
resolver: zodResolver(infoSchema),
defaultValues: {
name: initialData?.name || '',
tagline: initialData?.tagline || '',
slug: initialData?.slug || '',
banner: initialData?.banner || '',
description: initialData?.description || '',
description: normalizedDescription,
categories: Array.isArray(initialData?.categories)
? initialData.categories
: [],
Expand Down
61 changes: 35 additions & 26 deletions components/profile/ProjectsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,38 +100,47 @@ export default function ProjectsTab({ user }: ProjectsTabProps) {
onScrollCapture={handleScroll}
>
<div className='grid gap-4 pr-4 md:grid-cols-2 lg:grid-cols-1 2xl:grid-cols-2'>
{projects.map(project => (
<Link
key={project.id}
href={`/projects/${project.id}`}
target='_blank'
>
<ProjectCard
newTab={true}
isFullWidth={true}
data={
{
id: project.id,
slug: project.id,
{projects.map(project => {
// Find if this project is a hackathon submission
// We need to check if the user object has submissions
const submission =
user.user.hackathonSubmissionsAsParticipant?.find(
s => s.projectId === project.id
);

// If it's a submission, use the submission ID for the URL and add ?type=submission
const displayId = submission?.id || project.id;
const href = submission
? `/projects/${displayId}?type=submission`
: `/projects/${displayId}`;

return (
<Link
key={project.id}
href={href}
target='_blank'
rel='noopener noreferrer'
>
<ProjectCard
newTab={true}
isFullWidth={true}
data={{
id: displayId,
slug: displayId,
project: {
...project,
creator: {
name: user.user.name || 'User',
image: user.user.image || '/avatar.png',
},
} as any,
fundingGoal: 0,
fundingRaised: 0,
fundingCurrency: 'USDC',
fundingEndDate: null,
milestones: [],
voteGoal: 0,
voteProgress: 0,
} as any
}
/>
</Link>
))}
},
isSubmission: !!submission,
submissionStatus: submission?.status,
}}
/>
</Link>
);
})}

{!hasMore && projects.length > 0 && (
<div className='col-span-full py-4 text-center'>
Expand Down
Loading
Loading