Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ const ActionButtons = () => {
)
: false;

// Server-derived role for the current viewer. Organizers and assigned
// judges cannot join their own hackathon; surfacing the role lets us
// show the right copy instead of a broken join button.
const viewerRole = (hackathon as any)?.viewerRole as
| 'organizer'
| 'judge'
| 'participant'
| 'guest'
| undefined;
const hasConflictingRole =
viewerRole === 'organizer' || viewerRole === 'judge';

const handleJoin = withAuth(async () => {
try {
await joinMutation.mutateAsync();
Expand Down Expand Up @@ -86,7 +98,13 @@ const ActionButtons = () => {

return (
<div className='flex w-full flex-col gap-3 md:w-auto md:flex-row md:items-center'>
{!isParticipant ? (
{hasConflictingRole ? (
<div className='flex h-12 w-full items-center justify-center rounded-xl border border-white/10 bg-white/5 px-8 text-sm font-medium text-gray-300 md:w-auto'>
{viewerRole === 'organizer'
? 'You are managing this hackathon'
: 'You are judging this hackathon'}
</div>
) : !isParticipant ? (
<BoundlessButton
className='s d bg-primary hover:bg-primary/90 h-12 w-full rounded-xl px-8 font-bold text-black disabled:bg-white/5 disabled:text-white/20 md:w-auto'
icon={!isRegistrationClosed && <IconUserPlus size={20} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import TeamCard from './teams/TeamCard';
import MyTeamView from './teams/MyTeamView';
import { BoundlessButton } from '@/components/buttons/BoundlessButton';
import Pagination from '@/components/ui/pagination';
import {
DropdownMenu,
DropdownMenuContent,
Expand Down Expand Up @@ -88,7 +89,10 @@ const FindTeam = () => {
!!hackathon?.id && hackathon.participantType !== 'INDIVIDUAL'
);

const teams = teamsResponse?.data?.teams || [];
const teams = (teamsResponse?.data?.teams || []).filter(
t => !myTeam || t.id !== myTeam.id
);
const totalPages = teamsResponse?.data?.pagination?.totalPages ?? 1;

const handleJoin = (team: Team) => {
setSelectedTeam(team);
Expand All @@ -99,14 +103,6 @@ const FindTeam = () => {

const isIndividualOnly = hackathon.participantType === 'INDIVIDUAL';

if (myTeam) {
return (
<TabsContent value='team-formation' className='mt-0 w-full outline-none'>
<MyTeamView team={myTeam} hackathonSlug={slug} />
</TabsContent>
);
}

return (
<TabsContent
value='team-formation'
Expand All @@ -129,9 +125,18 @@ const FindTeam = () => {
</div>
) : (
<>
{myTeam && (
<div className='space-y-4'>
<MyTeamView team={myTeam} hackathonSlug={slug} />
<div className='h-px w-full bg-white/5' />
</div>
)}

<div className='flex items-center justify-between'>
<div>
<h2 className='text-2xl font-bold text-white'>Open Teams</h2>
<h2 className='text-2xl font-bold text-white'>
{myTeam ? 'Other Teams' : 'Open Teams'}
</h2>
<p className='mt-1 text-sm text-gray-500'>
Find builders to collaborate with on your project.
</p>
Expand Down Expand Up @@ -261,16 +266,23 @@ const FindTeam = () => {
</p>
</div>
) : teams.length > 0 ? (
<div className='grid grid-cols-1 gap-6 md:grid-cols-1'>
{teams.map(team => (
<TeamCard
key={team.id}
team={team}
onJoin={() => handleJoin(team)}
onMessageLeader={handleMessageLeader}
/>
))}
</div>
<>
<div className='grid grid-cols-1 gap-6 lg:grid-cols-2'>
{teams.map(team => (
<TeamCard
key={team.id}
team={team}
onJoin={() => handleJoin(team)}
onMessageLeader={handleMessageLeader}
/>
))}
</div>
<Pagination
currentPage={page}
totalPages={totalPages}
onPageChange={setPage}
/>
</>
) : (
<div className='flex flex-col items-center justify-center space-y-6 rounded-3xl border border-white/5 bg-[#0D0E10] py-24 text-center'>
<div className='flex h-20 w-20 items-center justify-center rounded-full bg-white/5'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const SubmissionCard = ({ submission }: SubmissionCardProps) => {
<img
src={banner}
alt={`${projectName} banner`}
className='h-full w-full object-cover transition-transform duration-300 group-hover:scale-105'
className='h-full w-full object-contain transition-transform duration-300 group-hover:scale-105'
/>
) : logo ? (
// eslint-disable-next-line @next/next/no-img-element
Expand All @@ -113,7 +113,7 @@ const SubmissionCard = ({ submission }: SubmissionCardProps) => {
<img
src={logo}
alt={`${projectName} logo`}
className='absolute bottom-3 left-3 h-12 w-12 rounded-lg border-2 border-[#0D0E10] bg-[#0D0E10] object-cover shadow-lg'
className='absolute bottom-3 left-3 h-12 w-12 rounded-lg border-2 border-[#0D0E10] bg-[#0D0E10] object-contain shadow-lg'
/>
)}
</div>
Expand All @@ -130,12 +130,9 @@ const SubmissionCard = ({ submission }: SubmissionCardProps) => {

{/* Tags/Categories */}
<div className='flex flex-wrap gap-2 pt-2'>
<span className='text-primary rounded-md bg-[#232B20]/50 px-2.5 py-1 text-[10px] font-bold tracking-wider uppercase'>
{category}
</span>
{submission.category && (
<span className='rounded-md bg-white/5 px-2.5 py-1 text-[10px] font-bold tracking-wider text-gray-400 uppercase'>
{submission.category}
{category && (
<span className='text-primary rounded-md bg-[#232B20]/50 px-2.5 py-1 text-[10px] font-bold tracking-wider uppercase'>
{category}
</span>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,14 @@ const MyTeamView = ({ team, hackathonSlug }: MyTeamViewProps) => {
</div>
<div>
<h2 className='text-xl font-bold text-white sm:text-2xl'>
{team.teamName}
<a
href={`/hackathons/${hackathonSlug}/teams/${team.id}`}
target='_blank'
rel='noopener noreferrer'
className='hover:text-primary transition-colors'
>
{team.teamName}
</a>
</h2>
<div className='mt-1 flex flex-wrap items-center gap-2 sm:gap-3'>
<span className='text-primary text-sm font-bold'>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import React from 'react';
import React, { useCallback } from 'react';
import { useParams } from 'next/navigation';
import { cn } from '@/lib/utils';
import { Team, TeamMember } from '@/lib/api/hackathons/teams';
import GroupAvatar from '@/components/avatars/GroupAvatar';
Expand All @@ -14,6 +15,16 @@ interface TeamCardProps {
}

const TeamCard = ({ team, onJoin, onMessageLeader }: TeamCardProps) => {
const { slug } = useParams<{ slug: string }>();

const openTeamDetails = useCallback(() => {
if (!slug || !team.id) return;
window.open(
`/hackathons/${slug}/teams/${team.id}`,
'_blank',
'noopener,noreferrer'
);
}, [slug, team.id]);
const {
teamName,
description,
Expand All @@ -32,7 +43,18 @@ const TeamCard = ({ team, onJoin, onMessageLeader }: TeamCardProps) => {
};

return (
<div className='group hover:border-primary/20 flex h-full flex-col overflow-hidden rounded-2xl border border-white/5 bg-[#0A0B0D] p-8 transition-all hover:bg-[#0D0F12]'>
<div
role='link'
tabIndex={0}
onClick={openTeamDetails}
onKeyDown={e => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
openTeamDetails();
}
}}
className='group hover:border-primary/20 flex h-full cursor-pointer flex-col overflow-hidden rounded-2xl border border-white/5 bg-[#0A0B0D] p-8 transition-all hover:bg-[#0D0F12]'
>
<div className='mb-6 flex flex-col gap-6 sm:flex-row sm:items-start sm:justify-between'>
<div className='flex items-start gap-4'>
<div className='text-primary flex h-14 w-14 shrink-0 items-center justify-center rounded-xl bg-[#232B20] text-xl font-black'>
Expand Down Expand Up @@ -70,7 +92,10 @@ const TeamCard = ({ team, onJoin, onMessageLeader }: TeamCardProps) => {
variant='outline'
size='sm'
className='h-11 shrink-0 rounded-xl border-white/10 bg-white/5 px-4 text-sm font-bold text-white transition-all hover:bg-white/10'
onClick={e => onMessageLeader(team, e.currentTarget)}
onClick={e => {
e.stopPropagation();
onMessageLeader(team, e.currentTarget);
}}
aria-label='Message team leader'
>
<MessageCircle className='mr-2 h-4 w-4' />
Expand All @@ -81,7 +106,10 @@ const TeamCard = ({ team, onJoin, onMessageLeader }: TeamCardProps) => {
variant='outline'
size='sm'
className='border-primary/20 text-primary hover:bg-primary h-11 w-full rounded-xl bg-[#232B20]/30 px-6 text-sm font-bold transition-all hover:text-black sm:w-auto'
onClick={() => onJoin?.(team)}
onClick={e => {
e.stopPropagation();
onJoin?.(team);
}}
disabled={memberCount >= maxSize}
>
Join Team
Expand Down
23 changes: 18 additions & 5 deletions app/(landing)/hackathons/[slug]/components/tabs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import FindTeam from './contents/FindTeam';
import SponsorsTab from './contents/SponsorsTab';
import { useEffect, useState, useMemo } from 'react';
import { useHackathonData } from '@/lib/providers/hackathonProvider';
import { useHackathonAnnouncements } from '@/hooks/hackathon/use-hackathon-queries';
import {
useHackathonAnnouncements,
useHackathonTeams,
} from '@/hooks/hackathon/use-hackathon-queries';
import { useCommentSystem } from '@/hooks/use-comment-system';
import { CommentEntityType } from '@/types/comment';
import { Megaphone } from 'lucide-react';
Expand Down Expand Up @@ -46,6 +49,17 @@ const HackathonTabs = ({ sidebar }: HackathonTabsProps) => {
enabled: !!currentHackathon?.id,
});

const participantType = currentHackathon?.participantType;
const isTeamHackathon =
participantType === 'TEAM' || participantType === 'TEAM_OR_INDIVIDUAL';

const { data: teamsCountResponse } = useHackathonTeams(
slug,
{ page: 1, limit: 1 },
!!slug && isTeamHackathon
);
const teamsTotal = teamsCountResponse?.data?.pagination?.total ?? 0;

const [activeTab, setActiveTab] = useState('overview');

const hackathonTabs = useMemo(() => {
Expand All @@ -55,10 +69,6 @@ const HackathonTabs = ({ sidebar }: HackathonTabsProps) => {
const hasWinners = !!(winners && winners.length > 0);
const hasAnnouncements = announcements.length > 0;

const participantType = currentHackathon.participantType;
const isTeamHackathon =
participantType === 'TEAM' || participantType === 'TEAM_OR_INDIVIDUAL';

const tabs = [
{ id: 'overview', label: 'Overview' },
...(hasParticipants
Expand Down Expand Up @@ -105,6 +115,7 @@ const HackathonTabs = ({ sidebar }: HackathonTabsProps) => {
tabs.push({
id: 'team-formation',
label: 'Find Team',
badge: teamsTotal,
});
}

Expand Down Expand Up @@ -159,6 +170,8 @@ const HackathonTabs = ({ sidebar }: HackathonTabsProps) => {
announcements,
announcementsLoading,
generalLoading,
isTeamHackathon,
teamsTotal,
]);

useEffect(() => {
Expand Down
1 change: 1 addition & 0 deletions app/(landing)/hackathons/[slug]/submit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export default function SubmitProjectPage({
category: mySubmission.category,
description: mySubmission.description,
logo: mySubmission.logo,
banner: mySubmission.banner,
videoUrl: mySubmission.videoUrl,
introduction: mySubmission.introduction,
links: mySubmission.links,
Expand Down
Loading
Loading