fix(benchmark): Add settings nav link, fix submission bug, improve UX

- Add Benchmark to Settings sidebar navigation
- Fix Luxon DateTime bug when saving submission timestamp
- Add privacy explanation text before Share button
- Add error handling and display for failed submissions
- Show "Submitting..." state and success confirmation
- Add link to view leaderboard after successful submission

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Chris Sherwood 2026-01-21 16:55:11 -08:00 committed by Jake Turner
parent 755807f95e
commit 6efd049424
3 changed files with 47 additions and 11 deletions

View File

@ -5,6 +5,7 @@ import Docker from 'dockerode'
import si from 'systeminformation'
import { v4 as uuidv4 } from 'uuid'
import axios from 'axios'
import { DateTime } from 'luxon'
import BenchmarkResult from '#models/benchmark_result'
import BenchmarkSetting from '#models/benchmark_setting'
import { SystemService } from '#services/system_service'
@ -154,7 +155,7 @@ export class BenchmarkService {
if (response.data.success) {
result.submitted_to_repository = true
result.submitted_at = new Date() as any
result.submitted_at = DateTime.now()
result.repository_id = response.data.repository_id
await result.save()

View File

@ -1,4 +1,5 @@
import {
ChartBarIcon,
Cog6ToothIcon,
CommandLineIcon,
FolderIcon,
@ -16,6 +17,7 @@ import { getServiceLink } from '~/lib/navigation'
const navigation = [
{ name: 'Apps', href: '/settings/apps', icon: CommandLineIcon, current: false },
{ name: 'Benchmark', href: '/settings/benchmark', icon: ChartBarIcon, current: false },
{ name: 'Legal Notices', href: '/settings/legal', icon: IconGavel, current: false },
{ name: 'Maps Manager', href: '/settings/maps', icon: IconMapRoute, current: false },
{ name: 'Models Manager', href: '/settings/models', icon: IconDatabaseStar, current: false },

View File

@ -128,18 +128,27 @@ export default function BenchmarkPage(props: {
})
// Submit to repository mutation
const [submitError, setSubmitError] = useState<string | null>(null)
const submitResult = useMutation({
mutationFn: async (benchmarkId?: string) => {
setSubmitError(null)
const res = await fetch('/api/benchmark/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ benchmark_id: benchmarkId }),
})
return res.json()
const data = await res.json()
if (!data.success) {
throw new Error(data.error || 'Failed to submit benchmark')
}
return data
},
onSuccess: () => {
refetchLatest()
},
onError: (error: Error) => {
setSubmitError(error.message)
},
})
// Simulate progress during sync benchmark (since we don't get SSE updates)
@ -336,21 +345,45 @@ export default function BenchmarkPage(props: {
Your NOMAD Score is a weighted composite of all benchmark results.
</p>
{!latestResult.submitted_to_repository && (
<StyledButton
onClick={() => submitResult.mutate(latestResult.benchmark_id)}
disabled={submitResult.isPending}
leftIcon={<CloudArrowUpIcon className="w-5 h-5" />}
>
Share with Community
</StyledButton>
<div className="space-y-3">
<p className="text-sm text-desert-stone-dark">
Share your benchmark score anonymously with the NOMAD community. Only your hardware specs and scores are sent no identifying information.
</p>
<StyledButton
onClick={() => submitResult.mutate(latestResult.benchmark_id)}
disabled={submitResult.isPending}
leftIcon={<CloudArrowUpIcon className="w-5 h-5" />}
>
{submitResult.isPending ? 'Submitting...' : 'Share with Community'}
</StyledButton>
{submitError && (
<Alert
type="error"
title="Submission Failed"
message={submitError}
variant="bordered"
dismissible
onDismiss={() => setSubmitError(null)}
/>
)}
</div>
)}
{latestResult.submitted_to_repository && (
<Alert
type="success"
title="Shared with Community"
message={`Repository ID: ${latestResult.repository_id}`}
message="Your benchmark has been submitted to the community leaderboard. Thanks for contributing!"
variant="bordered"
/>
>
<a
href="https://benchmark.projectnomad.us"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-desert-green hover:underline mt-2 inline-block"
>
View the leaderboard
</a>
</Alert>
)}
</div>
</div>