Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
7 changes: 5 additions & 2 deletions src/components/Index/GenreBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { useGenres } from "@/hooks/queries/useGenresQuery";

interface GenreBadgeProps {
genreId: string;
size?: "default" | "sm";
}

export function GenreBadge({ genreId }: GenreBadgeProps) {
export function GenreBadge({ genreId, size = "default" }: GenreBadgeProps) {
const { genres, loading, error } = useGenres();

if (loading || error) return null;
Expand All @@ -16,7 +17,9 @@ export function GenreBadge({ genreId }: GenreBadgeProps) {
return (
<Badge
variant="secondary"
className="bg-purple-600/50 text-purple-100 mb-2"
className={`bg-purple-600/50 text-purple-100 ${
size === "sm" ? "text-xs px-2 py-1" : ""
}`}
>
{genre.name}
</Badge>
Expand Down
95 changes: 95 additions & 0 deletions src/components/Index/MultiArtistSetCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { useToast } from "@/hooks/use-toast";
import { FestivalSet } from "@/services/queries";
import { SetHeader } from "./shared/SetHeader";
import { SetImage } from "./shared/SetImage";
import { SetMetadata } from "./shared/SetMetadata";
import { SetDescription } from "./shared/SetDescription";
import { SetVotingButtons } from "./shared/SetVotingButtons";

interface MultiArtistSetCardProps {
set: FestivalSet;
userVote?: number;
userKnowledge?: boolean;
votingLoading?: boolean;
onVote: (
setId: string,
voteType: number,
) => Promise<{ requiresAuth: boolean }>;
onKnowledgeToggle: (setId: string) => Promise<{ requiresAuth: boolean }>;
onAuthRequired: () => void;
use24Hour?: boolean;
}

export function MultiArtistSetCard({
set,
userVote,
userKnowledge,
votingLoading,
onVote,
onKnowledgeToggle,
onAuthRequired,
use24Hour = false,
}: MultiArtistSetCardProps) {
const { toast } = useToast();

async function handleVote(voteType: number) {
const result = await onVote(set.id, voteType);
if (result.requiresAuth) {
onAuthRequired();
}
}

async function handleKnowledgeToggle() {
const result = await onKnowledgeToggle(set.id);
if (result.requiresAuth) {
onAuthRequired();
} else {
const newKnowledgeState = !userKnowledge;
toast({
title: `${set.name} is ${newKnowledgeState ? "known" : "unknown"}`,
duration: 2000,
});
}
}

function getVoteCount(voteType: number) {
return set.votes.filter((vote) => vote.vote_type === voteType).length;
}

return (
<Card className="bg-white/10 backdrop-blur-md border-purple-400/30 hover:bg-white/15 transition-all duration-300 overflow-hidden">
<CardHeader className="pb-4">
{/* Set Image with Mixed Artists */}
<SetImage artists={set.artists} setName={set.name} setSlug={set.slug} />

<div className="flex items-start justify-between">
<div className="flex-1">
<SetHeader
setName={set.name}
artistCount={set.artists.length}
userKnowledge={userKnowledge}
onKnowledgeToggle={handleKnowledgeToggle}
/>

<SetMetadata set={set} use24Hour={use24Hour} />
</div>
</div>

<SetDescription
artists={set.artists}
setDescription={set.description}
/>
</CardHeader>

<CardContent>
<SetVotingButtons
userVote={userVote}
votingLoading={votingLoading}
onVote={handleVote}
getVoteCount={getVoteCount}
/>
</CardContent>
</Card>
);
}
148 changes: 148 additions & 0 deletions src/components/Index/MultiArtistSetListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { useToast } from "@/hooks/use-toast";
import { FestivalSet } from "@/services/queries";
import { SetHeader } from "./shared/SetHeader";
import { SetImage } from "./shared/SetImage";
import { SetMetadata } from "./shared/SetMetadata";
import { SetDescription } from "./shared/SetDescription";
import { SetVotingButtons } from "./shared/SetVotingButtons";

interface MultiArtistSetListItemProps {
set: FestivalSet;
userVote?: number;
userKnowledge?: boolean;
votingLoading?: boolean;
onVote: (
setId: string,
voteType: number,
) => Promise<{ requiresAuth: boolean }>;
onKnowledgeToggle: (setId: string) => Promise<{ requiresAuth: boolean }>;
onAuthRequired: () => void;
use24Hour?: boolean;
}

export function MultiArtistSetListItem({
set,
userVote,
userKnowledge,
votingLoading,
onVote,
onKnowledgeToggle,
onAuthRequired,
use24Hour = false,
}: MultiArtistSetListItemProps) {
const { toast } = useToast();

async function handleVote(voteType: number) {
const result = await onVote(set.id, voteType);
if (result.requiresAuth) {
onAuthRequired();
}
}

async function handleKnowledgeToggle() {
const result = await onKnowledgeToggle(set.id);
if (result.requiresAuth) {
onAuthRequired();
} else {
const newKnowledgeState = !userKnowledge;
toast({
title: `${set.name} is ${newKnowledgeState ? "known" : "unknown"}`,
duration: 2000,
});
}
}

function getVoteCount(voteType: number) {
return set.votes.filter((vote) => vote.vote_type === voteType).length;
}

return (
<div
className="bg-white/10 backdrop-blur-md border-purple-400/30 hover:bg-white/15 transition-all duration-300 rounded-lg p-4"
data-testid="artist-item"
>
{/* Mobile Layout (sm and below) */}
<div className="block md:hidden space-y-3">
<div className="flex items-start gap-3 relative">
<SetImage
artists={set.artists}
setName={set.name}
setSlug={set.slug}
size="sm"
/>
<div className="flex-1 min-w-0">
<SetHeader
setName={set.name}
artists={set.artists}
artistCount={set.artists.length}
userKnowledge={userKnowledge}
onKnowledgeToggle={handleKnowledgeToggle}
size="sm"
/>

<SetMetadata set={set} use24Hour={use24Hour} />
</div>
</div>

<SetDescription
artists={set.artists}
setDescription={set.description}
className="text-purple-200 text-sm"
/>

<SetVotingButtons
userVote={userVote}
votingLoading={votingLoading}
onVote={handleVote}
getVoteCount={getVoteCount}
size="sm"
layout="horizontal"
/>
</div>

{/* Desktop Layout (md and above) */}
<div className="hidden md:flex items-center gap-4 relative">
<SetImage
artists={set.artists}
setName={set.name}
setSlug={set.slug}
size="md"
/>

<div className="flex-1 min-w-0">
<div className="flex items-start justify-between mb-2">
<div className="flex-1 min-w-0">
<SetHeader
setName={set.name}
artists={set.artists}
artistCount={set.artists.length}
userKnowledge={userKnowledge}
onKnowledgeToggle={handleKnowledgeToggle}
size="sm"
/>

<SetMetadata set={set} use24Hour={use24Hour} />
</div>
</div>

<SetDescription
artists={set.artists}
setDescription={set.description}
className="text-purple-200 text-sm mb-2"
/>
</div>

<div className="flex items-center gap-2 flex-shrink-0">
<SetVotingButtons
userVote={userVote}
votingLoading={votingLoading}
onVote={handleVote}
getVoteCount={getVoteCount}
size="sm"
layout="horizontal"
/>
</div>
</div>
</div>
);
}
Loading
Loading