diff --git a/frontend/src/components/admin/VersionInfo.tsx b/frontend/src/components/admin/VersionInfo.tsx
index 7063722f..653cac38 100644
--- a/frontend/src/components/admin/VersionInfo.tsx
+++ b/frontend/src/components/admin/VersionInfo.tsx
@@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { Info, ArrowUpCircle } from 'lucide-react';
import { api } from '../../config/api';
+import { githubReleaseUrl as releaseUrl } from '../../utils/githubReleaseUrl';
import packageJson from '../../../package.json';
// Frontend version from package.json
@@ -63,20 +64,48 @@ export const VersionInfo: React.FC = () => {
{channelBadge}
-
Frontend: v{FRONTEND_VERSION}
+
{versionInfo && (
-
Backend: v{versionInfo.backend}
+
)}
- {updateInfo?.enabled && updateInfo?.updateAvailable && (
-
+
)}
);
diff --git a/frontend/src/utils/__tests__/githubReleaseUrl.test.ts b/frontend/src/utils/__tests__/githubReleaseUrl.test.ts
new file mode 100644
index 00000000..6a3afde0
--- /dev/null
+++ b/frontend/src/utils/__tests__/githubReleaseUrl.test.ts
@@ -0,0 +1,30 @@
+/**
+ * Regression coverage for the version → release-page URL template (#566).
+ *
+ * release-please tags every release as `vX.Y.Z[-beta.N]`. If anyone
+ * later refactors the helper (e.g. switches the prefix, the repo path,
+ * or strips the channel suffix), these assertions will fail loud
+ * instead of silently producing dead links in the admin sidebar.
+ */
+import { describe, expect, it } from 'vitest';
+import { githubReleaseUrl } from '../githubReleaseUrl';
+
+describe('githubReleaseUrl', () => {
+ it('maps a stable version to its tag page', () => {
+ expect(githubReleaseUrl('3.55.0')).toBe(
+ 'https://github.com/the-luap/picpeak/releases/tag/v3.55.0',
+ );
+ });
+
+ it('maps a beta version (with channel suffix) to its tag page', () => {
+ expect(githubReleaseUrl('3.55.0-beta.0')).toBe(
+ 'https://github.com/the-luap/picpeak/releases/tag/v3.55.0-beta.0',
+ );
+ });
+
+ it('does not double-prefix the v when given a bare version', () => {
+ // Defensive — the helper is the single source of truth for the
+ // leading "v". Callers must pass the bare version string.
+ expect(githubReleaseUrl('3.55.0')).not.toContain('vv');
+ });
+});
diff --git a/frontend/src/utils/githubReleaseUrl.ts b/frontend/src/utils/githubReleaseUrl.ts
new file mode 100644
index 00000000..adaf6180
--- /dev/null
+++ b/frontend/src/utils/githubReleaseUrl.ts
@@ -0,0 +1,14 @@
+/**
+ * Build the GitHub release page URL for a given version string.
+ *
+ * Version strings already carry their channel suffix (e.g. `3.55.0`
+ * for stable, `3.55.0-beta.0` for beta). release-please tags every
+ * release as `vX.Y.Z[-beta.N]`, so a pure string template covers
+ * both channels without branching on the suffix.
+ *
+ * Used by the admin sidebar version display to deep-link to release
+ * notes for the running version (#566) and by the update-available
+ * indicator to link to the upgrade target's notes.
+ */
+export const githubReleaseUrl = (version: string): string =>
+ `https://github.com/the-luap/picpeak/releases/tag/v${version}`;