@buerokratt-ria/common-gui-components 0.0.59 → 0.0.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All changes to this project will be documented in this file.
4
4
 
5
5
  ## Template [MajorVersion.MediterraneanVersion.MinorVersion] - DD-MM-YYYY
6
6
 
7
+
8
+ ## [0.0.61] - 11.06.2026
9
+
10
+ - Removed redundant scroll from the History table
11
+ - Added Apply action to the History table header multi-option filter
12
+ - Made the History table header sticky
13
+
14
+ ## [0.0.60] - 09.06.2026
15
+
16
+ - History table: theme, follow up, quality sorting
17
+ - History table: header filter positioning
18
+
7
19
  ## [0.0.59] - 05.06.2026
8
20
 
9
21
  - Quality measurements in chat history view
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buerokratt-ria/common-gui-components",
3
- "version": "0.0.59",
3
+ "version": "0.0.61",
4
4
  "description": "Common GUI components and pre defined templates.",
5
5
  "main": "index.ts",
6
6
  "author": "ExiRai",
@@ -69,9 +69,37 @@
69
69
  }
70
70
 
71
71
  .card-wrapper {
72
+ display: flex;
73
+ flex-direction: column;
72
74
  flex: 1;
73
- overflow: auto;
75
+ min-height: 0;
76
+ overflow: hidden;
74
77
  transition: flex 0.3s ease;
78
+
79
+ .card {
80
+ box-sizing: border-box;
81
+ display: flex;
82
+ flex: 1;
83
+ flex-direction: column;
84
+ max-height: none;
85
+ min-height: 0;
86
+ overflow: hidden;
87
+ }
88
+
89
+ .card__body {
90
+ display: flex;
91
+ flex: 1;
92
+ flex-direction: column;
93
+ min-height: 0;
94
+ overflow: hidden;
95
+ }
96
+
97
+ .data-table__scrollWrapper {
98
+ flex: 1;
99
+ max-height: none;
100
+ min-height: 0;
101
+ overflow: auto;
102
+ }
75
103
  }
76
104
 
77
105
  .drawer-container {
@@ -6,6 +6,7 @@ type HeaderComboboxBaseProps = {
6
6
  readonly label: string;
7
7
  readonly options?: ComponentProps<typeof FormCombobox>['options'];
8
8
  readonly isSearchEnabled?: ComponentProps<typeof FormCombobox>['isSearchEnabled'];
9
+ readonly allOptionValue?: ComponentProps<typeof FormCombobox>['allOptionValue'];
9
10
  };
10
11
 
11
12
  type HeaderComboboxSingleProps = HeaderComboboxBaseProps & {
@@ -18,6 +19,7 @@ type HeaderComboboxMultipleProps = HeaderComboboxBaseProps & {
18
19
  readonly multiple?: true;
19
20
  readonly value?: string[];
20
21
  readonly onChange: (value: string[]) => void;
22
+ readonly isApplyBtnVisible?: boolean;
21
23
  };
22
24
 
23
25
  type HeaderComboboxProps = HeaderComboboxSingleProps | HeaderComboboxMultipleProps;
@@ -27,6 +29,7 @@ const HeaderCombobox: FC<HeaderComboboxProps> = (props) => {
27
29
  label,
28
30
  options = [],
29
31
  isSearchEnabled = true,
32
+ allOptionValue,
30
33
  } = props;
31
34
  const sharedProps = {
32
35
  hideInputStyle: true,
@@ -36,6 +39,8 @@ const HeaderCombobox: FC<HeaderComboboxProps> = (props) => {
36
39
  searchPlaceholder: label,
37
40
  options,
38
41
  isSearchEnabled,
42
+ isMenuPortaled: true,
43
+ allOptionValue,
39
44
  };
40
45
 
41
46
  return (
@@ -57,6 +62,7 @@ const HeaderCombobox: FC<HeaderComboboxProps> = (props) => {
57
62
  multiple={true}
58
63
  value={props.value}
59
64
  onChange={props.onChange}
65
+ isApplyBtnVisible={props.isApplyBtnVisible}
60
66
  />
61
67
  )}
62
68
  </span>
@@ -0,0 +1 @@
1
+ export const ALL_COLUMNS_VALUE = '__all__';
@@ -16,9 +16,9 @@ import {
16
16
  Dialog,
17
17
  Drawer,
18
18
  FormCheckbox,
19
+ FormCombobox,
19
20
  FormDatepicker,
20
21
  FormInput,
21
- FormMultiselect,
22
22
  HistoricalChat,
23
23
  Icon,
24
24
  Tooltip,
@@ -40,6 +40,7 @@ import {StoreState} from "../../../store";
40
40
  import {saveFile} from "../../../services/file";
41
41
  import {ChatMetadataPanel, HeaderCombobox, QualitySettings, SelectedFilterTags} from './components';
42
42
  import { CharMeasurementType } from './types';
43
+ import { ALL_COLUMNS_VALUE } from './constants';
43
44
 
44
45
  type HistoryProps = {
45
46
  user: UserInfo | null;
@@ -162,11 +163,15 @@ const formatChatAnalysisCell = (
162
163
  return value.join(', ');
163
164
  };
164
165
 
165
- const ALL_COLUMNS_VALUE = '__all__';
166
- const BOOLEAN_SORT_COLUMN_IDS = new Set([
166
+ // Boolean -> truthy values before falsy
167
+ // For other columns -> desc before asc - populated before empty
168
+ const NON_EMPTY_FIRST_SORT_COLUMN_IDS = new Set([
167
169
  'authenticatedPerson',
168
170
  'istest',
169
171
  'isPreserve',
172
+ 'followUpStatus',
173
+ 'responseQuality',
174
+ 'theme',
170
175
  ]);
171
176
  const CHAT_STATUSES = [
172
177
  CHAT_EVENTS.ACCEPTED,
@@ -184,7 +189,7 @@ const getEndedChatsSortBy = (sorting: SortingState) => {
184
189
  }
185
190
 
186
191
  const [sortingObject] = sorting;
187
- const sortType = BOOLEAN_SORT_COLUMN_IDS.has(sortingObject.id)
192
+ const sortType = NON_EMPTY_FIRST_SORT_COLUMN_IDS.has(sortingObject.id)
188
193
  ? sortingObject.desc ? 'asc' : 'desc'
189
194
  : sortingObject.desc ? 'desc' : 'asc';
190
195
 
@@ -431,7 +436,6 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
431
436
  const isChatAnalysisEnabled = useMemo(() => {
432
437
  return qualitySettingsConfigQuery.data?.chatAnalysisEnabled ?? false;
433
438
  }, [qualitySettingsConfigQuery.data]);
434
- console.log("IS CHAT ANALYSIS ENABLED", isChatAnalysisEnabled);
435
439
 
436
440
  const getAllEndedChats = useMutation({
437
441
  mutationFn: (data: {
@@ -575,21 +579,6 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
575
579
  return realSelectedColumns;
576
580
  };
577
581
 
578
- const normalizeSelectedColumns = (selection: string[]) => {
579
- const currentAllSelected = selectedColumns.includes(ALL_COLUMNS_VALUE) || areAllColumnsSelected(selectedColumns);
580
- const nextAllSelected = selection.includes(ALL_COLUMNS_VALUE);
581
-
582
- if (nextAllSelected && !currentAllSelected) {
583
- return [ALL_COLUMNS_VALUE, ...getAllColumnValues()];
584
- }
585
-
586
- if (!nextAllSelected && currentAllSelected) {
587
- return [];
588
- }
589
-
590
- return getUiSelectedColumns(selection);
591
- };
592
-
593
582
  const chatStatusChangeMutation = useMutation({
594
583
  mutationFn: async (data: { chatId: string | number; event: string }) => {
595
584
  const changeableTo = [
@@ -1263,10 +1252,12 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
1263
1252
  label={t('chat.history.csaName')}
1264
1253
  options={customerSupportAgentsQuery.data ?? []}
1265
1254
  value={csaIdCodesFilter}
1255
+ allOptionValue={ALL_COLUMNS_VALUE}
1266
1256
  onChange={(value) => {
1267
1257
  const normalizedValue = normalizeCsaFilterValues(value);
1268
1258
  tableHeaderForm.setValue('csaIdCodesFilter', normalizedValue);
1269
1259
  }}
1260
+ isApplyBtnVisible
1270
1261
  />
1271
1262
  );
1272
1263
  },
@@ -1360,6 +1351,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
1360
1351
  label={t('chat.history.rating') ?? ''}
1361
1352
  options={ratingOptions}
1362
1353
  value={feedbackRatings}
1354
+ allOptionValue={ALL_COLUMNS_VALUE}
1363
1355
  onChange={(value) => {
1364
1356
  setTableHeaderValue(
1365
1357
  'feedbackRatings',
@@ -1370,6 +1362,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
1370
1362
  )
1371
1363
  );
1372
1364
  }}
1365
+ isApplyBtnVisible
1373
1366
  />
1374
1367
  );
1375
1368
  },
@@ -1394,6 +1387,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
1394
1387
  label={t('global.status') ?? ''}
1395
1388
  options={statusOptions}
1396
1389
  value={status}
1390
+ allOptionValue={ALL_COLUMNS_VALUE}
1397
1391
  onChange={(value) => {
1398
1392
  setTableHeaderValue(
1399
1393
  'status',
@@ -1404,6 +1398,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
1404
1398
  )
1405
1399
  );
1406
1400
  }}
1401
+ isApplyBtnVisible
1407
1402
  isSearchEnabled={true}
1408
1403
  />
1409
1404
  );
@@ -1442,12 +1437,14 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
1442
1437
  label={t('chat.history.www') ?? ''}
1443
1438
  value={domains}
1444
1439
  options={domainOptions}
1440
+ allOptionValue={ALL_COLUMNS_VALUE}
1445
1441
  onChange={(value) => {
1446
1442
  setTableHeaderValue(
1447
1443
  'domains',
1448
1444
  normalizeAllOptionFilterValues(value, domains, currentDomains)
1449
1445
  );
1450
1446
  }}
1447
+ isApplyBtnVisible
1451
1448
  />
1452
1449
  );
1453
1450
  },
@@ -1455,87 +1452,93 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
1455
1452
  sortDescFirst: false,
1456
1453
  }),
1457
1454
  ...isChatAnalysisEnabled ? [
1458
- columnHelper.display({
1459
- id: 'theme',
1460
- header: () => (
1461
- <HeaderCombobox
1462
- label={t('chat.history.theme')}
1463
- options={themeOptions}
1464
- value={theme}
1465
- onChange={(value) => {
1466
- setTableHeaderValue(
1467
- 'theme',
1468
- normalizeAllOptionFilterValues(
1469
- value,
1470
- theme,
1471
- getAllStringFilterValues(realThemeOptions)
1472
- )
1473
- );
1474
- }}
1475
- />
1455
+ columnHelper.accessor(
1456
+ (row) => formatChatAnalysisCell(
1457
+ row.theme,
1458
+ t('chat.quality.selectionEmptied')
1476
1459
  ),
1477
- cell: (props) => {
1478
- return formatChatAnalysisCell(
1479
- props.row.original.theme,
1480
- t('chat.quality.selectionEmptied')
1481
- );
1482
- },
1483
- enableSorting: true,
1484
- }),
1485
- columnHelper.display({
1486
- id: 'responseQuality',
1487
- header: () => (
1488
- <HeaderCombobox
1489
- label={t('chat.history.responseQuality')}
1490
- options={responseQualityOptions}
1491
- value={responseQuality}
1492
- onChange={(value) => {
1493
- setTableHeaderValue(
1494
- 'responseQuality',
1495
- normalizeAllOptionFilterValues(
1496
- value,
1497
- responseQuality,
1498
- getAllStringFilterValues(realResponseQualityOptions)
1499
- )
1500
- );
1501
- }}
1502
- />
1460
+ {
1461
+ id: 'theme',
1462
+ header: () => (
1463
+ <HeaderCombobox
1464
+ label={t('chat.history.theme')}
1465
+ options={themeOptions}
1466
+ value={theme}
1467
+ allOptionValue={ALL_COLUMNS_VALUE}
1468
+ onChange={(value) => {
1469
+ setTableHeaderValue(
1470
+ 'theme',
1471
+ normalizeAllOptionFilterValues(
1472
+ value,
1473
+ theme,
1474
+ getAllStringFilterValues(realThemeOptions)
1475
+ )
1476
+ );
1477
+ }}
1478
+ isApplyBtnVisible
1479
+ />
1480
+ ),
1481
+ enableSorting: true,
1482
+ }
1483
+ ),
1484
+ columnHelper.accessor(
1485
+ (row) => formatChatAnalysisCell(
1486
+ row.responseQuality,
1487
+ t('chat.quality.selectionEmptied')
1503
1488
  ),
1504
- cell: (props) => {
1505
- return formatChatAnalysisCell(
1506
- props.row.original.responseQuality,
1507
- t('chat.quality.selectionEmptied')
1508
- );
1509
- },
1510
- enableSorting: true,
1511
- }),
1512
- columnHelper.display({
1513
- id: 'followUpStatus',
1514
- header: () => (
1515
- <HeaderCombobox
1516
- label={t('chat.history.followUpStatus')}
1517
- options={followUpStatusOptions}
1518
- value={followUpStatus}
1519
- onChange={(value) => {
1520
- setTableHeaderValue(
1521
- 'followUpStatus',
1522
- normalizeAllOptionFilterValues(
1523
- value,
1524
- followUpStatus,
1525
- getAllStringFilterValues(realFollowUpStatusOptions)
1526
- )
1527
- );
1528
- }}
1529
- />
1489
+ {
1490
+ id: 'responseQuality',
1491
+ header: () => (
1492
+ <HeaderCombobox
1493
+ label={t('chat.history.responseQuality')}
1494
+ options={responseQualityOptions}
1495
+ value={responseQuality}
1496
+ allOptionValue={ALL_COLUMNS_VALUE}
1497
+ onChange={(value) => {
1498
+ setTableHeaderValue(
1499
+ 'responseQuality',
1500
+ normalizeAllOptionFilterValues(
1501
+ value,
1502
+ responseQuality,
1503
+ getAllStringFilterValues(realResponseQualityOptions)
1504
+ )
1505
+ );
1506
+ }}
1507
+ isApplyBtnVisible
1508
+ />
1509
+ ),
1510
+ enableSorting: true,
1511
+ }
1512
+ ),
1513
+ columnHelper.accessor(
1514
+ (row) => formatChatAnalysisCell(
1515
+ row.followUpStatus,
1516
+ t('chat.quality.selectionEmptied')
1530
1517
  ),
1531
- cell: (props) => {
1532
- return formatChatAnalysisCell(
1533
- props.row.original.followUpStatus,
1534
- t('chat.quality.selectionEmptied')
1535
- );
1536
- },
1537
- enableSorting: true,
1538
- }),
1518
+ {
1519
+ id: 'followUpStatus',
1520
+ header: () => (
1521
+ <HeaderCombobox
1522
+ label={t('chat.history.followUpStatus')}
1523
+ options={followUpStatusOptions}
1524
+ value={followUpStatus}
1525
+ allOptionValue={ALL_COLUMNS_VALUE}
1526
+ onChange={(value) => {
1527
+ setTableHeaderValue(
1528
+ 'followUpStatus',
1529
+ normalizeAllOptionFilterValues(
1530
+ value,
1531
+ followUpStatus,
1532
+ getAllStringFilterValues(realFollowUpStatusOptions)
1533
+ )
1534
+ );
1535
+ }}
1536
+ isApplyBtnVisible
1537
+ />
1538
+ ),
1539
+ enableSorting: true,
1540
+ }
1541
+ ),
1539
1542
  ] : [],
1540
1543
  columnHelper.display({
1541
1544
  id: 'detail',
@@ -2052,25 +2055,27 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
2052
2055
  </>
2053
2056
  )}
2054
2057
  <Track style={{width: '240px'}}>
2055
- <FormMultiselect
2058
+ <FormCombobox
2056
2059
  key={counterKey}
2057
2060
  name="visibleColumns"
2058
2061
  label={t('')}
2059
2062
  placeholder={t('chat.history.chosenColumn')}
2060
2063
  options={visibleColumnOptions}
2061
- selectedOptions={visibleColumnOptions.filter((o) =>
2062
- selectedColumns.includes(o.value)
2063
- )}
2064
+ value={selectedColumns}
2064
2065
  selectedOptionsCount={getRealSelectedColumns(selectedColumns).length}
2065
- onSelectionChange={(selection) => {
2066
- const columns = normalizeSelectedColumns(selection?.map((s) => s.value) ?? []);
2066
+ multiple={true}
2067
+ allOptionValue={ALL_COLUMNS_VALUE}
2068
+ direction="down"
2069
+ onChange={(selection) => {
2070
+ const columns = getUiSelectedColumns(selection);
2067
2071
  setSelectedColumns(columns);
2068
2072
  setCounterKey(prev => prev + 1);
2069
2073
  updatePagePreferences.mutate({
2070
2074
  page_results: pagination.pageSize,
2071
- selected_columns: getRealSelectedColumns(columns)
2075
+ selected_columns: getRealSelectedColumns(columns),
2072
2076
  })
2073
2077
  }}
2078
+ isApplyBtnVisible
2074
2079
  />
2075
2080
  </Track>
2076
2081
  </Track>
@@ -2100,12 +2105,13 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
2100
2105
  onRemove={removeSelectedFilterTag}
2101
2106
  onClearFiltersClick={onClearFilersClick}
2102
2107
  />
2103
- <div className="card-drawer-container" style={{height: '100%', overflow: 'auto', maxHeight: '60vh'}}>
2108
+ <div className="card-drawer-container">
2104
2109
  <div className="card-wrapper">
2105
2110
  <Card>
2106
2111
  <DataTable
2107
2112
  data={filteredEndedChatsList}
2108
2113
  sortable
2114
+ stickyHeader
2109
2115
  columns={getFilteredColumns()}
2110
2116
  selectedRow={(row) => row.original.id === selectedChat?.id}
2111
2117
  pagination={pagination}
@@ -2,6 +2,7 @@
2
2
  "global": {
3
3
  "email": "Email",
4
4
  "save": "Save",
5
+ "apply": "Apply",
5
6
  "add": "Add",
6
7
  "edit": "Edit",
7
8
  "delete": "Delete",
@@ -2,6 +2,7 @@
2
2
  "global": {
3
3
  "email": "Email",
4
4
  "save": "Salvesta",
5
+ "apply": "Rakenda",
5
6
  "add": "Lisa",
6
7
  "edit": "Muuda",
7
8
  "delete": "Kustuta",
@@ -30,6 +30,14 @@
30
30
  position: relative;
31
31
  }
32
32
 
33
+ &--sticky-header {
34
+ th {
35
+ background-color: get-color(white);
36
+ border-bottom-color: transparent;
37
+ box-shadow: inset 0 -1px 0 get-color(black-coral-10);
38
+ }
39
+ }
40
+
33
41
  td {
34
42
  padding: 12px 24px 12px 16px;
35
43
  border-bottom: 1px solid get-color(black-coral-2);
@@ -55,6 +55,7 @@ type DataTableProps = {
55
55
  pagesCount?: number;
56
56
  meta?: TableMeta<any>;
57
57
  selectedRow?: (row: Row<any>) => boolean;
58
+ stickyHeader?: boolean;
58
59
  totalCountLabel?: string | null;
59
60
  };
60
61
 
@@ -116,6 +117,7 @@ const DataTable: FC<DataTableProps> = ({
116
117
  pagesCount,
117
118
  meta,
118
119
  selectedRow,
120
+ stickyHeader,
119
121
  totalCountLabel,
120
122
  }) => {
121
123
  const id = useId();
@@ -189,69 +191,72 @@ const DataTable: FC<DataTableProps> = ({
189
191
  return (
190
192
  <>
191
193
  <div className="data-table__scrollWrapper">
192
- <table className="data-table">
194
+ <table className={clsx('data-table', stickyHeader && 'data-table--sticky-header')}>
193
195
  {!disableHead && (
194
196
  <thead>
195
197
  {table.getHeaderGroups().map((headerGroup) => (
196
198
  <tr key={headerGroup.id}>
197
- {headerGroup.headers.map((header) => (
198
- <th
199
- key={header.id}
200
- style={{
201
- width: header.column.columnDef.meta?.size,
202
- position: header.column.columnDef.meta?.sticky
203
- ? 'sticky'
204
- : undefined,
205
- left:
206
- header.column.columnDef.meta?.sticky === 'left'
207
- ? `${header.column.getAfter('left') * 0.675}px`
208
- : undefined,
209
- right:
210
- header.column.columnDef.meta?.sticky === 'right'
211
- ? `${header.column.getAfter('right') * 0.675}px`
212
- : undefined,
213
- backgroundColor: 'white',
214
- zIndex: header.column.columnDef.meta?.sticky ? 1 : 0,
215
- }}
216
- >
217
- {header.isPlaceholder ? null : (
218
- <Track gap={8}>
219
- {sortable && header.column.getCanSort() && (
220
- <button
221
- onClick={header.column.getToggleSortingHandler()}
222
- >
223
- {{
224
- asc: (
225
- <Icon
226
- icon={<MdExpandMore fontSize={20} />}
227
- size="medium"
228
- />
229
- ),
230
- desc: (
199
+ {headerGroup.headers.map((header) => {
200
+ const stickyColumn = header.column.columnDef.meta?.sticky;
201
+
202
+ return (
203
+ <th
204
+ key={header.id}
205
+ style={{
206
+ width: header.column.columnDef.meta?.size,
207
+ position:
208
+ stickyHeader || stickyColumn ? 'sticky' : undefined,
209
+ top: stickyHeader ? 0 : undefined,
210
+ left:
211
+ stickyColumn === 'left'
212
+ ? `${header.column.getAfter('left') * 0.675}px`
213
+ : undefined,
214
+ right:
215
+ stickyColumn === 'right'
216
+ ? `${header.column.getAfter('right') * 0.675}px`
217
+ : undefined,
218
+ zIndex: stickyHeader ? (stickyColumn ? 3 : 1) : stickyColumn ? 1 : 0,
219
+ }}
220
+ >
221
+ {header.isPlaceholder ? null : (
222
+ <Track gap={8}>
223
+ {sortable && header.column.getCanSort() && (
224
+ <button
225
+ onClick={header.column.getToggleSortingHandler()}
226
+ >
227
+ {{
228
+ asc: (
229
+ <Icon
230
+ icon={<MdExpandMore fontSize={20} />}
231
+ size="medium"
232
+ />
233
+ ),
234
+ desc: (
235
+ <Icon
236
+ icon={<MdExpandLess fontSize={20} />}
237
+ size="medium"
238
+ />
239
+ ),
240
+ }[header.column.getIsSorted() as string] ?? (
231
241
  <Icon
232
- icon={<MdExpandLess fontSize={20} />}
242
+ icon={<MdUnfoldMore fontSize={22} />}
233
243
  size="medium"
234
244
  />
235
- ),
236
- }[header.column.getIsSorted() as string] ?? (
237
- <Icon
238
- icon={<MdUnfoldMore fontSize={22} />}
239
- size="medium"
240
- />
241
- )}
242
- </button>
243
- )}
244
- {flexRender(
245
- header.column.columnDef.header,
246
- header.getContext()
247
- )}
248
- {filterable && header.column.getCanFilter() && (
249
- <Filter column={header.column} table={table} />
250
- )}
251
- </Track>
252
- )}
253
- </th>
254
- ))}
245
+ )}
246
+ </button>
247
+ )}
248
+ {flexRender(
249
+ header.column.columnDef.header,
250
+ header.getContext()
251
+ )}
252
+ {filterable && header.column.getCanFilter() && (
253
+ <Filter column={header.column} table={table} />
254
+ )}
255
+ </Track>
256
+ )}
257
+ </th>
258
+ );
259
+ })}
255
260
  </tr>
256
261
  ))}
257
262
  </thead>