mirror of
https://github.com/ershisan99/www.git
synced 2025-12-17 21:09:22 +00:00
refactor user table, make opponent name a link
This commit is contained in:
5
bun.lock
5
bun.lock
@@ -34,6 +34,7 @@
|
|||||||
"@radix-ui/react-tooltip": "^1.1.8",
|
"@radix-ui/react-tooltip": "^1.1.8",
|
||||||
"@t3-oss/env-nextjs": "^0.12.0",
|
"@t3-oss/env-nextjs": "^0.12.0",
|
||||||
"@tanstack/react-query": "^5.69.0",
|
"@tanstack/react-query": "^5.69.0",
|
||||||
|
"@tanstack/react-table": "^8.21.2",
|
||||||
"@tanstack/react-virtual": "^3.13.6",
|
"@tanstack/react-virtual": "^3.13.6",
|
||||||
"@trpc/client": "^11.0.0",
|
"@trpc/client": "^11.0.0",
|
||||||
"@trpc/react-query": "^11.0.0",
|
"@trpc/react-query": "^11.0.0",
|
||||||
@@ -392,8 +393,12 @@
|
|||||||
|
|
||||||
"@tanstack/react-query": ["@tanstack/react-query@5.71.5", "", { "dependencies": { "@tanstack/query-core": "5.71.5" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-WpxZWy4fDASjY+iAaXB+aY+LC95PQ34W6EWVkjJ0hdzWWbczFnr9nHvHkVDpwdR18I1NO8igNGQJFrLrgyzI8Q=="],
|
"@tanstack/react-query": ["@tanstack/react-query@5.71.5", "", { "dependencies": { "@tanstack/query-core": "5.71.5" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-WpxZWy4fDASjY+iAaXB+aY+LC95PQ34W6EWVkjJ0hdzWWbczFnr9nHvHkVDpwdR18I1NO8igNGQJFrLrgyzI8Q=="],
|
||||||
|
|
||||||
|
"@tanstack/react-table": ["@tanstack/react-table@8.21.2", "", { "dependencies": { "@tanstack/table-core": "8.21.2" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-11tNlEDTdIhMJba2RBH+ecJ9l1zgS2kjmexDPAraulc8jeNA4xocSNeyzextT0XJyASil4XsCYlJmf5jEWAtYg=="],
|
||||||
|
|
||||||
"@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.6", "", { "dependencies": { "@tanstack/virtual-core": "3.13.6" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WT7nWs8ximoQ0CDx/ngoFP7HbQF9Q2wQe4nh2NB+u2486eX3nZRE40P9g6ccCVq7ZfTSH5gFOuCoVH5DLNS/aA=="],
|
"@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.6", "", { "dependencies": { "@tanstack/virtual-core": "3.13.6" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WT7nWs8ximoQ0CDx/ngoFP7HbQF9Q2wQe4nh2NB+u2486eX3nZRE40P9g6ccCVq7ZfTSH5gFOuCoVH5DLNS/aA=="],
|
||||||
|
|
||||||
|
"@tanstack/table-core": ["@tanstack/table-core@8.21.2", "", {}, "sha512-uvXk/U4cBiFMxt+p9/G7yUWI/UbHYbyghLCjlpWZ3mLeIZiUBSKcUnw9UnKkdRz7Z/N4UBuFLWQdJCjUe7HjvA=="],
|
||||||
|
|
||||||
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.6", "", {}, "sha512-cnQUeWnhNP8tJ4WsGcYiX24Gjkc9ALstLbHcBj1t3E7EimN6n6kHH+DPV4PpDnuw00NApQp+ViojMj1GRdwYQg=="],
|
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.6", "", {}, "sha512-cnQUeWnhNP8tJ4WsGcYiX24Gjkc9ALstLbHcBj1t3E7EimN6n6kHH+DPV4PpDnuw00NApQp+ViojMj1GRdwYQg=="],
|
||||||
|
|
||||||
"@trpc/client": ["@trpc/client@11.0.1", "", { "peerDependencies": { "@trpc/server": "11.0.1", "typescript": ">=5.7.2" } }, "sha512-HvOrvWAXbGBwt4om+NfuxrK4f+ik2aaNIXq7WLrJbEp7U+YXfh5++1a5p4JDaikrvSaObJ389DhYAGWz90xSGw=="],
|
"@trpc/client": ["@trpc/client@11.0.1", "", { "peerDependencies": { "@trpc/server": "11.0.1", "typescript": ">=5.7.2" } }, "sha512-HvOrvWAXbGBwt4om+NfuxrK4f+ik2aaNIXq7WLrJbEp7U+YXfh5++1a5p4JDaikrvSaObJ389DhYAGWz90xSGw=="],
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
"@radix-ui/react-tooltip": "^1.1.8",
|
"@radix-ui/react-tooltip": "^1.1.8",
|
||||||
"@t3-oss/env-nextjs": "^0.12.0",
|
"@t3-oss/env-nextjs": "^0.12.0",
|
||||||
"@tanstack/react-query": "^5.69.0",
|
"@tanstack/react-query": "^5.69.0",
|
||||||
|
"@tanstack/react-table": "^8.21.2",
|
||||||
"@tanstack/react-virtual": "^3.13.6",
|
"@tanstack/react-virtual": "^3.13.6",
|
||||||
"@trpc/client": "^11.0.0",
|
"@trpc/client": "^11.0.0",
|
||||||
"@trpc/react-query": "^11.0.0",
|
"@trpc/react-query": "^11.0.0",
|
||||||
|
|||||||
228
src/app/players/[id]/_components/games-table.tsx
Normal file
228
src/app/players/[id]/_components/games-table.tsx
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from '@/components/ui/table'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import type { SelectGames } from '@/server/db/types'
|
||||||
|
import {
|
||||||
|
type SortingState,
|
||||||
|
createColumnHelper,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from '@tanstack/react-table'
|
||||||
|
import { ArrowDownCircle, ArrowUp, ArrowUpCircle } from 'lucide-react'
|
||||||
|
import { useFormatter } from 'next-intl'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useMemo, useState } from 'react'
|
||||||
|
|
||||||
|
const numberFormatter = new Intl.NumberFormat('en-US', {
|
||||||
|
signDisplay: 'exceptZero',
|
||||||
|
})
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<SelectGames>()
|
||||||
|
|
||||||
|
const useColumns = () => {
|
||||||
|
const format = useFormatter()
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
columnHelper.accessor('opponentName', {
|
||||||
|
meta: { className: 'pl-4' },
|
||||||
|
header: 'Opponent',
|
||||||
|
cell: (info) => (
|
||||||
|
<Link
|
||||||
|
href={`/players/${info.row.original.opponentId}`}
|
||||||
|
className='pl-4 font-medium hover:underline'
|
||||||
|
>
|
||||||
|
{info.getValue()}
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('gameType', {
|
||||||
|
header: 'Game Type',
|
||||||
|
cell: (info) => {
|
||||||
|
const gameType = info.getValue()
|
||||||
|
return (
|
||||||
|
<Badge
|
||||||
|
variant='outline'
|
||||||
|
className={cn(
|
||||||
|
'font-normal capitalize',
|
||||||
|
gameType === 'ranked'
|
||||||
|
? 'border-violet-200 bg-violet-50 text-violet-700 dark:border-violet-800 dark:bg-violet-950 dark:text-violet-300'
|
||||||
|
: gameType.toLowerCase() === 'vanilla'
|
||||||
|
? 'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-300'
|
||||||
|
: 'border-zinc-200 bg-zinc-50 text-zinc-700 dark:border-zinc-800 dark:bg-zinc-700 dark:text-zinc-300'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{info.getValue()}
|
||||||
|
</Badge>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('opponentMmr', {
|
||||||
|
header: 'Opponent MMR',
|
||||||
|
meta: { className: 'justify-end' },
|
||||||
|
cell: (info) => (
|
||||||
|
<span className='flex w-full justify-end font-mono'>
|
||||||
|
{Math.trunc(info.getValue())}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('playerMmr', {
|
||||||
|
header: 'MMR',
|
||||||
|
meta: { className: 'justify-end' },
|
||||||
|
cell: (info) => (
|
||||||
|
<span className='flex w-full justify-end font-mono'>
|
||||||
|
{Math.trunc(info.getValue())}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('mmrChange', {
|
||||||
|
header: 'Result',
|
||||||
|
meta: { className: 'justify-end' },
|
||||||
|
cell: (info) => {
|
||||||
|
const mmrChange = info.getValue()
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
'flex items-center justify-end font-medium font-mono',
|
||||||
|
mmrChange > 0 ? 'text-emerald-500' : 'text-rose-500'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{numberFormatter.format(Math.trunc(mmrChange))}
|
||||||
|
{mmrChange > 0 ? (
|
||||||
|
<ArrowUpCircle className='ml-1 h-4 w-4' />
|
||||||
|
) : (
|
||||||
|
<ArrowDownCircle className='ml-1 h-4 w-4' />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('gameTime', {
|
||||||
|
header: 'Date',
|
||||||
|
meta: { className: 'justify-end' },
|
||||||
|
cell: (info) => (
|
||||||
|
<span
|
||||||
|
className={'flex items-center justify-end font-medium font-mono'}
|
||||||
|
>
|
||||||
|
{format.dateTime(info.getValue(), {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('gameTime', {
|
||||||
|
header: 'Time',
|
||||||
|
meta: { className: 'justify-end pr-4' },
|
||||||
|
cell: (info) => (
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
'flex items-center justify-end pr-4 font-medium font-mono'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{format.dateTime(info.getValue(), {
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
id: 'time',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GamesTable({ games }: { games: SelectGames[] }) {
|
||||||
|
const [sorting, setSorting] = useState<SortingState>([])
|
||||||
|
const columns = useColumns()
|
||||||
|
const table = useReactTable({
|
||||||
|
data: games,
|
||||||
|
columns,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
},
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getRowId: (originalRow) => originalRow.gameNum.toString(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='rounded-md border'>
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => {
|
||||||
|
const sortDirection = header.column.getIsSorted()
|
||||||
|
return (
|
||||||
|
<TableHead key={header.id} className={'px-0'}>
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
'flex w-full items-center',
|
||||||
|
(header.column.columnDef.meta as any)?.className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className={cn(
|
||||||
|
header.column.getCanSort() &&
|
||||||
|
'cursor-pointer select-none',
|
||||||
|
(
|
||||||
|
header.column.columnDef.meta as any
|
||||||
|
)?.className?.includes('justify-end') &&
|
||||||
|
'flex-row-reverse'
|
||||||
|
)}
|
||||||
|
size={'table'}
|
||||||
|
variant='ghost'
|
||||||
|
onClick={header.column.getToggleSortingHandler()}
|
||||||
|
>
|
||||||
|
{flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
{sortDirection ? (
|
||||||
|
<ArrowUp
|
||||||
|
className={cn(
|
||||||
|
'transition-transform',
|
||||||
|
sortDirection === 'asc' ? 'rotate-180' : ''
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className={'h-4 w-4'} />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</TableHead>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow key={row.id}>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
import type React from 'react'
|
import type React from 'react'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
import { GamesTable } from '@/app/players/[id]/_components/games-table'
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Card, CardContent, CardHeader } from '@/components/ui/card'
|
import { Card, CardContent, CardHeader } from '@/components/ui/card'
|
||||||
@@ -13,14 +14,6 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select'
|
} from '@/components/ui/select'
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from '@/components/ui/table'
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { RANKED_CHANNEL, VANILLA_CHANNEL } from '@/shared/constants'
|
import { RANKED_CHANNEL, VANILLA_CHANNEL } from '@/shared/constants'
|
||||||
@@ -29,10 +22,8 @@ import {
|
|||||||
ArrowDownCircle,
|
ArrowDownCircle,
|
||||||
ArrowUpCircle,
|
ArrowUpCircle,
|
||||||
BarChart3,
|
BarChart3,
|
||||||
Calendar,
|
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronUp,
|
ChevronUp,
|
||||||
Clock,
|
|
||||||
Filter,
|
Filter,
|
||||||
MinusCircle,
|
MinusCircle,
|
||||||
Star,
|
Star,
|
||||||
@@ -342,107 +333,7 @@ export function UserInfo() {
|
|||||||
<TabsContent value='matches' className='m-0'>
|
<TabsContent value='matches' className='m-0'>
|
||||||
<div className='overflow-hidden rounded-lg border'>
|
<div className='overflow-hidden rounded-lg border'>
|
||||||
<div className='overflow-x-auto'>
|
<div className='overflow-x-auto'>
|
||||||
<Table>
|
<GamesTable games={games} />
|
||||||
<TableHeader>
|
|
||||||
<TableRow className='bg-gray-50 dark:bg-zinc-800/50'>
|
|
||||||
<TableHead className='w-[100px]'>Game Type</TableHead>
|
|
||||||
<TableHead>Opponent</TableHead>
|
|
||||||
<TableHead className='text-right'>
|
|
||||||
Opponent MMR
|
|
||||||
</TableHead>
|
|
||||||
<TableHead className='text-right'>MMR</TableHead>
|
|
||||||
<TableHead className='text-right'>Result</TableHead>
|
|
||||||
<TableHead className='text-center'>
|
|
||||||
Leaderboard
|
|
||||||
</TableHead>
|
|
||||||
<TableHead className='text-right'>
|
|
||||||
<span className='flex items-center justify-end gap-1'>
|
|
||||||
<Calendar className='h-4 w-4' /> Date
|
|
||||||
</span>
|
|
||||||
</TableHead>
|
|
||||||
<TableHead className='text-right'>
|
|
||||||
<span className='flex items-center justify-end gap-1'>
|
|
||||||
<Clock className='h-4 w-4' /> Time
|
|
||||||
</span>
|
|
||||||
</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{filteredGames.map((game) => (
|
|
||||||
<TableRow
|
|
||||||
key={game.gameId}
|
|
||||||
className='transition-colors hover:bg-gray-50 dark:hover:bg-zinc-800/70'
|
|
||||||
>
|
|
||||||
<TableCell>
|
|
||||||
<Badge
|
|
||||||
variant='outline'
|
|
||||||
className='font-normal capitalize'
|
|
||||||
>
|
|
||||||
{game.gameType}
|
|
||||||
</Badge>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className='font-medium'>
|
|
||||||
{game.opponentName}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className='text-right font-mono'>
|
|
||||||
{Math.trunc(game.opponentMmr)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className='text-right font-mono'>
|
|
||||||
{Math.trunc(game.playerMmr)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className='text-right font-mono'>
|
|
||||||
{game.mmrChange > 0 ? (
|
|
||||||
<span className='flex items-center justify-end font-medium text-emerald-500'>
|
|
||||||
{numberFormatter.format(
|
|
||||||
Math.trunc(game.mmrChange)
|
|
||||||
)}
|
|
||||||
<ArrowUpCircle className='ml-1 inline h-4 w-4' />
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span className='flex items-center justify-end font-medium text-rose-500'>
|
|
||||||
{numberFormatter.format(
|
|
||||||
Math.trunc(game.mmrChange)
|
|
||||||
)}
|
|
||||||
<ArrowDownCircle className='ml-1 inline h-4 w-4' />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className='text-center'>
|
|
||||||
<Badge
|
|
||||||
variant='outline'
|
|
||||||
className={cn(
|
|
||||||
'w-full font-normal',
|
|
||||||
game.gameType === 'ranked'
|
|
||||||
? 'border-violet-200 bg-violet-50 text-violet-700 dark:border-violet-800 dark:bg-violet-950 dark:text-violet-300'
|
|
||||||
: game.gameType.toLowerCase() === 'vanilla'
|
|
||||||
? 'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-300'
|
|
||||||
: 'border-zinc-200 bg-zinc-50 text-zinc-700 dark:border-zinc-800 dark:bg-zinc-700 dark:text-zinc-300'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{game.gameType === 'ranked'
|
|
||||||
? 'Ranked Queue'
|
|
||||||
: game.gameType.toLowerCase() === 'vanilla'
|
|
||||||
? 'Vanilla Queue'
|
|
||||||
: 'N/A'}
|
|
||||||
</Badge>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className='text-right font-mono text-slate-500 dark:text-slate-400'>
|
|
||||||
{format.dateTime(game.gameTime, {
|
|
||||||
year: 'numeric',
|
|
||||||
month: '2-digit',
|
|
||||||
day: '2-digit',
|
|
||||||
})}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className='text-right font-mono text-slate-500 dark:text-slate-400'>
|
|
||||||
{format.dateTime(game.gameTime, {
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit',
|
|
||||||
})}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|||||||
@@ -1,36 +1,37 @@
|
|||||||
import * as React from "react"
|
import { Slot } from '@radix-ui/react-slot'
|
||||||
import { Slot } from "@radix-ui/react-slot"
|
import { type VariantProps, cva } from 'class-variance-authority'
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
import type * as React from 'react'
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
`inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive`,
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default:
|
default:
|
||||||
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
|
||||||
destructive:
|
destructive:
|
||||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40',
|
||||||
outline:
|
outline:
|
||||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50',
|
||||||
secondary:
|
secondary:
|
||||||
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
|
||||||
ghost:
|
ghost:
|
||||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
|
||||||
link: "text-primary underline-offset-4 hover:underline",
|
link: 'text-primary underline-offset-4 hover:underline',
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
table: 'h-9 px-2 py-2',
|
||||||
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
||||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
|
||||||
icon: "size-9",
|
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
|
||||||
|
icon: 'size-9',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
variant: "default",
|
variant: 'default',
|
||||||
size: "default",
|
size: 'default',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -41,15 +42,15 @@ function Button({
|
|||||||
size,
|
size,
|
||||||
asChild = false,
|
asChild = false,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<"button"> &
|
}: React.ComponentProps<'button'> &
|
||||||
VariantProps<typeof buttonVariants> & {
|
VariantProps<typeof buttonVariants> & {
|
||||||
asChild?: boolean
|
asChild?: boolean
|
||||||
}) {
|
}) {
|
||||||
const Comp = asChild ? Slot : "button"
|
const Comp = asChild ? Slot : 'button'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
data-slot="button"
|
data-slot='button'
|
||||||
className={cn(buttonVariants({ variant, size, className }))}
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|||||||
3
src/server/db/types.ts
Normal file
3
src/server/db/types.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import type { player_games } from '@/server/db/schema'
|
||||||
|
|
||||||
|
export type SelectGames = typeof player_games.$inferSelect
|
||||||
Reference in New Issue
Block a user