victor HF Staff commited on
Commit
62565ae
·
1 Parent(s): aedf456

feat: add sortable indicators to model detail table headers

Browse files
Files changed (1) hide show
  1. index.html +83 -8
index.html CHANGED
@@ -472,8 +472,10 @@
472
  let allRows = []; // Store all fetched rows globally
473
  let currentFilteredRows = []; // Store currently filtered rows
474
  let uniqueModels = [];
475
- let currentSortKey = null; // Track current sort column
476
- let currentSortDirection = 'asc'; // Track current sort direction ('asc' or 'desc')
 
 
477
  const rowsPerFetch = 100; // How many rows to fetch per API call
478
 
479
  // Plotly layout defaults (same as before)
@@ -604,6 +606,26 @@
604
  sortTable(currentSortKey, currentSortDirection);
605
  });
606
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
607
 
608
  // --- Fetching Logic ---
609
  function fetchAllRows(offset = 0) {
@@ -892,7 +914,8 @@
892
  document.getElementById('table-title').textContent = `Detailed Comparison for ${selectedModelId.split('/').pop()}`;
893
  const tableHead = document.querySelector('#modelDetailTable thead');
894
  const tableBody = document.querySelector('#modelDetailTable tbody');
895
- tableHead.innerHTML = ''; tableBody.innerHTML = ''; // Clear
 
896
 
897
  const providerStats = {};
898
 
@@ -905,9 +928,27 @@
905
  if (row.duration_ms !== null && row.duration_ms >= 0) providerStats[provider].latencies.push(row.duration_ms);
906
  });
907
 
908
- const headerRow = tableHead.insertRow();
909
- const headers = ['Provider', 'Median Latency (ms)', 'Success Rate (%)', 'Error Count', 'Total Requests'];
910
- headers.forEach(text => { headerRow.insertCell().textContent = text; });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
911
 
912
  const providerDataArray = [];
913
  for (const provider in providerStats) {
@@ -919,8 +960,33 @@
919
  errors: stats.errors, total: stats.total
920
  });
921
  }
922
- providerDataArray.sort((a, b) => (a.medianLatency ?? Infinity) - (b.medianLatency ?? Infinity)); // Sort by latency
923
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
924
  providerDataArray.forEach(data => {
925
  const row = tableBody.insertRow();
926
  row.insertCell().textContent = data.provider;
@@ -937,7 +1003,16 @@
937
  });
938
  }
939
 
940
- // --- Sorting Function ---
 
 
 
 
 
 
 
 
 
941
  function sortTable(key, direction) {
942
  currentFilteredRows.sort((a, b) => {
943
  let valA = a[key];
 
472
  let allRows = []; // Store all fetched rows globally
473
  let currentFilteredRows = []; // Store currently filtered rows
474
  let uniqueModels = [];
475
+ let currentSortKey = null; // Track current sort column for requestTable
476
+ let currentSortDirection = 'asc'; // Track current sort direction for requestTable
477
+ let modelDetailSortKey = 'medianLatency'; // Default sort for detail table
478
+ let modelDetailSortDirection = 'asc'; // Default direction for detail table
479
  const rowsPerFetch = 100; // How many rows to fetch per API call
480
 
481
  // Plotly layout defaults (same as before)
 
606
  sortTable(currentSortKey, currentSortDirection);
607
  });
608
 
609
+ // Sorting listener for Model Detail Table Header
610
+ document.querySelector('#modelDetailTable thead').addEventListener('click', (event) => {
611
+ const header = event.target.closest('th.sortable-header');
612
+ if (!header) return;
613
+
614
+ const sortKey = header.dataset.sortKey;
615
+
616
+ if (sortKey === modelDetailSortKey) {
617
+ modelDetailSortDirection = modelDetailSortDirection === 'asc' ? 'desc' : 'asc';
618
+ } else {
619
+ modelDetailSortKey = sortKey;
620
+ modelDetailSortDirection = 'asc';
621
+ }
622
+
623
+ // Re-render the table which includes sorting
624
+ const selectedModel = modelSelector.value;
625
+ const rowsForTable = selectedModel === 'all' ? allRows : allRows.filter(row => row.model_id === selectedModel);
626
+ createModelDetailTable(rowsForTable, selectedModel);
627
+ });
628
+
629
 
630
  // --- Fetching Logic ---
631
  function fetchAllRows(offset = 0) {
 
914
  document.getElementById('table-title').textContent = `Detailed Comparison for ${selectedModelId.split('/').pop()}`;
915
  const tableHead = document.querySelector('#modelDetailTable thead');
916
  const tableBody = document.querySelector('#modelDetailTable tbody');
917
+ // Clear only the body, keep the header for event listeners
918
+ tableBody.innerHTML = '';
919
 
920
  const providerStats = {};
921
 
 
928
  if (row.duration_ms !== null && row.duration_ms >= 0) providerStats[provider].latencies.push(row.duration_ms);
929
  });
930
 
931
+ // Create Header (only if it doesn't exist)
932
+ if (tableHead.rows.length === 0) {
933
+ const headerRow = tableHead.insertRow();
934
+ const headers = [
935
+ { text: 'Provider', key: 'provider', sortable: true },
936
+ { text: 'Median Latency (ms)', key: 'medianLatency', sortable: true },
937
+ { text: 'Success Rate (%)', key: 'successRate', sortable: true },
938
+ { text: 'Error Count', key: 'errors', sortable: true },
939
+ { text: 'Total Requests', key: 'total', sortable: true }
940
+ ];
941
+ headers.forEach(header => {
942
+ const th = document.createElement('th');
943
+ th.textContent = header.text;
944
+ if (header.sortable) {
945
+ th.classList.add('sortable-header');
946
+ th.dataset.sortKey = header.key;
947
+ }
948
+ headerRow.appendChild(th);
949
+ });
950
+ }
951
+
952
 
953
  const providerDataArray = [];
954
  for (const provider in providerStats) {
 
960
  errors: stats.errors, total: stats.total
961
  });
962
  }
 
963
 
964
+ // Sort the data based on current state
965
+ providerDataArray.sort((a, b) => {
966
+ let valA = a[modelDetailSortKey];
967
+ let valB = b[modelDetailSortKey];
968
+
969
+ // Handle null/undefined for numeric sorts (e.g., medianLatency)
970
+ const nullVal = modelDetailSortDirection === 'asc' ? Infinity : -Infinity;
971
+ if (typeof valA === 'number' || valA === null || valA === undefined) {
972
+ valA = (valA === null || valA === undefined) ? nullVal : valA;
973
+ }
974
+ if (typeof valB === 'number' || valB === null || valB === undefined) {
975
+ valB = (valB === null || valB === undefined) ? nullVal : valB;
976
+ }
977
+
978
+ // Comparison logic
979
+ if (typeof valA === 'string' && typeof valB === 'string') {
980
+ return modelDetailSortDirection === 'asc' ? valA.localeCompare(valB) : valB.localeCompare(valA);
981
+ } else {
982
+ return modelDetailSortDirection === 'asc' ? valA - valB : valB - valA;
983
+ }
984
+ });
985
+
986
+ // Update visual indicators *after* sorting
987
+ updateModelDetailSortIndicators();
988
+
989
+ // Render rows
990
  providerDataArray.forEach(data => {
991
  const row = tableBody.insertRow();
992
  row.insertCell().textContent = data.provider;
 
1003
  });
1004
  }
1005
 
1006
+ function updateModelDetailSortIndicators() {
1007
+ document.querySelectorAll('#modelDetailTable th.sortable-header').forEach(th => {
1008
+ th.classList.remove('sort-asc', 'sort-desc');
1009
+ if (th.dataset.sortKey === modelDetailSortKey) {
1010
+ th.classList.add(modelDetailSortDirection === 'asc' ? 'sort-asc' : 'sort-desc');
1011
+ }
1012
+ });
1013
+ }
1014
+
1015
+ // --- Sorting Function for Request Inspector ---
1016
  function sortTable(key, direction) {
1017
  currentFilteredRows.sort((a, b) => {
1018
  let valA = a[key];