Skip to content

Commit c13e278

Browse files
fix(admin): polish dashboard actions and clarify telemetry labels (#215)
* chore(repo): checkpoint current changes * fix(telemetry): clarify requested models and provider names * chore(repo): remove temporary repro artifacts * fix(ci): restore lint and audit test compatibility * fix(repo): align lint hooks and telemetry helpers * test(e2e): add release stack helper * revert(ci): restore workflow lint action
1 parent ff8de91 commit c13e278

80 files changed

Lines changed: 3290 additions & 658 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,9 @@ repos:
3333
language: system
3434
pass_filenames: false
3535
files: '^(internal/(auditlog|core|providers|server|usage)/.*\.go|tests/perf/.*\.go|Makefile|\.github/workflows/test\.yml)$'
36-
37-
# 3. High-Performance Go Linter (golangci-lint)
38-
- repo: https://github.com/golangci/golangci-lint
39-
rev: v2.7.2 # Latest stable version (Dec 2025)
40-
hooks:
41-
- id: golangci-lint
42-
args: [--fix=false, --timeout=5m] # Optional: increase timeout if project is large
36+
- id: go-lint
37+
name: make lint
38+
entry: make lint
39+
language: system
40+
pass_filenames: false
41+
files: '^(\.golangci\.yml|Makefile|\.github/workflows/test\.yml|.*\.go)$'

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,11 @@ swagger:
8383

8484
# Run linter
8585
lint:
86-
golangci-lint run ./...
86+
golangci-lint run ./cmd/... ./config/... ./internal/...
8787
golangci-lint run --build-tags=e2e ./tests/e2e/...
8888
golangci-lint run --build-tags=integration ./tests/integration/...
8989
golangci-lint run --build-tags=contract ./tests/contract/...
9090

9191
# Run linter with auto-fix
9292
lint-fix:
93-
golangci-lint run --fix ./...
93+
golangci-lint run --fix ./cmd/... ./config/... ./internal/...

internal/admin/dashboard/dashboard.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ package dashboard
33

44
import (
55
"bytes"
6+
"crypto/sha256"
67
"embed"
8+
"encoding/hex"
79
"html/template"
810
"io/fs"
911
"net/http"
12+
"strings"
1013

1114
"github.com/labstack/echo/v5"
1215
)
@@ -22,7 +25,16 @@ type Handler struct {
2225

2326
// New creates a new dashboard handler with parsed templates and static file server.
2427
func New() (*Handler, error) {
25-
tmpl, err := template.ParseFS(content, "templates/*.html")
28+
assetVersions, err := buildAssetVersions("css/dashboard.css")
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
tmpl, err := template.New("layout").Funcs(template.FuncMap{
34+
"assetURL": func(path string) string {
35+
return assetURL(path, assetVersions)
36+
},
37+
}).ParseFS(content, "templates/*.html")
2638
if err != nil {
2739
return nil, err
2840
}
@@ -55,3 +67,32 @@ func (h *Handler) Static(c *echo.Context) error {
5567
h.staticFS.ServeHTTP(c.Response(), c.Request())
5668
return nil
5769
}
70+
71+
func buildAssetVersions(paths ...string) (map[string]string, error) {
72+
versions := make(map[string]string, len(paths))
73+
for _, path := range paths {
74+
normalizedPath := strings.TrimLeft(strings.TrimSpace(path), "/")
75+
if normalizedPath == "" {
76+
continue
77+
}
78+
data, err := content.ReadFile("static/" + normalizedPath)
79+
if err != nil {
80+
return nil, err
81+
}
82+
sum := sha256.Sum256(data)
83+
versions[normalizedPath] = hex.EncodeToString(sum[:6])
84+
}
85+
return versions, nil
86+
}
87+
88+
func assetURL(path string, versions map[string]string) string {
89+
normalizedPath := strings.TrimLeft(strings.TrimSpace(path), "/")
90+
if normalizedPath == "" {
91+
return "/admin/static/"
92+
}
93+
urlPath := "/admin/static/" + normalizedPath
94+
if version := versions[normalizedPath]; version != "" {
95+
return urlPath + "?v=" + version
96+
}
97+
return urlPath
98+
}

internal/admin/dashboard/dashboard_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dashboard
33
import (
44
"net/http"
55
"net/http/httptest"
6+
"regexp"
67
"strings"
78
"testing"
89

@@ -59,6 +60,9 @@ func TestIndex_ReturnsHTML(t *testing.T) {
5960
if strings.Contains(body, `x-init="init()"`) {
6061
t.Errorf("expected dashboard HTML not to call init() explicitly")
6162
}
63+
if !regexp.MustCompile(`/admin/static/css/dashboard\.css\?v=[0-9a-f]+`).MatchString(rec.Body.String()) {
64+
t.Errorf("expected versioned dashboard CSS link in page HTML")
65+
}
6266
}
6367

6468
func TestStatic_ServesCSS(t *testing.T) {

internal/admin/dashboard/static/css/dashboard.css

Lines changed: 172 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,10 @@ body {
835835
border-color: var(--accent);
836836
}
837837

838+
.models-filter-input {
839+
max-width: 840px;
840+
}
841+
838842
.table-wrapper {
839843
background: var(--bg-surface);
840844
border: 1px solid var(--border);
@@ -875,6 +879,9 @@ body {
875879

876880
.mono {
877881
font-family: 'SF Mono', Menlo, Consolas, monospace;
882+
}
883+
884+
.font-size-md {
878885
font-size: 13px;
879886
}
880887

@@ -942,6 +949,21 @@ td.col-price {
942949
border-color: color-mix(in srgb, var(--danger) 50%, var(--border));
943950
}
944951

952+
.table-icon-btn {
953+
width: 32px;
954+
min-width: 32px;
955+
height: 32px;
956+
padding: 0;
957+
gap: 0;
958+
border-radius: 999px;
959+
}
960+
961+
.table-icon-svg {
962+
width: 14px;
963+
height: 14px;
964+
flex-shrink: 0;
965+
}
966+
945967
.model-alias-editor {
946968
background: var(--bg-surface);
947969
border: 1px solid var(--border);
@@ -1487,27 +1509,44 @@ td.col-price {
14871509
gap: 10px;
14881510
}
14891511

1490-
.audit-filter-row .filter-input:first-child {
1491-
grid-column: span 4;
1512+
.audit-filter-select {
1513+
grid-column: span 2;
1514+
min-width: 0;
14921515
}
14931516

1494-
.audit-filter-input {
1495-
grid-column: span 2;
1517+
.audit-filter-row-search .filter-input {
1518+
grid-column: 1 / -1;
14961519
max-width: none;
14971520
}
14981521

1499-
.audit-filter-select {
1522+
.audit-filter-row-controls .audit-filter-select {
15001523
grid-column: span 2;
1501-
min-width: 0;
15021524
}
15031525

1504-
.audit-filter-row .pagination-btn {
1505-
grid-column: span 1;
1506-
min-width: 84px;
1526+
.audit-filter-row-controls .pagination-btn {
1527+
grid-column: 11 / -1;
1528+
justify-self: end;
1529+
min-width: 108px;
15071530
}
15081531

1509-
.audit-filter-row-selects .audit-filter-select {
1510-
grid-column: span 2;
1532+
.audit-clear-btn {
1533+
display: inline-flex;
1534+
align-items: center;
1535+
justify-content: center;
1536+
gap: 8px;
1537+
background: #fff;
1538+
border-color: color-mix(in srgb, #fff 80%, var(--border));
1539+
color: #111110;
1540+
font-weight: 600;
1541+
}
1542+
1543+
.audit-clear-btn:hover:not(:disabled) {
1544+
background: #f5f5f5;
1545+
}
1546+
1547+
.audit-clear-btn .table-icon-svg {
1548+
width: 12px;
1549+
height: 12px;
15111550
}
15121551

15131552
.audit-log-summary {
@@ -2661,6 +2700,10 @@ body.conversation-drawer-open {
26612700
.aliases-editor-header .table-action-btn {
26622701
width: 100%;
26632702
}
2703+
.alias-actions-cell .table-icon-btn,
2704+
.aliases-editor-header .table-icon-btn {
2705+
width: 36px;
2706+
}
26642707

26652708
/* Audit page mobile */
26662709
.audit-log-toolbar {
@@ -2669,8 +2712,7 @@ body.conversation-drawer-open {
26692712
.audit-filter-row {
26702713
grid-template-columns: 1fr;
26712714
}
2672-
.audit-filter-row .filter-input:first-child,
2673-
.audit-filter-input,
2715+
.audit-filter-row .filter-input,
26742716
.audit-filter-select,
26752717
.audit-filter-row .pagination-btn {
26762718
grid-column: auto;
@@ -2763,27 +2805,132 @@ body.conversation-drawer-open {
27632805
position: absolute;
27642806
top: 12px;
27652807
right: 14px;
2766-
display: flex;
2767-
align-items: center;
2768-
gap: 6px;
2769-
flex-wrap: wrap;
2770-
justify-content: flex-end;
2771-
max-width: calc(100% - 28px);
2772-
}
2773-
2774-
.exec-pipeline-meta-chip {
27752808
display: inline-flex;
27762809
align-items: center;
2777-
padding: 3px 8px;
2778-
border-radius: 999px;
2810+
gap: 0;
2811+
min-width: 0;
2812+
max-width: calc(100% - 28px);
2813+
padding: 2px 10px;
2814+
border-radius: 12px;
27792815
border: 1px solid var(--border);
27802816
background: color-mix(in srgb, var(--bg-surface) 86%, transparent);
27812817
color: var(--text-muted);
2782-
font-size: 10px;
2818+
font-size: 12px;
2819+
font-weight: 500;
27832820
line-height: 1.2;
27842821
white-space: nowrap;
27852822
}
27862823

2824+
.exec-pipeline-meta-copy {
2825+
appearance: none;
2826+
cursor: pointer;
2827+
text-align: left;
2828+
overflow: hidden;
2829+
transition: background-color 0.15s, border-color 0.15s, color 0.15s, box-shadow 0.15s;
2830+
}
2831+
2832+
.exec-pipeline-meta-copy:hover,
2833+
.exec-pipeline-meta-copy:focus-visible {
2834+
border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
2835+
background: color-mix(in srgb, var(--accent) 8%, var(--bg-surface));
2836+
color: color-mix(in srgb, var(--accent) 74%, var(--text));
2837+
}
2838+
2839+
.exec-pipeline-meta-copy:focus-visible {
2840+
outline: none;
2841+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent);
2842+
}
2843+
2844+
.exec-pipeline-meta-label {
2845+
flex: 0 0 auto;
2846+
font-weight: 700;
2847+
}
2848+
2849+
.exec-pipeline-meta-placeholder {
2850+
flex: 0 0 auto;
2851+
max-width: 3ch;
2852+
margin-left: 4px;
2853+
overflow: hidden;
2854+
opacity: 1;
2855+
transition: max-width 0.18s ease, margin-left 0.18s ease, opacity 0.15s ease;
2856+
}
2857+
2858+
.exec-pipeline-meta-value {
2859+
flex: 0 1 auto;
2860+
max-width: 0;
2861+
margin-left: 0;
2862+
overflow: hidden;
2863+
opacity: 0;
2864+
text-overflow: clip;
2865+
transition: max-width 0.22s ease, margin-left 0.18s ease, opacity 0.15s ease;
2866+
}
2867+
2868+
.exec-pipeline-meta-copy:hover .exec-pipeline-meta-placeholder,
2869+
.exec-pipeline-meta-copy:focus-visible .exec-pipeline-meta-placeholder,
2870+
.exec-pipeline-meta-copied .exec-pipeline-meta-placeholder,
2871+
.exec-pipeline-meta-error .exec-pipeline-meta-placeholder {
2872+
max-width: 0;
2873+
margin-left: 0;
2874+
opacity: 0;
2875+
}
2876+
2877+
.exec-pipeline-meta-copy:hover .exec-pipeline-meta-value,
2878+
.exec-pipeline-meta-copy:focus-visible .exec-pipeline-meta-value,
2879+
.exec-pipeline-meta-copied .exec-pipeline-meta-value,
2880+
.exec-pipeline-meta-error .exec-pipeline-meta-value {
2881+
max-width: 42ch;
2882+
margin-left: 4px;
2883+
opacity: 1;
2884+
}
2885+
2886+
.exec-pipeline-meta-icon {
2887+
display: inline-flex;
2888+
align-items: center;
2889+
justify-content: center;
2890+
flex: 0 0 auto;
2891+
width: 0;
2892+
height: 14px;
2893+
margin-left: 0;
2894+
overflow: hidden;
2895+
opacity: 0;
2896+
line-height: 0;
2897+
transform: translateX(4px) translateY(1px) scale(0.84);
2898+
transition: width 0.18s ease, margin-left 0.18s ease, opacity 0.15s ease, transform 0.18s ease;
2899+
}
2900+
2901+
.exec-pipeline-meta-icon svg {
2902+
width: 14px;
2903+
height: 14px;
2904+
stroke: currentcolor;
2905+
fill: none;
2906+
stroke-width: 2;
2907+
stroke-linecap: round;
2908+
stroke-linejoin: round;
2909+
}
2910+
2911+
.exec-pipeline-meta-copied,
2912+
.exec-pipeline-meta-copied:hover,
2913+
.exec-pipeline-meta-copied:focus-visible {
2914+
background: color-mix(in srgb, var(--success) 12%, var(--bg));
2915+
border-color: color-mix(in srgb, var(--success) 40%, var(--border));
2916+
color: var(--success);
2917+
}
2918+
2919+
.exec-pipeline-meta-copied .exec-pipeline-meta-icon {
2920+
width: 14px;
2921+
margin-left: 6px;
2922+
opacity: 1;
2923+
transform: translateY(1px);
2924+
}
2925+
2926+
.exec-pipeline-meta-error,
2927+
.exec-pipeline-meta-error:hover,
2928+
.exec-pipeline-meta-error:focus-visible {
2929+
background: color-mix(in srgb, var(--danger) 10%, var(--bg));
2930+
border-color: color-mix(in srgb, var(--danger) 34%, var(--border));
2931+
color: var(--danger);
2932+
}
2933+
27872934
/* ─── Main pipeline row ─── */
27882935

27892936
.exec-pipeline-row {

0 commit comments

Comments
 (0)