blanchon commited on
Commit
e66c992
·
1 Parent(s): ec62d48

Match table: add Uploaded column, sort by it by default

Browse files

Surface the per-row uploaded_at timestamp so freshly ingested matches
float to the top by default. The match-row roll-up takes the latest
upload across the series' maps; rounds use their own per-round
uploaded_at; the legacy Date (match_date) column stays so users can
still browse by when matches were originally played.

src/lib/components/match-table/columns.ts CHANGED
@@ -27,6 +27,19 @@ declare module '@tanstack/table-core' {
27
  const dateSort = (a: { match_date: string }, b: { match_date: string }) =>
28
  new Date(a.match_date).getTime() - new Date(b.match_date).getTime();
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  // Cells use `accessorFn` returning the underlying value so global filtering and
31
  // sorting see the right thing; visual rendering happens in `cell` via components.
32
 
@@ -104,7 +117,8 @@ export const roundColumns: ColumnDef<RoundRow>[] = [
104
  cell: ({ row }) => renderComponent(DateCell, { iso: row.original.match_date }),
105
  enableGlobalFilter: false,
106
  meta: { label: 'Date' }
107
- }
 
108
  ];
109
 
110
  export const mapColumns: ColumnDef<MapRow>[] = [
@@ -185,7 +199,8 @@ export const mapColumns: ColumnDef<MapRow>[] = [
185
  cell: ({ row }) => renderComponent(DateCell, { iso: row.original.match_date }),
186
  enableGlobalFilter: false,
187
  meta: { label: 'Date' }
188
- }
 
189
  ];
190
 
191
  export const matchColumns: ColumnDef<MatchRow>[] = [
@@ -249,5 +264,6 @@ export const matchColumns: ColumnDef<MatchRow>[] = [
249
  cell: ({ row }) => renderComponent(DateCell, { iso: row.original.match_date }),
250
  enableGlobalFilter: false,
251
  meta: { label: 'Date' }
252
- }
 
253
  ];
 
27
  const dateSort = (a: { match_date: string }, b: { match_date: string }) =>
28
  new Date(a.match_date).getTime() - new Date(b.match_date).getTime();
29
 
30
+ const uploadedSort = (a: { uploaded_at: string }, b: { uploaded_at: string }) =>
31
+ new Date(a.uploaded_at).getTime() - new Date(b.uploaded_at).getTime();
32
+
33
+ const uploadedColumn = <T extends { uploaded_at: string }>(): ColumnDef<T> => ({
34
+ id: 'uploaded_at',
35
+ accessorFn: (r) => new Date(r.uploaded_at).getTime(),
36
+ sortingFn: (a, b) => uploadedSort(a.original, b.original),
37
+ header: ({ column }) => renderComponent(SortHeader, { column, label: 'Uploaded' }),
38
+ cell: ({ row }) => renderComponent(DateCell, { iso: row.original.uploaded_at }),
39
+ enableGlobalFilter: false,
40
+ meta: { label: 'Uploaded' }
41
+ });
42
+
43
  // Cells use `accessorFn` returning the underlying value so global filtering and
44
  // sorting see the right thing; visual rendering happens in `cell` via components.
45
 
 
117
  cell: ({ row }) => renderComponent(DateCell, { iso: row.original.match_date }),
118
  enableGlobalFilter: false,
119
  meta: { label: 'Date' }
120
+ },
121
+ uploadedColumn<RoundRow>()
122
  ];
123
 
124
  export const mapColumns: ColumnDef<MapRow>[] = [
 
199
  cell: ({ row }) => renderComponent(DateCell, { iso: row.original.match_date }),
200
  enableGlobalFilter: false,
201
  meta: { label: 'Date' }
202
+ },
203
+ uploadedColumn<MapRow>()
204
  ];
205
 
206
  export const matchColumns: ColumnDef<MatchRow>[] = [
 
264
  cell: ({ row }) => renderComponent(DateCell, { iso: row.original.match_date }),
265
  enableGlobalFilter: false,
266
  meta: { label: 'Date' }
267
+ },
268
+ uploadedColumn<MatchRow>()
269
  ];
src/lib/components/match-table/rows.ts CHANGED
@@ -20,6 +20,7 @@ export type RoundRow = {
20
  winner: Match['winner'];
21
  format: string;
22
  match_date: string;
 
23
  rounds_played: number;
24
  };
25
 
@@ -33,6 +34,7 @@ export type MatchRow = {
33
  winner: Match['winner'] | '';
34
  format: string;
35
  match_date: string;
 
36
  maps: string[];
37
  maps_played: number;
38
  first_map: string;
@@ -74,6 +76,7 @@ export function buildRoundRows(matches: Match[], rounds: Round[]): RoundRow[] {
74
  winner: m.winner,
75
  format: m.format,
76
  match_date: m.match_date,
 
77
  rounds_played: m.rounds_played
78
  });
79
  }
@@ -105,6 +108,12 @@ export function buildMatchRows(matches: Match[], rounds: Round[]): MatchRow[] {
105
  (acc, m) => acc + (durationByMap.get(`${m.match_id}|${m.map_name}`) ?? 0),
106
  0
107
  );
 
 
 
 
 
 
108
  rows.push({
109
  match_id: matchId,
110
  event: head.event,
@@ -115,6 +124,7 @@ export function buildMatchRows(matches: Match[], rounds: Round[]): MatchRow[] {
115
  winner: team1MapWins > team2MapWins ? 'team1' : team2MapWins > team1MapWins ? 'team2' : '',
116
  format: head.format,
117
  match_date: head.match_date,
 
118
  maps: sorted.map((m) => m.map_name),
119
  maps_played: sorted.length,
120
  first_map: head.map_name,
@@ -124,7 +134,8 @@ export function buildMatchRows(matches: Match[], rounds: Round[]): MatchRow[] {
124
  }
125
  rows.sort(
126
  (a, b) =>
127
- new Date(b.match_date).getTime() - new Date(a.match_date).getTime() || b.match_id - a.match_id
 
128
  );
129
  return rows;
130
  }
 
20
  winner: Match['winner'];
21
  format: string;
22
  match_date: string;
23
+ uploaded_at: string;
24
  rounds_played: number;
25
  };
26
 
 
34
  winner: Match['winner'] | '';
35
  format: string;
36
  match_date: string;
37
+ uploaded_at: string;
38
  maps: string[];
39
  maps_played: number;
40
  first_map: string;
 
76
  winner: m.winner,
77
  format: m.format,
78
  match_date: m.match_date,
79
+ uploaded_at: r.uploaded_at,
80
  rounds_played: m.rounds_played
81
  });
82
  }
 
108
  (acc, m) => acc + (durationByMap.get(`${m.match_id}|${m.map_name}`) ?? 0),
109
  0
110
  );
111
+ // Use the latest upload across the match's maps so the row reflects when
112
+ // the whole series finished landing in the dataset.
113
+ const latestUpload = sorted.reduce(
114
+ (acc, m) => (m.uploaded_at > acc ? m.uploaded_at : acc),
115
+ head.uploaded_at
116
+ );
117
  rows.push({
118
  match_id: matchId,
119
  event: head.event,
 
124
  winner: team1MapWins > team2MapWins ? 'team1' : team2MapWins > team1MapWins ? 'team2' : '',
125
  format: head.format,
126
  match_date: head.match_date,
127
+ uploaded_at: latestUpload,
128
  maps: sorted.map((m) => m.map_name),
129
  maps_played: sorted.length,
130
  first_map: head.map_name,
 
134
  }
135
  rows.sort(
136
  (a, b) =>
137
+ new Date(b.uploaded_at).getTime() - new Date(a.uploaded_at).getTime() ||
138
+ b.match_id - a.match_id
139
  );
140
  return rows;
141
  }
src/routes/+page.svelte CHANGED
@@ -85,7 +85,7 @@
85
 
86
  const newTableState = (): TableState => ({
87
  pagination: { pageIndex: 0, pageSize: 25 },
88
- sorting: [{ id: 'match_date', desc: true }],
89
  columnFilters: [],
90
  columnVisibility: {},
91
  globalFilter: ''
 
85
 
86
  const newTableState = (): TableState => ({
87
  pagination: { pageIndex: 0, pageSize: 25 },
88
+ sorting: [{ id: 'uploaded_at', desc: true }],
89
  columnFilters: [],
90
  columnVisibility: {},
91
  globalFilter: ''