mirror of
https://github.com/ershisan99/www.git
synced 2025-12-17 12:34:17 +00:00
show date/time for the current timezone
This commit is contained in:
153
src/app/(home)/major-league-balatro/_components/match-card.tsx
Normal file
153
src/app/(home)/major-league-balatro/_components/match-card.tsx
Normal file
@@ -0,0 +1,153 @@
|
||||
import { PlayerAvatar } from '@/app/(home)/major-league-balatro/_components/player-avatar'
|
||||
import { players } from '@/app/(home)/major-league-balatro/_constants/players'
|
||||
import type { Match } from '@/app/(home)/major-league-balatro/types'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/mobile-tooltip'
|
||||
import { SiTwitch, SiYoutube } from '@icons-pack/react-simple-icons'
|
||||
import { TvMinimalPlay } from 'lucide-react'
|
||||
import { useFormatter } from 'next-intl'
|
||||
import Link from 'next/link'
|
||||
import type React from 'react'
|
||||
|
||||
type MatchCardProps = {
|
||||
match: Match
|
||||
}
|
||||
export const MatchCard = ({ match }: MatchCardProps) => {
|
||||
const formatter = useFormatter()
|
||||
const { player1Id, player2Id, datetime, completed, vod1, vod2 } = match
|
||||
const date = formatter.dateTime(datetime, {
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})
|
||||
const time = formatter.dateTime(datetime, {
|
||||
timeStyle: 'short',
|
||||
})
|
||||
const player1 = players[player1Id]
|
||||
const player2 = players[player2Id]
|
||||
|
||||
if (!player1) {
|
||||
throw new Error(`Player ${player1Id} not found`)
|
||||
}
|
||||
if (!player2) {
|
||||
throw new Error(`Player ${player2Id} not found`)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className='overflow-hidden'>
|
||||
<div className='flex flex-col sm:flex-row'>
|
||||
<div className='flex items-center justify-center p-4 sm:w-1/4'>
|
||||
<div className='text-muted-foreground text-sm underline decoration-dashed underline-offset-4'>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
{date} • {time}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent align={'center'} sideOffset={5}>
|
||||
<div>Date and time are shown for your current timezone.</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
<div className='p-4 sm:w-3/4'>
|
||||
<div className='flex flex-col justify-between gap-4 sm:flex-row sm:items-center'>
|
||||
<div className='flex items-center gap-3'>
|
||||
<div className='-space-x-4 flex'>
|
||||
<PlayerAvatar playerName={player1.name} img={player1.picture} />
|
||||
<PlayerAvatar playerName={player2.name} img={player2.picture} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className='font-bold text-lg'>
|
||||
{player1.name} vs {player2.name}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
{completed ? (
|
||||
<>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant='outline' size='sm' className='gap-1'>
|
||||
<TvMinimalPlay className='size-4' />
|
||||
Watch
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{vod1 && (
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={vod1}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<SiYoutube className='h-4 w-4' />
|
||||
{player1.name}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{vod2 && (
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={vod2}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<SiYoutube className='h-4 w-4' />
|
||||
{player2.name}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
) : (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant='outline' size='sm' className='gap-1'>
|
||||
<SiTwitch className='size-4' />
|
||||
Watch Live
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={`https://twitch.tv/${player1.socials.twitch}`}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
{player1.name}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={`https://twitch.tv/${player2.socials.twitch}`}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
{player2.name}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { PlayerAvatar } from '@/app/(home)/major-league-balatro/_components/player-avatar'
|
||||
import { players } from '@/app/(home)/major-league-balatro/_constants/players'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ExternalLink, Twitch, Users, Youtube } from 'lucide-react'
|
||||
import { SiTwitch, SiYoutube } from '@icons-pack/react-simple-icons'
|
||||
import Link from 'next/link'
|
||||
|
||||
export function Organizer() {
|
||||
@@ -14,7 +16,11 @@ export function Organizer() {
|
||||
<div className='mx-auto mt-8 flex max-w-3xl flex-col items-center gap-8 md:flex-row'>
|
||||
<div className='relative h-32 w-32 flex-shrink-0 rounded-full bg-muted'>
|
||||
<div className='absolute inset-0 flex items-center justify-center'>
|
||||
<Users className='h-16 w-16 text-muted-foreground/50' />
|
||||
<PlayerAvatar
|
||||
className={'size-32'}
|
||||
img={players.zainotv.picture}
|
||||
playerName={players.zainotv.picture}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
@@ -31,7 +37,7 @@ export function Organizer() {
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<Button variant='outline' size='sm' className='gap-2'>
|
||||
<Twitch className='h-4 w-4' />
|
||||
<SiTwitch className='h-4 w-4' />
|
||||
Twitch
|
||||
</Button>
|
||||
</Link>
|
||||
@@ -41,7 +47,7 @@ export function Organizer() {
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<Button variant='outline' size='sm' className='gap-2'>
|
||||
<Youtube className='h-4 w-4' />
|
||||
<SiYoutube className='h-4 w-4' />
|
||||
YouTube
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@@ -1,28 +1,9 @@
|
||||
import { players } from '@/app/(home)/major-league-balatro/_constants/players'
|
||||
import { MatchCard } from '@/app/(home)/major-league-balatro/_components/match-card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { SiTwitch, SiYoutube } from '@icons-pack/react-simple-icons'
|
||||
import {
|
||||
Calendar,
|
||||
Camera,
|
||||
Clock,
|
||||
TvMinimalPlay,
|
||||
Twitch,
|
||||
Youtube,
|
||||
} from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useMemo } from 'react'
|
||||
import type { BadgeProps, Match, WeekConfig } from '../types'
|
||||
import { PlayerAvatar } from './player-avatar'
|
||||
|
||||
const WEEK_CONFIG: Record<string | number, WeekConfig> = {
|
||||
1: {
|
||||
@@ -110,181 +91,6 @@ type MatchDivisionProps = {
|
||||
division: 'Blue' | 'Red'
|
||||
}
|
||||
|
||||
const MatchDivision = ({ division }: MatchDivisionProps) => {
|
||||
const bgColor = division === 'Blue' ? 'bg-blue-950/20' : 'bg-red-950/20'
|
||||
const textColor =
|
||||
division === 'Blue'
|
||||
? 'border-blue-500 text-blue-500'
|
||||
: 'border-red-500 text-red-500'
|
||||
|
||||
return (
|
||||
<div className={`flex items-center justify-center p-4 sm:w-1/4 ${bgColor}`}>
|
||||
<Badge variant='outline' className={textColor}>
|
||||
{division} Division
|
||||
</Badge>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type MatchDateTimeProps = {
|
||||
date: string
|
||||
time: string
|
||||
}
|
||||
|
||||
const MatchDateTime = ({ date, time }: MatchDateTimeProps) => (
|
||||
<div className='flex items-center gap-2 text-muted-foreground text-sm'>
|
||||
<Calendar className='h-4 w-4' />
|
||||
<span>{date}</span>
|
||||
<Clock className='ml-2 h-4 w-4' />
|
||||
<span>{time}</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
type VodButtonProps = {
|
||||
url: string
|
||||
player: string
|
||||
}
|
||||
|
||||
const VodButton = ({ url, player }: VodButtonProps) => (
|
||||
<Link href={url} target='_blank' rel='noopener noreferrer'>
|
||||
<Button variant='outline' size='sm' className='gap-1'>
|
||||
<Youtube className='h-4 w-4' />
|
||||
{player}
|
||||
</Button>
|
||||
</Link>
|
||||
)
|
||||
|
||||
type LiveButtonProps = {
|
||||
username: string
|
||||
}
|
||||
|
||||
const LiveButton = ({ username }: LiveButtonProps) => (
|
||||
<Link
|
||||
href={`https://twitch.tv/${username.toLowerCase()}`}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<Button variant='outline' size='sm' className='gap-1'>
|
||||
<Twitch className='h-4 w-4' />
|
||||
Watch Live
|
||||
</Button>
|
||||
</Link>
|
||||
)
|
||||
|
||||
type MatchCardProps = {
|
||||
match: Match
|
||||
}
|
||||
|
||||
const MatchCard = ({ match }: MatchCardProps) => {
|
||||
const { player1Id, player2Id, date, time, completed, vod1, vod2 } = match
|
||||
|
||||
const player1 = players[player1Id]
|
||||
const player2 = players[player2Id]
|
||||
|
||||
if (!player1) {
|
||||
throw new Error(`Player ${player1Id} not found`)
|
||||
}
|
||||
if (!player2) {
|
||||
throw new Error(`Player ${player2Id} not found`)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className='overflow-hidden'>
|
||||
<div className='flex flex-col sm:flex-row'>
|
||||
<div className='flex items-center justify-center p-4 sm:w-1/4'>
|
||||
<div className='text-muted-foreground text-sm'>
|
||||
{date} • {time}
|
||||
</div>
|
||||
</div>
|
||||
<div className='p-4 sm:w-3/4'>
|
||||
<div className='flex flex-col justify-between gap-4 sm:flex-row sm:items-center'>
|
||||
<div className='flex items-center gap-3'>
|
||||
<div className='-space-x-4 flex'>
|
||||
<PlayerAvatar playerName={player1.name} img={player1.picture} />
|
||||
<PlayerAvatar playerName={player2.name} img={player2.picture} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className='font-bold text-lg'>
|
||||
{player1.name} vs {player2.name}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
{completed ? (
|
||||
<>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant='outline' size='sm' className='gap-1'>
|
||||
<TvMinimalPlay className='size-4' />
|
||||
Watch
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{vod1 && (
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={vod1}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<SiYoutube className='h-4 w-4' />
|
||||
{player1.name}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{vod2 && (
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={vod2}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<SiYoutube className='h-4 w-4' />
|
||||
{player2.name}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
) : (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant='outline' size='sm' className='gap-1'>
|
||||
<SiTwitch className='size-4' />
|
||||
Watch Live
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={`https://twitch.tv/${player1.socials.twitch}`}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
{player1.name}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={`https://twitch.tv/${player2.socials.twitch}`}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
{player2.name}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
type WeekTabProps = {
|
||||
week: string | number
|
||||
matches: Match[]
|
||||
@@ -339,7 +145,7 @@ export function MlbSchedule({ matches }: MlbScheduleProps) {
|
||||
sortedMatches.find((match) => new Date(match.datetime) > now)?.week || 1
|
||||
)
|
||||
}, [sortedMatches])
|
||||
console.log(currentWeek)
|
||||
|
||||
return (
|
||||
<section id='schedule' className='container py-8 md:py-12'>
|
||||
<div className='mx-auto flex max-w-[58rem] flex-col items-center space-y-4 text-center'>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Player } from '../types'
|
||||
|
||||
export const players: Record<string, Player> = {
|
||||
export const players = {
|
||||
roffle: {
|
||||
id: 'roffle',
|
||||
name: 'Roffle',
|
||||
@@ -120,4 +120,4 @@ export const players: Record<string, Player> = {
|
||||
youtube: 'seadubbs11',
|
||||
},
|
||||
},
|
||||
}
|
||||
} satisfies Record<string, Player>
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as AvatarPrimitive from '@radix-ui/react-avatar'
|
||||
import type * as React from 'react'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
import { CDN_URL } from '@/shared/constants'
|
||||
|
||||
function Avatar({
|
||||
className,
|
||||
@@ -23,12 +24,16 @@ function Avatar({
|
||||
|
||||
function AvatarImage({
|
||||
className,
|
||||
src,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
||||
const isDev = process.env.NODE_ENV === 'development'
|
||||
|
||||
return (
|
||||
<AvatarPrimitive.Image
|
||||
data-slot='avatar-image'
|
||||
className={cn('aspect-square size-full object-cover!', className)}
|
||||
src={isDev ? src : `${CDN_URL}${src}`}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user