Skip to content

Commit fa546fa

Browse files
authored
feat: multiple UI improvements for consistency (#91)
Also fixed version injection to the docker images.
1 parent e366d06 commit fa546fa

22 files changed

Lines changed: 452 additions & 216 deletions

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,4 @@ coverage.html
3636
# Misc
3737
*.log
3838
.ai_plans/
39+
!.build-version

.github/workflows/deploy-docker.core.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ jobs:
1818
steps:
1919
- name: Checkout repository
2020
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
21+
with:
22+
fetch-depth: 0
23+
24+
- name: Set build version info
25+
run: |
26+
VERSION=$(git describe --tags --always 2>/dev/null || echo "dev")
27+
COMMIT=$(git rev-parse --short HEAD)
28+
DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
29+
echo "VERSION=${VERSION}" >> .build-version
30+
echo "COMMIT=${COMMIT}" >> .build-version
31+
echo "DATE=${DATE}" >> .build-version
32+
echo "Writing build version: VERSION=${VERSION} COMMIT=${COMMIT} DATE=${DATE}"
2133
2234
- name: Deploy docker images
2335
uses: ethpandaops/actions/docker-build-push@7d7b7bfe36fe4cfa538acd0e23706a23ce07c3ac # master

.github/workflows/deploy-docker.ui.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ jobs:
1818
steps:
1919
- name: Checkout repository
2020
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
21+
with:
22+
fetch-depth: 0
23+
24+
- name: Set app version from git
25+
run: |
26+
VERSION=$(git describe --tags --always 2>/dev/null || git rev-parse --short HEAD)
27+
echo "Setting VITE_APP_VERSION=${VERSION}"
28+
# Write a .env file that Vite picks up at build time
29+
echo "VITE_APP_VERSION=${VERSION}" > ui/.env.production
2130
2231
- name: Deploy docker images
2332
uses: ethpandaops/actions/docker-build-push@7d7b7bfe36fe4cfa538acd0e23706a23ce07c3ac # master

Dockerfile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ ARG VERSION=dev
1717
ARG COMMIT=none
1818
ARG DATE=unknown
1919

20-
# Build the binary
21-
RUN CGO_ENABLED=0 GOOS=linux go build \
20+
# Build the binary. If .build-version exists (CI), source it to override ARG defaults.
21+
RUN if [ -f .build-version ]; then \
22+
. ./.build-version; \
23+
fi && \
24+
CGO_ENABLED=0 GOOS=linux go build \
2225
-ldflags "-X main.version=${VERSION} -X main.commit=${COMMIT} -X main.date=${DATE}" \
2326
-o /benchmarkoor ./cmd/benchmarkoor
2427

Dockerfile.ui

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Build stage
22
FROM node:22-alpine AS builder
33

4+
ARG APP_VERSION=""
5+
46
WORKDIR /app
57

68
# Copy package files first for caching
@@ -13,7 +15,8 @@ COPY ui/ .
1315
# Remove symlink and create empty results directory for build
1416
RUN rm -f public/results && mkdir -p public/results
1517

16-
# Build the UI
18+
# Build the UI (VITE_APP_VERSION is baked in at build time)
19+
ENV VITE_APP_VERSION=${APP_VERSION}
1720
RUN npm run build
1821

1922
# Final stage

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ docker-build-core:
110110
## docker-build-ui: Build the UI Docker image
111111
docker-build-ui:
112112
docker build \
113+
--build-arg APP_VERSION=$(VERSION) \
113114
-t $(DOCKER_IMAGE_UI):$(DOCKER_TAG) \
114115
-t $(DOCKER_IMAGE_UI):latest \
115116
-f Dockerfile.ui .

ui/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
})()
1515
</script>
1616
</head>
17-
<body>
17+
<body class="bg-gray-50 dark:bg-gray-900">
1818
<div id="root"></div>
1919
<script type="module" src="/src/main.tsx"></script>
2020
</body>

ui/src/api/client.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ export interface FetchResult<T> {
55
status: number
66
}
77

8+
// Append a cache-busting query parameter rounded to the current minute.
9+
// Requests within the same minute share the same cache key.
10+
function cacheBustUrl(url: string): string {
11+
const separator = url.includes('?') ? '&' : '?'
12+
return `${url}${separator}_t=${Math.floor(Date.now() / 60000)}`
13+
}
14+
815
// Check if the content type indicates JSON
916
function isJsonContentType(response: Response): boolean {
1017
const contentType = response.headers.get('content-type')
@@ -17,7 +24,7 @@ export async function fetchViaS3(
1724
url: string,
1825
init?: RequestInit,
1926
): Promise<Response> {
20-
const resp = await fetch(url, { credentials: 'include' })
27+
const resp = await fetch(cacheBustUrl(url), { credentials: 'include' })
2128
if (!resp.ok) return resp
2229

2330
const { url: presignedUrl } = await resp.json()
@@ -31,7 +38,7 @@ export async function fetchData<T>(path: string): Promise<FetchResult<T>> {
3138

3239
const response = isS3Mode(config)
3340
? await fetchViaS3(url)
34-
: await fetch(url)
41+
: await fetch(cacheBustUrl(url))
3542

3643
if (!response.ok) {
3744
return { data: null, status: response.status }
@@ -113,7 +120,7 @@ export async function fetchText(path: string): Promise<FetchResult<string>> {
113120

114121
const response = isS3Mode(config)
115122
? await fetchViaS3(url)
116-
: await fetch(url)
123+
: await fetch(cacheBustUrl(url))
117124

118125
if (!response.ok) {
119126
return { data: null, status: response.status }

ui/src/components/layout/Footer.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import packageJson from '../../../package.json'
22

3+
const appVersion = import.meta.env.VITE_APP_VERSION || packageJson.version
4+
35
export function Footer() {
46
return (
57
<footer className="border-t border-gray-200 bg-white py-6 dark:border-gray-800 dark:bg-gray-900">
@@ -14,7 +16,7 @@ export function Footer() {
1416
ethpandaops/benchmarkoor
1517
</a>
1618
<span className="mx-2"></span>
17-
<span>v{packageJson.version}</span>
19+
<span>v{appVersion}</span>
1820
</div>
1921
</footer>
2022
)

ui/src/components/layout/Header.tsx

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import { useState, useEffect } from 'react'
22
import { Link, useMatchRoute, useNavigate } from '@tanstack/react-router'
33
import clsx from 'clsx'
4-
import { Sun, Moon, LogIn, LogOut, Shield, User } from 'lucide-react'
4+
import { Sun, Moon, LogIn, LogOut, Shield, User, Menu, X } from 'lucide-react'
55
import { useAuth } from '@/hooks/useAuth'
66

7-
function NavLink({ to, children }: { to: string; children: React.ReactNode }) {
7+
function NavLink({ to, children, onClick }: { to: string; children: React.ReactNode; onClick?: () => void }) {
88
const matchRoute = useMatchRoute()
99
const isActive = matchRoute({ to, fuzzy: true })
1010

1111
return (
1212
<Link
1313
to={to}
14+
onClick={onClick}
1415
className={clsx(
1516
'rounded-sm px-3 py-1.5 text-sm/6 font-medium transition-colors',
1617
isActive
@@ -50,7 +51,7 @@ function ThemeSwitcher() {
5051
)
5152
}
5253

53-
function AuthControls() {
54+
function AuthControls({ onNavigate }: { onNavigate?: () => void }) {
5455
const { user, isApiEnabled, isAdmin, logout } = useAuth()
5556
const navigate = useNavigate()
5657

@@ -60,6 +61,7 @@ function AuthControls() {
6061
return (
6162
<Link
6263
to="/login"
64+
onClick={onNavigate}
6365
className="flex items-center gap-1.5 rounded-sm px-3 py-1.5 text-sm font-medium text-gray-600 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-700/50 dark:hover:text-gray-100"
6466
>
6567
<LogIn className="size-4" />
@@ -70,12 +72,13 @@ function AuthControls() {
7072

7173
const handleLogout = async () => {
7274
await logout()
75+
onNavigate?.()
7376
navigate({ to: '/runs' })
7477
}
7578

7679
return (
7780
<div className="flex items-center gap-2">
78-
{isAdmin && <NavLink to="/admin">Admin</NavLink>}
81+
{isAdmin && <NavLink to="/admin" onClick={onNavigate}>Admin</NavLink>}
7982
<div className="flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-300">
8083
<User className="size-4" />
8184
<span>{user.username}</span>
@@ -93,6 +96,10 @@ function AuthControls() {
9396
}
9497

9598
export function Header() {
99+
const [mobileOpen, setMobileOpen] = useState(false)
100+
101+
const closeMobile = () => setMobileOpen(false)
102+
96103
return (
97104
<header className="border-b border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800">
98105
<div className="mx-auto flex max-w-7xl items-center gap-8 px-4 py-2">
@@ -101,15 +108,40 @@ export function Header() {
101108
<img src="/img/logo_white.png" alt="Benchmarkoor" className="hidden h-12 dark:block" />
102109
<span className="text-lg/7 font-semibold text-gray-900 dark:text-gray-100">Benchmarkoor</span>
103110
</Link>
104-
<nav className="flex items-center gap-1">
111+
112+
{/* Desktop nav */}
113+
<nav className="hidden items-center gap-1 md:flex">
105114
<NavLink to="/runs">Runs</NavLink>
106115
<NavLink to="/suites">Suites</NavLink>
107116
</nav>
108-
<div className="ml-auto flex items-center gap-2">
117+
<div className="ml-auto hidden items-center gap-2 md:flex">
109118
<AuthControls />
110119
<ThemeSwitcher />
111120
</div>
121+
122+
{/* Mobile hamburger */}
123+
<button
124+
onClick={() => setMobileOpen(!mobileOpen)}
125+
className="ml-auto rounded-sm p-2 text-gray-500 hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 md:hidden"
126+
aria-label="Toggle menu"
127+
>
128+
{mobileOpen ? <X className="size-5" /> : <Menu className="size-5" />}
129+
</button>
112130
</div>
131+
132+
{/* Mobile menu */}
133+
{mobileOpen && (
134+
<div className="border-t border-gray-200 px-4 py-3 dark:border-gray-700 md:hidden">
135+
<nav className="flex flex-col gap-1">
136+
<NavLink to="/runs" onClick={closeMobile}>Runs</NavLink>
137+
<NavLink to="/suites" onClick={closeMobile}>Suites</NavLink>
138+
</nav>
139+
<div className="mt-3 flex items-center gap-2 border-t border-gray-200 pt-3 dark:border-gray-700">
140+
<AuthControls onNavigate={closeMobile} />
141+
<ThemeSwitcher />
142+
</div>
143+
</div>
144+
)}
113145
</header>
114146
)
115147
}

0 commit comments

Comments
 (0)