Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 147 additions & 4 deletions DebugProbe.AspNetCore/Assets/css/debugprobe.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ a {
}

h2 {
margin-bottom: 10px;
margin: 0 0 4px;
}

h3 {
Expand All @@ -41,6 +41,7 @@ h4 {
}

.toolbar,
.index-header,
.topbar,
.accordion-header,
.accordion-meta,
Expand All @@ -51,12 +52,81 @@ h4 {
}

.toolbar,
.index-header,
.topbar,
.accordion-header,
.details-item {
justify-content: space-between;
}

.index-header {
gap: 16px;
margin-bottom: 14px;
}

.muted {
margin: 0;
color: #666;
font-size: 13px;
}

.filters {
display: grid;
grid-template-columns: minmax(220px, 1fr) minmax(130px, 170px) minmax(130px, 170px) auto auto;
gap: 10px;
margin-bottom: 14px;
}

.stats-bar {
display: grid;
grid-template-columns: repeat(4, minmax(150px, 1fr));
gap: 10px;
margin-bottom: 14px;
}

.stat-tile {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
min-height: 52px;
padding: 0 16px;
background: #fff;
border: 1px solid #e9e9e9;
border-radius: 8px;
text-align: center;
}

.stat-tile strong {
color: #1f2937;
font-size: 22px;
line-height: 1;
}

.stat-tile span {
color: #666;
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
}

.filters input,
.filters select {
min-height: 36px;
padding: 0 10px;
background: #fff;
border: 1px solid #ddd;
border-radius: 4px;
color: #222;
font: inherit;
}

.filters input:focus,
.filters select:focus {
outline: 2px solid rgba(108, 92, 231, 0.18);
border-color: #6c5ce7;
}

.topbar {
padding: 18px;
background: #1b1b1b;
Expand Down Expand Up @@ -140,6 +210,13 @@ table {
border-collapse: collapse;
}

.table-wrap {
overflow-x: auto;
background: #fff;
border: 1px solid #e9e9e9;
border-radius: 8px;
}

th,
td {
padding: 10px;
Expand All @@ -149,16 +226,43 @@ td {

th {
background: #fafafa;
color: #555;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
}

tr:hover {
background: #f1f1f1;
}

tbody tr:last-child td {
border-bottom: none;
}

.clickable-row {
cursor: pointer;
}

.method-pill {
display: inline-flex;
min-width: 54px;
justify-content: center;
padding: 4px 8px;
background: #f3f4f6;
border-radius: 999px;
color: #333;
font-size: 12px;
font-weight: 700;
}

.empty-state,
.empty-row td {
padding: 24px;
color: #777;
text-align: center;
}

/* =========================
Section Titles
========================= */
Expand Down Expand Up @@ -245,6 +349,19 @@ tr:hover {
padding: 14px;
}

.index-header {
align-items: flex-start;
flex-direction: column;
}

.filters {
grid-template-columns: 1fr;
}

.stats-bar {
grid-template-columns: repeat(2, minmax(0, 1fr));
}

.payload-group {
padding: 14px;
}
Expand Down Expand Up @@ -373,13 +490,14 @@ pre {

.copy-btn,
.btn-clear,
.btn-secondary,
.trace-id-button {
border-radius: 4px;
cursor: pointer;
}

.copy-btn,
.btn-clear {
.btn-secondary {
background: #2d2d2d;
border: 1px solid #444;
color: #ccc;
Expand All @@ -394,16 +512,41 @@ pre {
}

.copy-btn:hover,
.btn-clear:hover {
.btn-secondary:hover {
background: #3a3a3a;
}

.btn-clear {
.btn-secondary {
padding: 6px 12px;
color: #fff;
font-size: 13px;
}

.btn-secondary {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
min-height: 36px;
background: #fff;
border-color: #ddd;
color: #333;
}

.btn-secondary:hover {
background: #f3f3f3;
}

.btn-clear svg {
width: 15px;
height: 15px;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
}

.trace-id-button {
padding: 6px 10px;
background: #f3f4f6;
Expand Down
89 changes: 70 additions & 19 deletions DebugProbe.AspNetCore/Assets/html/index.html
Original file line number Diff line number Diff line change
@@ -1,21 +1,72 @@
<div class="container">
<div class="toolbar">
<div></div>
<div class="toolbar-right">
<button id="clearBtn" class="btn-clear">Clear</button>
<div class="container">
<div class="index-header">
<div>
<h2>Requests</h2>
<p class="muted">Showing <span id="visibleCount">{{total_count}}</span> of {{total_count}}</p>
</div>
</div>
<table>
<thead>
<tr>
<th>Time</th>
<th>Method</th>
<th>Path</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{{rows}}
</tbody>
</table>
</div>

<div class="stats-bar" aria-label="Request summary">
<div class="stat-tile">
<strong>{{total_requests}}</strong>
<span>Requests</span>
</div>
<div class="stat-tile">
<strong>{{avg_response_time}}</strong>
<span>Avg</span>
</div>
<div class="stat-tile">
<strong>{{slow_requests}}</strong>
<span>Over 1s</span>
</div>
<div class="stat-tile">
<strong>{{error_rate}}</strong>
<span>Errors</span>
</div>
</div>

<div class="filters" aria-label="Request filters">
<input id="requestSearch" type="search" placeholder="Search path, method, status, or trace id" autocomplete="off" />
<select id="methodFilter" aria-label="Filter by method">
<option value="">All methods</option>
{{method_options}}
</select>
<select id="statusFilter" aria-label="Filter by status">
<option value="">All statuses</option>
<option value="2">2xx</option>
<option value="3">3xx</option>
<option value="4">4xx</option>
<option value="5">5xx</option>
</select>
<button id="resetFiltersBtn" class="btn-secondary" type="button">Reset</button>
<button id="clearBtn" class="btn-secondary btn-clear" type="button" title="Clear requests" aria-label="Clear requests">
<svg aria-hidden="true" viewBox="0 0 24 24" focusable="false">
<path d="M3 6h18" />
<path d="M8 6V4h8v2" />
<path d="M6 6l1 14h10l1-14" />
<path d="M10 11v5" />
<path d="M14 11v5" />
</svg>
<span>Clear</span>
</button>
</div>

<div class="table-wrap">
<table id="requestTable">
<thead>
<tr>
<th>Time</th>
<th>Method</th>
<th>Path</th>
<th>Status</th>
<th>Duration</th>
</tr>
</thead>
<tbody>
{{rows}}
</tbody>
</table>
</div>

<div id="emptyFilterState" class="empty-state" hidden>No matching requests</div>
</div>
43 changes: 43 additions & 0 deletions DebugProbe.AspNetCore/Assets/js/debugprobe-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,46 @@ document.querySelectorAll(".clickable-row[data-url]").forEach(row => {
window.location.assign(row.dataset.url);
});
});

const requestSearch = document.getElementById("requestSearch");
const methodFilter = document.getElementById("methodFilter");
const statusFilter = document.getElementById("statusFilter");
const resetFiltersBtn = document.getElementById("resetFiltersBtn");
const visibleCount = document.getElementById("visibleCount");
const emptyFilterState = document.getElementById("emptyFilterState");
const requestRows = Array.from(document.querySelectorAll("#requestTable tbody tr.clickable-row"));

function applyRequestFilters() {
if (!requestRows.length) return;

const search = (requestSearch?.value ?? "").trim().toLowerCase();
const method = methodFilter?.value ?? "";
const statusFamily = statusFilter?.value ?? "";
let shown = 0;

requestRows.forEach(row => {
const matchesSearch = !search || (row.dataset.search ?? "").toLowerCase().includes(search);
const matchesMethod = !method || row.dataset.method === method;
const matchesStatus = !statusFamily || row.dataset.statusFamily === statusFamily;
const isVisible = matchesSearch && matchesMethod && matchesStatus;

row.hidden = !isVisible;
if (isVisible) shown++;
});

if (visibleCount) visibleCount.innerText = shown.toString();
if (emptyFilterState) emptyFilterState.hidden = shown > 0;
}

[requestSearch, methodFilter, statusFilter].forEach(control => {
control?.addEventListener("input", applyRequestFilters);
control?.addEventListener("change", applyRequestFilters);
});

resetFiltersBtn?.addEventListener("click", () => {
if (requestSearch) requestSearch.value = "";
if (methodFilter) methodFilter.value = "";
if (statusFilter) statusFilter.value = "";
applyRequestFilters();
requestSearch?.focus();
});
Loading
Loading