Compare commits

...

4 Commits

Author SHA1 Message Date
CanbiZ (MickLesk)
4e8021e6fd fix(frontend): wrap Tooltip in TooltipProvider 2026-01-28 14:35:18 +01:00
CanbiZ (MickLesk)
29cf802a9a fix(api): add github-versions endpoint and fix legacy versions route 2026-01-28 14:07:29 +01:00
CanbiZ (MickLesk)
649ad2cc8b feat(frontend): show tooltip for pinned versions 2026-01-28 14:03:46 +01:00
CanbiZ (MickLesk)
760603da54 fix(frontend): use github-versions.json for version display
- Update AppVersion type to match new format with slug field
- Switch from versions.json to github-versions.json API
- Simplify version matching by direct slug comparison
- Remove 'Loading versions...' text - show nothing if no version found
2026-01-28 14:02:20 +01:00
6 changed files with 77 additions and 23 deletions

View File

@@ -0,0 +1,36 @@
import { NextResponse } from "next/server";
import { promises as fs } from "node:fs";
import path from "node:path";
import type { GitHubVersionsResponse } from "@/lib/types";
export const dynamic = "force-static";
const jsonDir = "public/json";
const versionsFileName = "github-versions.json";
const encoding = "utf-8";
async function getVersions(): Promise<GitHubVersionsResponse> {
const filePath = path.resolve(jsonDir, versionsFileName);
const fileContent = await fs.readFile(filePath, encoding);
const data: GitHubVersionsResponse = JSON.parse(fileContent);
return data;
}
export async function GET() {
try {
const versions = await getVersions();
return NextResponse.json(versions);
}
catch (error) {
console.error(error);
const err = error as globalThis.Error;
return NextResponse.json({
generated: "",
versions: [],
error: err.message || "An unexpected error occurred",
}, {
status: 500,
});
}
}

View File

@@ -3,18 +3,22 @@ import { NextResponse } from "next/server";
import { promises as fs } from "node:fs"; import { promises as fs } from "node:fs";
import path from "node:path"; import path from "node:path";
import type { AppVersion } from "@/lib/types";
export const dynamic = "force-static"; export const dynamic = "force-static";
const jsonDir = "public/json"; const jsonDir = "public/json";
const versionsFileName = "versions.json"; const versionsFileName = "versions.json";
const encoding = "utf-8"; const encoding = "utf-8";
interface LegacyVersion {
name: string;
version: string;
date: string;
}
async function getVersions() { async function getVersions() {
const filePath = path.resolve(jsonDir, versionsFileName); const filePath = path.resolve(jsonDir, versionsFileName);
const fileContent = await fs.readFile(filePath, encoding); const fileContent = await fs.readFile(filePath, encoding);
const versions: AppVersion[] = JSON.parse(fileContent); const versions: LegacyVersion[] = JSON.parse(fileContent);
const modifiedVersions = versions.map((version) => { const modifiedVersions = versions.map((version) => {
let newName = version.name; let newName = version.name;

View File

@@ -1,13 +1,13 @@
"use client"; "use client";
import { X } from "lucide-react"; import { X, HelpCircle } from "lucide-react";
import { Suspense } from "react"; import { Suspense } from "react";
import Image from "next/image"; import Image from "next/image";
import type { AppVersion, Script } from "@/lib/types"; import type { AppVersion, Script } from "@/lib/types";
import { cleanSlug } from "@/lib/utils/resource-utils";
import { Separator } from "@/components/ui/separator"; import { Separator } from "@/components/ui/separator";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useVersions } from "@/hooks/use-versions"; import { useVersions } from "@/hooks/use-versions";
import { basePath } from "@/config/site-config"; import { basePath } from "@/config/site-config";
import { extractDate } from "@/lib/time"; import { extractDate } from "@/lib/time";
@@ -108,18 +108,31 @@ function VersionInfo({ item }: { item: Script }) {
const { data: versions = [], isLoading } = useVersions(); const { data: versions = [], isLoading } = useVersions();
if (isLoading || versions.length === 0) { if (isLoading || versions.length === 0) {
return <p className="text-sm text-muted-foreground">Loading versions...</p>; return null;
} }
const matchedVersion = versions.find((v: AppVersion) => { const matchedVersion = versions.find((v: AppVersion) => v.slug === item.slug);
const cleanName = v.name.replace(/[^a-z0-9]/gi, "").toLowerCase();
return cleanName === cleanSlug(item.slug) || cleanName.includes(cleanSlug(item.slug));
});
if (!matchedVersion) if (!matchedVersion)
return null; return null;
return <span className="font-medium text-sm">{matchedVersion.version}</span>; return (
<span className="font-medium text-sm flex items-center gap-1">
{matchedVersion.version}
{matchedVersion.pinned && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent className="max-w-xs">
<p>This version is pinned. We test each update for breaking changes before releasing new versions.</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</span>
);
} }
export function ScriptItem({ item, setSelectedScript }: ScriptItemProps) { export function ScriptItem({ item, setSelectedScript }: ScriptItemProps) {

View File

@@ -2,7 +2,7 @@
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import type { AppVersion } from "@/lib/types"; import type { AppVersion, GitHubVersionsResponse } from "@/lib/types";
import { fetchVersions } from "@/lib/data"; import { fetchVersions } from "@/lib/data";
@@ -10,14 +10,8 @@ export function useVersions() {
return useQuery<AppVersion[]>({ return useQuery<AppVersion[]>({
queryKey: ["versions"], queryKey: ["versions"],
queryFn: async () => { queryFn: async () => {
const fetchedVersions = await fetchVersions(); const response: GitHubVersionsResponse = await fetchVersions();
if (Array.isArray(fetchedVersions)) { return response.versions ?? [];
return fetchedVersions;
}
if (fetchedVersions && typeof fetchedVersions === "object") {
return [fetchedVersions];
}
return [];
}, },
}); });
} }

View File

@@ -10,7 +10,7 @@ export async function fetchCategories() {
} }
export async function fetchVersions() { export async function fetchVersions() {
const response = await fetch(`/ProxmoxVE/api/versions`); const response = await fetch(`/ProxmoxVE/api/github-versions`);
if (!response.ok) { if (!response.ok) {
throw new Error(`Failed to fetch versions: ${response.statusText}`); throw new Error(`Failed to fetch versions: ${response.statusText}`);
} }

View File

@@ -63,7 +63,14 @@ export type OperatingSystem = {
}; };
export type AppVersion = { export type AppVersion = {
name: string; slug: string;
repo: string;
version: string; version: string;
date: Date; pinned: boolean;
date: string;
};
export type GitHubVersionsResponse = {
generated: string;
versions: AppVersion[];
}; };