import type { ColumnDef, RowData } from '@tanstack/table-core'; import { renderComponent } from '$lib/components/ui/data-table'; import SortHeader from './data-table-sort-header.svelte'; import PlainHeader from './data-table-plain-header.svelte'; import EventCell from './cells/event-cell.svelte'; import MatchIdCell from './cells/match-id-cell.svelte'; import TeamsCell from './cells/teams-cell.svelte'; import ScoreCell from './cells/score-cell.svelte'; import MapCell from './cells/map-cell.svelte'; import MapsListCell from './cells/maps-list-cell.svelte'; import WinnerSideCell from './cells/winner-side-cell.svelte'; import DurationCell from './cells/duration-cell.svelte'; import DateCell from './cells/date-cell.svelte'; import RoundCell from './cells/round-cell.svelte'; import RoundsPlayedCell from './cells/rounds-played-cell.svelte'; import PlayerCell from './cells/player-cell.svelte'; import StartTimeCell from './cells/start-time-cell.svelte'; import type { MapRow, MatchRow, PovRow, RoundRow } from './rows'; import type { Match } from '$lib/types'; declare module '@tanstack/table-core' { interface ColumnMeta { label?: string; cellClass?: string; headClass?: string; } } const dateSort = (a: { match_date: string }, b: { match_date: string }) => new Date(a.match_date).getTime() - new Date(b.match_date).getTime(); const uploadedSort = (a: { uploaded_at: string }, b: { uploaded_at: string }) => new Date(a.uploaded_at).getTime() - new Date(b.uploaded_at).getTime(); const uploadedColumn = (): ColumnDef => ({ id: 'uploaded_at', accessorFn: (r) => new Date(r.uploaded_at).getTime(), sortingFn: (a, b) => uploadedSort(a.original, b.original), header: ({ column }) => renderComponent(SortHeader, { column, label: 'Uploaded' }), cell: ({ row }) => renderComponent(DateCell, { iso: row.original.uploaded_at }), enableGlobalFilter: false, meta: { label: 'Uploaded' } }); // Cells use `accessorFn` returning the underlying value so global filtering and // sorting see the right thing; visual rendering happens in `cell` via components. const matchIdColumn = (): ColumnDef => ({ id: 'match_id', accessorKey: 'match_id', header: ({ column }) => renderComponent(SortHeader, { column, label: 'Match' }), cell: ({ row }) => renderComponent(MatchIdCell, { matchId: row.original.match_id }), meta: { label: 'Match', cellClass: 'w-[6rem] pr-2', headClass: 'w-[6rem] pr-2' } }); const eventColumn = (): ColumnDef => ({ id: 'event', accessorKey: 'event', header: ({ column }) => renderComponent(SortHeader, { column, label: 'Event' }), cell: ({ row }) => renderComponent(EventCell, { event: row.original.event, format: row.original.format }), meta: { label: 'Event', cellClass: 'max-w-[16rem]', headClass: 'max-w-[16rem]' } }); export const roundColumns: ColumnDef[] = [ matchIdColumn(), eventColumn(), { id: 'map_name', accessorKey: 'map_name', header: () => renderComponent(PlainHeader, { label: 'Map' }), cell: ({ row }) => renderComponent(MapCell, { map: row.original.map_name }), enableSorting: false, filterFn: (row, id, value) => { if (!value) return true; return row.getValue(id) === value; }, meta: { label: 'Map' } }, { id: 'teams', accessorFn: (r) => `${r.team1} ${r.team2}`, header: () => renderComponent(PlainHeader, { label: 'Teams' }), cell: ({ row }) => renderComponent(TeamsCell, { team1: row.original.team1, team2: row.original.team2, winner: row.original.winner }), enableSorting: false, meta: { label: 'Teams' } }, { id: 'round', accessorKey: 'round', header: ({ column }) => renderComponent(SortHeader, { column, label: 'Round' }), cell: ({ row }) => renderComponent(RoundCell, { round: row.original.round, total: row.original.rounds_played }), enableGlobalFilter: false, meta: { label: 'Round' } }, { id: 'duration_s', accessorKey: 'duration_s', header: ({ column }) => renderComponent(SortHeader, { column, label: 'Duration' }), cell: ({ row }) => renderComponent(DurationCell, { seconds: row.original.duration_s }), enableGlobalFilter: false, meta: { label: 'Duration' } }, { id: 'match_date', accessorFn: (r) => new Date(r.match_date).getTime(), sortingFn: (a, b) => dateSort(a.original, b.original), header: ({ column }) => renderComponent(SortHeader, { column, label: 'Date' }), cell: ({ row }) => renderComponent(DateCell, { iso: row.original.match_date }), enableGlobalFilter: false, meta: { label: 'Date' } }, uploadedColumn() ]; export const mapColumns: ColumnDef[] = [ matchIdColumn(), eventColumn(), { id: 'map_name', accessorKey: 'map_name', header: () => renderComponent(PlainHeader, { label: 'Map' }), cell: ({ row }) => renderComponent(MapCell, { map: row.original.map_name }), enableSorting: false, filterFn: (row, id, value) => { if (!value) return true; return row.getValue(id) === value; }, meta: { label: 'Map' } }, { id: 'teams', accessorFn: (r) => `${r.team1} ${r.team2}`, header: () => renderComponent(PlainHeader, { label: 'Teams' }), cell: ({ row }) => renderComponent(TeamsCell, { team1: row.original.team1, team2: row.original.team2, winner: row.original.winner }), enableSorting: false, meta: { label: 'Teams' } }, { id: 'score', accessorFn: (r) => r.score1 + r.score2, header: ({ column }) => renderComponent(SortHeader, { column, label: 'Score', align: 'start' }), cell: ({ row }) => renderComponent(ScoreCell, { score1: row.original.score1, score2: row.original.score2, winner: row.original.winner }), enableGlobalFilter: false, meta: { label: 'Score' } }, { id: 'winner_side', accessorKey: 'winner_side', header: () => renderComponent(PlainHeader, { label: 'Won by' }), cell: ({ row }) => renderComponent(WinnerSideCell, { side: (row.original as Match).winner_side }), enableSorting: false, filterFn: (row, id, value) => { if (!value) return true; return ((row.getValue(id) ?? '') + '').toLowerCase() === value; }, meta: { label: 'Won by' } }, { id: 'rounds_played', accessorKey: 'rounds_played', header: ({ column }) => renderComponent(SortHeader, { column, label: 'Rounds' }), cell: ({ row }) => renderComponent(RoundsPlayedCell, { rounds: row.original.rounds_played }), enableGlobalFilter: false, meta: { label: 'Rounds' } }, { id: 'duration_s', accessorKey: 'duration_s', header: ({ column }) => renderComponent(SortHeader, { column, label: 'Duration' }), cell: ({ row }) => renderComponent(DurationCell, { seconds: row.original.duration_s }), enableGlobalFilter: false, meta: { label: 'Duration' } }, { id: 'match_date', accessorFn: (r) => new Date(r.match_date).getTime(), sortingFn: (a, b) => dateSort(a.original, b.original), header: ({ column }) => renderComponent(SortHeader, { column, label: 'Date' }), cell: ({ row }) => renderComponent(DateCell, { iso: row.original.match_date }), enableGlobalFilter: false, meta: { label: 'Date' } }, uploadedColumn() ]; export const povColumns: ColumnDef[] = [ matchIdColumn(), eventColumn(), { id: 'map_name', accessorKey: 'map_name', header: () => renderComponent(PlainHeader, { label: 'Map' }), cell: ({ row }) => renderComponent(MapCell, { map: row.original.map_name }), enableSorting: false, filterFn: (row, id, value) => { if (!value) return true; return row.getValue(id) === value; }, meta: { label: 'Map' } }, { id: 'round', accessorKey: 'round', header: ({ column }) => renderComponent(SortHeader, { column, label: 'Round' }), cell: ({ row }) => renderComponent(RoundCell, { round: row.original.round, total: row.original.rounds_played }), enableGlobalFilter: false, meta: { label: 'Round' } }, { id: 'player', accessorKey: 'player', header: ({ column }) => renderComponent(SortHeader, { column, label: 'POV' }), cell: ({ row }) => renderComponent(PlayerCell, { player: row.original.player }), enableGlobalFilter: false, meta: { label: 'POV' } }, { id: 'start_t', accessorKey: 'start_t', header: ({ column }) => renderComponent(SortHeader, { column, label: 'Start' }), cell: ({ row }) => renderComponent(StartTimeCell, { seconds: row.original.start_t }), enableGlobalFilter: false, meta: { label: 'Start' } }, { id: 'teams', accessorFn: (r) => `${r.team1} ${r.team2}`, header: () => renderComponent(PlainHeader, { label: 'Teams' }), cell: ({ row }) => renderComponent(TeamsCell, { team1: row.original.team1, team2: row.original.team2, winner: row.original.winner }), enableSorting: false, meta: { label: 'Teams' } }, { id: 'duration_s', accessorKey: 'duration_s', header: ({ column }) => renderComponent(SortHeader, { column, label: 'Duration' }), cell: ({ row }) => renderComponent(DurationCell, { seconds: row.original.duration_s }), enableGlobalFilter: false, meta: { label: 'Duration' } }, { id: 'match_date', accessorFn: (r) => new Date(r.match_date).getTime(), sortingFn: (a, b) => dateSort(a.original, b.original), header: ({ column }) => renderComponent(SortHeader, { column, label: 'Date' }), cell: ({ row }) => renderComponent(DateCell, { iso: row.original.match_date }), enableGlobalFilter: false, meta: { label: 'Date' } }, uploadedColumn() ]; export const matchColumns: ColumnDef[] = [ matchIdColumn(), eventColumn(), { id: 'teams', accessorFn: (r) => `${r.team1} ${r.team2}`, header: () => renderComponent(PlainHeader, { label: 'Teams' }), cell: ({ row }) => renderComponent(TeamsCell, { team1: row.original.team1, team2: row.original.team2, winner: row.original.winner }), enableSorting: false, meta: { label: 'Teams' } }, { id: 'score', accessorFn: (r) => r.score1 + r.score2, header: ({ column }) => renderComponent(SortHeader, { column, label: 'Maps' }), cell: ({ row }) => renderComponent(ScoreCell, { score1: row.original.score1, score2: row.original.score2, winner: row.original.winner }), enableGlobalFilter: false, meta: { label: 'Maps won' } }, { id: 'maps', accessorFn: (r) => r.maps.join(' '), header: () => renderComponent(PlainHeader, { label: 'Map pool' }), cell: ({ row }) => renderComponent(MapsListCell, { maps: row.original.maps }), enableSorting: false, meta: { label: 'Map pool' } }, { id: 'rounds_played', accessorKey: 'rounds_played', header: ({ column }) => renderComponent(SortHeader, { column, label: 'Rounds' }), cell: ({ row }) => renderComponent(RoundsPlayedCell, { rounds: row.original.rounds_played }), enableGlobalFilter: false, meta: { label: 'Rounds' } }, { id: 'duration_s', accessorKey: 'duration_s', header: ({ column }) => renderComponent(SortHeader, { column, label: 'Duration' }), cell: ({ row }) => renderComponent(DurationCell, { seconds: row.original.duration_s }), enableGlobalFilter: false, meta: { label: 'Duration' } }, { id: 'match_date', accessorFn: (r) => new Date(r.match_date).getTime(), sortingFn: (a, b) => dateSort(a.original, b.original), header: ({ column }) => renderComponent(SortHeader, { column, label: 'Date' }), cell: ({ row }) => renderComponent(DateCell, { iso: row.original.match_date }), enableGlobalFilter: false, meta: { label: 'Date' } }, uploadedColumn() ];