mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2025-04-19 15:18:06 +00:00

* Refactor ScriptItem and Buttons components to enhance layout and integrate dropdown for links. Update InterFaces component for improved styling and structure. * Add React Query integration and enhance component structure - Introduced `@tanstack/react-query` for data fetching and state management. - Added `QueryProvider` component to wrap the application with QueryClient. - Refactored `ScriptItem` to utilize `useVersions` hook for fetching versions. - Created `ResourceDisplay` and `VersionBadge` components for better resource representation. - Improved layout and styling across various components, including `Alerts`, `Buttons`, and `DefaultPassword`. - Updated `layout.tsx` to include the new `QueryProvider` for global state management. * Remove bun.lock file to streamline dependency management and prevent potential conflicts. * Update dependencies in package.json and package-lock.json - Removed `@vercel/analytics` from dependencies. - Upgraded `vitest` and related packages to version `3.1.1`. - Updated various packages to their latest versions for improved performance and compatibility. - Adjusted Node.js engine requirements to support newer versions. * Update dependencies in package.json and package-lock.json - Upgraded various Radix UI components to their latest versions for improved functionality and performance. - Updated `chart.js`, `class-variance-authority`, `cmdk`, `framer-motion`, `fuse.js`, `nuqs`, `pocketbase`, and other packages to their latest versions. - Enhanced TypeScript and ESLint packages for better type checking and linting capabilities. - Updated Tailwind CSS and related plugins for improved styling and utility classes. - Adjusted Node.js engine requirements in several packages to support newer versions.
161 lines
6.0 KiB
TypeScript
161 lines
6.0 KiB
TypeScript
"use client";
|
|
|
|
import { extractDate } from "@/lib/time";
|
|
import { AppVersion, Script } from "@/lib/types";
|
|
|
|
import { X } from "lucide-react";
|
|
import Image from "next/image";
|
|
|
|
import { Separator } from "@/components/ui/separator";
|
|
import { basePath } from "@/config/siteConfig";
|
|
import { useVersions } from "@/hooks/useVersions";
|
|
import { cleanSlug } from "@/lib/utils/resource-utils";
|
|
import { Suspense } from "react";
|
|
import { ResourceDisplay } from "./ResourceDisplay";
|
|
import { getDisplayValueFromType } from "./ScriptInfoBlocks";
|
|
import Alerts from "./ScriptItems/Alerts";
|
|
import DefaultPassword from "./ScriptItems/DefaultPassword";
|
|
import Description from "./ScriptItems/Description";
|
|
import InstallCommand from "./ScriptItems/InstallCommand";
|
|
import Tooltips from "./ScriptItems/Tooltips";
|
|
import InterFaces from "./ScriptItems/InterFaces";
|
|
import Buttons from "./ScriptItems/Buttons";
|
|
|
|
interface ScriptItemProps {
|
|
item: Script;
|
|
setSelectedScript: (script: string | null) => void;
|
|
}
|
|
|
|
function ScriptHeader({ item }: { item: Script }) {
|
|
const defaultInstallMethod = item.install_methods?.[0];
|
|
const os = defaultInstallMethod?.resources?.os || "Proxmox Node";
|
|
const version = defaultInstallMethod?.resources?.version || "";
|
|
|
|
return (
|
|
<div className="flex flex-col lg:flex-row gap-6 w-full">
|
|
<div className="flex flex-col md:flex-row gap-6 flex-grow">
|
|
<div className="flex-shrink-0">
|
|
<Image
|
|
className="h-32 w-32 rounded-xl bg-gradient-to-br from-accent/40 to-accent/60 object-contain p-3 shadow-lg transition-transform hover:scale-105"
|
|
src={item.logo || `/${basePath}/logo.png`}
|
|
width={400}
|
|
onError={(e) => ((e.currentTarget as HTMLImageElement).src = `/${basePath}/logo.png`)}
|
|
height={400}
|
|
alt={item.name}
|
|
unoptimized
|
|
/>
|
|
</div>
|
|
<div className="flex flex-col justify-between flex-grow space-y-4">
|
|
<div className="space-y-2">
|
|
<div className="flex items-start justify-between">
|
|
<div>
|
|
<h1 className="text-2xl font-semibold tracking-tight flex items-center gap-2">
|
|
{item.name}
|
|
<VersionInfo item={item} />
|
|
<span className="inline-flex items-center rounded-md bg-accent/30 px-2 py-1 text-sm">
|
|
{getDisplayValueFromType(item.type)}
|
|
</span>
|
|
</h1>
|
|
<div className="mt-1 flex items-center gap-3 text-sm text-muted-foreground">
|
|
<span>Added {extractDate(item.date_created)}</span>
|
|
<span>•</span>
|
|
<span className=" capitalize">
|
|
{os} {version}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{/* <VersionInfo item={item} /> */}
|
|
</div>
|
|
<div className="flex flex-col gap-2 text-sm text-muted-foreground">
|
|
{defaultInstallMethod?.resources && (
|
|
<ResourceDisplay
|
|
title="Default"
|
|
cpu={defaultInstallMethod.resources.cpu}
|
|
ram={defaultInstallMethod.resources.ram}
|
|
hdd={defaultInstallMethod.resources.hdd}
|
|
/>
|
|
)}
|
|
{item.install_methods.find((method) => method.type === "alpine")?.resources && (
|
|
<ResourceDisplay
|
|
title="Alpine"
|
|
{...item.install_methods.find((method) => method.type === "alpine")!.resources!}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col gap-4 justify-between">
|
|
<InterFaces item={item} />
|
|
<div className="flex justify-end">
|
|
<Buttons item={item} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function VersionInfo({ item }: { item: Script }) {
|
|
const { data: versions = [], isLoading } = useVersions();
|
|
|
|
if (isLoading || versions.length === 0) {
|
|
return <p className="text-sm text-muted-foreground">Loading versions...</p>;
|
|
}
|
|
|
|
const matchedVersion = versions.find((v: AppVersion) => {
|
|
const cleanName = v.name.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
return cleanName === cleanSlug(item.slug) || cleanName.includes(cleanSlug(item.slug));
|
|
});
|
|
|
|
if (!matchedVersion) return null;
|
|
|
|
return <span className="font-medium text-sm">{matchedVersion.version}</span>;
|
|
}
|
|
|
|
export function ScriptItem({ item, setSelectedScript }: ScriptItemProps) {
|
|
const closeScript = () => {
|
|
window.history.pushState({}, document.title, window.location.pathname);
|
|
setSelectedScript(null);
|
|
};
|
|
|
|
return (
|
|
<div className="w-full max-w-5xl mx-auto">
|
|
<div className="flex w-full flex-col">
|
|
<div className="mb-3 flex items-center justify-between">
|
|
<h2 className="text-2xl font-semibold tracking-tight text-foreground/90">Selected Script</h2>
|
|
<button
|
|
onClick={closeScript}
|
|
className="rounded-full p-2 text-muted-foreground hover:bg-card/50 transition-colors"
|
|
>
|
|
<X className="h-5 w-5" />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="rounded-xl border border-border/40 bg-gradient-to-b from-card/30 to-background/50 backdrop-blur-sm shadow-sm">
|
|
<div className="p-6 space-y-6">
|
|
<Suspense fallback={<div className="animate-pulse h-32 bg-accent/20 rounded-xl" />}>
|
|
<ScriptHeader item={item} />
|
|
</Suspense>
|
|
|
|
<Description item={item} />
|
|
<Alerts item={item} />
|
|
|
|
<div className="mt-4 rounded-lg border shadow-sm">
|
|
<div className="flex gap-3 px-4 py-2 bg-accent/25">
|
|
<h2 className="text-lg font-semibold">How to {item.type === "misc" ? "use" : "install"}</h2>
|
|
<Tooltips item={item} />
|
|
</div>
|
|
<Separator />
|
|
<div className="">
|
|
<InstallCommand item={item} />
|
|
</div>
|
|
</div>
|
|
|
|
<DefaultPassword item={item} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|