mirror of
https://github.com/community-scripts/ProxmoxVE
synced 2025-02-14 11:49:16 +00:00
Refactor all components to use data from new API
This commit is contained in:
parent
1ef3c5a0eb
commit
76cf85293c
@ -42,10 +42,10 @@ export default function ScriptAccordion({
|
||||
useEffect(() => {
|
||||
if (selectedScript) {
|
||||
const category = items.find((category) =>
|
||||
category.expand.items.some((script) => script.title === selectedScript),
|
||||
category.scripts.some((script) => script.name === selectedScript),
|
||||
);
|
||||
if (category) {
|
||||
setExpandedItem(category.catagoryName);
|
||||
setExpandedItem(category.name);
|
||||
handleSelected(selectedScript);
|
||||
}
|
||||
}
|
||||
@ -60,81 +60,80 @@ export default function ScriptAccordion({
|
||||
{items.map((category) => (
|
||||
<AccordionItem
|
||||
key={category.id + ":category"}
|
||||
value={category.catagoryName}
|
||||
value={category.name}
|
||||
className={cn("sm:text-md flex flex-col border-none", {
|
||||
"rounded-lg bg-accent/30": expandedItem === category.catagoryName,
|
||||
"rounded-lg bg-accent/30": expandedItem === category.name,
|
||||
})}
|
||||
>
|
||||
<AccordionTrigger
|
||||
className={cn(
|
||||
"duration-250 rounded-lg transition ease-in-out hover:-translate-y-1 hover:scale-105 hover:bg-accent",
|
||||
{ "": expandedItem === category.catagoryName },
|
||||
{ "": expandedItem === category.name },
|
||||
)}
|
||||
>
|
||||
<div className="mr-2 flex w-full items-center justify-between">
|
||||
<span className="pl-2">{category.catagoryName} </span>
|
||||
<span className="pl-2">{category.name} </span>
|
||||
<span className="rounded-full bg-gray-200 px-2 py-1 text-xs text-muted-foreground hover:no-underline dark:bg-blue-800/20">
|
||||
{category.expand.items.length}
|
||||
{category.scripts.length}
|
||||
</span>
|
||||
</div>{" "}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent
|
||||
data-state={
|
||||
expandedItem === category.catagoryName ? "open" : "closed"
|
||||
expandedItem === category.name ? "open" : "closed"
|
||||
}
|
||||
className="pt-0"
|
||||
>
|
||||
{category.expand.items
|
||||
{category.scripts
|
||||
.slice()
|
||||
.sort((a, b) => a.title.localeCompare(b.title))
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((script, index) => (
|
||||
<div key={index}>
|
||||
<Link
|
||||
href={{
|
||||
pathname: "/scripts",
|
||||
query: { id: script.title },
|
||||
query: { id: script.name},
|
||||
}}
|
||||
prefetch={false}
|
||||
className={`flex cursor-pointer items-center justify-between gap-1 px-1 py-1 text-muted-foreground hover:rounded-lg hover:bg-accent/60 hover:dark:bg-accent/20 ${
|
||||
selectedScript === script.title
|
||||
selectedScript === script.name
|
||||
? "rounded-lg bg-accent font-semibold dark:bg-accent/30 dark:text-white"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => handleSelected(script.title)}
|
||||
onClick={() => handleSelected(script.name)}
|
||||
ref={(el) => {
|
||||
linkRefs.current[script.title] = el;
|
||||
linkRefs.current[script.name] = el;
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={script.logo}
|
||||
height={16}
|
||||
width={16}
|
||||
unoptimized
|
||||
onError={(e) =>
|
||||
((e.currentTarget as HTMLImageElement).src =
|
||||
"/logo.png")
|
||||
}
|
||||
alt={script.title}
|
||||
className="mr-1 w-4 h-4 rounded-full"
|
||||
/>
|
||||
<span className="flex items-center gap-2">
|
||||
{script.title}
|
||||
{script.isMostViewed && (
|
||||
<Star className="h-3 w-3 text-yellow-500"></Star>
|
||||
)}
|
||||
</span>
|
||||
<div className="flex items-center">
|
||||
<Image
|
||||
src={script.logo || "/logo.png"}
|
||||
height={16}
|
||||
width={16}
|
||||
unoptimized
|
||||
onError={(e) =>
|
||||
((e.currentTarget as HTMLImageElement).src =
|
||||
"/logo.png")
|
||||
}
|
||||
alt={script.name}
|
||||
className="mr-1 w-4 h-4 rounded-full"
|
||||
/>
|
||||
<span className="flex items-center gap-2">
|
||||
{script.name}
|
||||
</span>
|
||||
</div>
|
||||
<Badge
|
||||
className={cn(
|
||||
"ml-auto w-[37.69px] justify-center text-center",
|
||||
{
|
||||
"text-primary/75": script.item_type === "VM",
|
||||
"text-yellow-500/75": script.item_type === "LXC",
|
||||
"border-none": script.item_type === "",
|
||||
hidden: !["VM", "LXC", ""].includes(script.item_type),
|
||||
"text-primary/75": script.type === "vm",
|
||||
"text-yellow-500/75": script.type === "ct",
|
||||
"border-none": script.type === "misc",
|
||||
hidden: !["VM", "LXC", ""].includes(script.type),
|
||||
},
|
||||
)}
|
||||
>
|
||||
{script.item_type}
|
||||
{script.type}
|
||||
</Badge>
|
||||
</Link>
|
||||
</div>
|
||||
|
@ -21,9 +21,9 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
|
||||
const latestScripts = useMemo(() => {
|
||||
if (!items) return [];
|
||||
const scripts = items.flatMap((category) => category.expand.items || []);
|
||||
const scripts = items.flatMap((category) => category.scripts || []);
|
||||
return scripts.sort(
|
||||
(a, b) => new Date(b.created).getTime() - new Date(a.created).getTime(),
|
||||
(a, b) => new Date(b.date_created).getTime() - new Date(a.date_created).getTime(),
|
||||
);
|
||||
}, [items]);
|
||||
|
||||
@ -70,14 +70,14 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
<div className="min-w flex w-full flex-row flex-wrap gap-4">
|
||||
{latestScripts.slice(startIndex, endIndex).map((item) => (
|
||||
<Card
|
||||
key={item.id}
|
||||
key={item.name}
|
||||
className="min-w-[250px] flex-1 flex-grow bg-accent/30"
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-3">
|
||||
<div className="flex h-16 w-16 items-center justify-center rounded-lg bg-accent p-1">
|
||||
<Image
|
||||
src={item.logo}
|
||||
src={item.logo || "/logo.png"}
|
||||
unoptimized
|
||||
height={64}
|
||||
width={64}
|
||||
@ -87,11 +87,11 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<p className="text-lg line-clamp-1">
|
||||
{item.title} {item.item_type}
|
||||
{item.name} {item.type}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground flex items-center gap-1">
|
||||
<CalendarPlus className="h-4 w-4" />
|
||||
{extractDate(item.created)}
|
||||
{extractDate(item.date_created)}
|
||||
</p>
|
||||
</div>
|
||||
</CardTitle>
|
||||
@ -106,7 +106,7 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
<Link
|
||||
href={{
|
||||
pathname: "/scripts",
|
||||
query: { id: item.title },
|
||||
query: { id: item.name },
|
||||
}}
|
||||
>
|
||||
View Script
|
||||
@ -120,100 +120,102 @@ export function LatestScripts({ items }: { items: Category[] }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function MostViewedScripts({ items }: { items: Category[] }) {
|
||||
const [page, setPage] = useState(1);
|
||||
// TODO: find way to determine what the most popular scripts are
|
||||
|
||||
const mostViewedScripts = useMemo(() => {
|
||||
if (!items) return [];
|
||||
const scripts = items.flatMap((category) => category.expand.items || []);
|
||||
const mostViewedScripts = scripts
|
||||
.filter((script) => script.isMostViewed)
|
||||
.map((script) => ({
|
||||
...script,
|
||||
}));
|
||||
return mostViewedScripts;
|
||||
}, [items]);
|
||||
// export function MostViewedScripts({ items }: { items: Category[] }) {
|
||||
// const [page, setPage] = useState(1);
|
||||
|
||||
const goToNextPage = () => {
|
||||
setPage((prevPage) => prevPage + 1);
|
||||
};
|
||||
// const mostViewedScripts = useMemo(() => {
|
||||
// if (!items) return [];
|
||||
// const scripts = items.flatMap((category) => category.scripts || []);
|
||||
// const mostViewedScripts = scripts
|
||||
// .filter((script) => script.isMostViewed)
|
||||
// .map((script) => ({
|
||||
// ...script,
|
||||
// }));
|
||||
// return mostViewedScripts;
|
||||
// }, [items]);
|
||||
|
||||
const goToPreviousPage = () => {
|
||||
setPage((prevPage) => prevPage - 1);
|
||||
};
|
||||
// const goToNextPage = () => {
|
||||
// setPage((prevPage) => prevPage + 1);
|
||||
// };
|
||||
|
||||
const startIndex = (page - 1) * ITEMS_PER_PAGE;
|
||||
const endIndex = page * ITEMS_PER_PAGE;
|
||||
// const goToPreviousPage = () => {
|
||||
// setPage((prevPage) => prevPage - 1);
|
||||
// };
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
{mostViewedScripts.length > 0 && (
|
||||
<>
|
||||
<h2 className="text-lg font-semibold">Most Viewed Scripts</h2>
|
||||
</>
|
||||
)}
|
||||
<div className="min-w flex w-full flex-row flex-wrap gap-4">
|
||||
{mostViewedScripts.slice(startIndex, endIndex).map((item) => (
|
||||
<Card
|
||||
key={item.id}
|
||||
className="min-w-[250px] flex-1 flex-grow bg-accent/30"
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-3">
|
||||
<div className="flex max-h-16 min-h-16 min-w-16 max-w-16 items-center justify-center rounded-lg bg-accent p-1">
|
||||
<Image
|
||||
unoptimized
|
||||
src={item.logo}
|
||||
height={64}
|
||||
width={64}
|
||||
alt=""
|
||||
className="h-11 w-11 object-contain"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<p className="line-clamp-1 text-lg">
|
||||
{item.title} {item.item_type}
|
||||
</p>
|
||||
<p className="flex items-center gap-1 text-sm text-muted-foreground">
|
||||
<CalendarPlus className="h-4 w-4" />
|
||||
{extractDate(item.created)}
|
||||
</p>
|
||||
</div>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardDescription className="line-clamp-3 text-card-foreground break-words">
|
||||
{item.description}
|
||||
</CardDescription>
|
||||
</CardContent>
|
||||
<CardFooter className="">
|
||||
<Button asChild variant="outline">
|
||||
<Link
|
||||
href={{
|
||||
pathname: "/scripts",
|
||||
query: { id: item.title },
|
||||
}}
|
||||
prefetch={false}
|
||||
>
|
||||
View Script
|
||||
</Link>
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-1 p-2">
|
||||
{page > 1 && (
|
||||
<Button onClick={goToPreviousPage} variant="outline">
|
||||
Previous
|
||||
</Button>
|
||||
)}
|
||||
{endIndex < mostViewedScripts.length && (
|
||||
<Button onClick={goToNextPage} variant="outline">
|
||||
{page === 1 ? "More.." : "Next"}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// const startIndex = (page - 1) * ITEMS_PER_PAGE;
|
||||
// const endIndex = page * ITEMS_PER_PAGE;
|
||||
|
||||
// return (
|
||||
// <div className="">
|
||||
// {mostViewedScripts.length > 0 && (
|
||||
// <>
|
||||
// <h2 className="text-lg font-semibold">Most Viewed Scripts</h2>
|
||||
// </>
|
||||
// )}
|
||||
// <div className="min-w flex w-full flex-row flex-wrap gap-4">
|
||||
// {mostViewedScripts.slice(startIndex, endIndex).map((item) => (
|
||||
// <Card
|
||||
// key={item.id}
|
||||
// className="min-w-[250px] flex-1 flex-grow bg-accent/30"
|
||||
// >
|
||||
// <CardHeader>
|
||||
// <CardTitle className="flex items-center gap-3">
|
||||
// <div className="flex max-h-16 min-h-16 min-w-16 max-w-16 items-center justify-center rounded-lg bg-accent p-1">
|
||||
// <Image
|
||||
// unoptimized
|
||||
// src={item.logo}
|
||||
// height={64}
|
||||
// width={64}
|
||||
// alt=""
|
||||
// className="h-11 w-11 object-contain"
|
||||
// />
|
||||
// </div>
|
||||
// <div className="flex flex-col">
|
||||
// <p className="line-clamp-1 text-lg">
|
||||
// {item.title} {item.item_type}
|
||||
// </p>
|
||||
// <p className="flex items-center gap-1 text-sm text-muted-foreground">
|
||||
// <CalendarPlus className="h-4 w-4" />
|
||||
// {extractDate(item.created)}
|
||||
// </p>
|
||||
// </div>
|
||||
// </CardTitle>
|
||||
// </CardHeader>
|
||||
// <CardContent>
|
||||
// <CardDescription className="line-clamp-3 text-card-foreground break-words">
|
||||
// {item.description}
|
||||
// </CardDescription>
|
||||
// </CardContent>
|
||||
// <CardFooter className="">
|
||||
// <Button asChild variant="outline">
|
||||
// <Link
|
||||
// href={{
|
||||
// pathname: "/scripts",
|
||||
// query: { id: item.title },
|
||||
// }}
|
||||
// prefetch={false}
|
||||
// >
|
||||
// View Script
|
||||
// </Link>
|
||||
// </Button>
|
||||
// </CardFooter>
|
||||
// </Card>
|
||||
// ))}
|
||||
// </div>
|
||||
// <div className="flex justify-end gap-1 p-2">
|
||||
// {page > 1 && (
|
||||
// <Button onClick={goToPreviousPage} variant="outline">
|
||||
// Previous
|
||||
// </Button>
|
||||
// )}
|
||||
// {endIndex < mostViewedScripts.length && (
|
||||
// <Button onClick={goToNextPage} variant="outline">
|
||||
// {page === 1 ? "More.." : "Next"}
|
||||
// </Button>
|
||||
// )}
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
@ -39,21 +39,21 @@ function ScriptItem({
|
||||
<div className="flex">
|
||||
<Image
|
||||
className="h-32 w-32 rounded-lg bg-accent/60 object-contain p-3 shadow-md"
|
||||
src={item.logo}
|
||||
src={item.logo || "/logo.png"}
|
||||
width={400}
|
||||
onError={(e) =>
|
||||
((e.currentTarget as HTMLImageElement).src = "/logo.png")
|
||||
}
|
||||
height={400}
|
||||
alt={item.title}
|
||||
alt={item.name}
|
||||
unoptimized
|
||||
/>
|
||||
<div className="ml-4 flex flex-col justify-between">
|
||||
<div className="flex h-full w-full flex-col justify-between">
|
||||
<div>
|
||||
<h1 className="text-lg font-semibold">{item.title}</h1>
|
||||
<h1 className="text-lg font-semibold">{item.name}</h1>
|
||||
<p className="w-full text-sm text-muted-foreground">
|
||||
Date added: {extractDate(item.created)}
|
||||
Date added: {extractDate(item.date_created)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-5">
|
||||
@ -76,7 +76,7 @@ function ScriptItem({
|
||||
<div className="mt-4 rounded-lg border bg-accent/50">
|
||||
<div className="flex gap-3 px-4 py-2">
|
||||
<h2 className="text-lg font-semibold">
|
||||
How to {item.item_type ? "install" : "use"}
|
||||
How to {item.type ? "install" : "use"}
|
||||
</h2>
|
||||
<Tooltips item={item} />
|
||||
</div>
|
||||
|
@ -5,12 +5,12 @@ import { Info } from "lucide-react";
|
||||
export default function Alerts({ item }: { item: Script }) {
|
||||
return (
|
||||
<>
|
||||
{item.expand?.alerts?.length > 0 &&
|
||||
item.expand.alerts.map((alert: any, index: number) => (
|
||||
{item?.notes?.length > 0 &&
|
||||
item.notes.map((note: any, index: number) => (
|
||||
<div key={index} className="mt-4 flex flex-col gap-2">
|
||||
<p className="inline-flex items-center gap-2 rounded-lg border border-red-500/25 bg-destructive/25 p-2 pl-4 text-sm">
|
||||
<Info className="h-4 min-h-4 w-4 min-w-4" />
|
||||
<span>{TextCopyBlock(alert.content)}</span>
|
||||
<span>{TextCopyBlock(note.text)}</span>
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
|
@ -21,12 +21,12 @@ export default function Buttons({ item }: { item: Script }) {
|
||||
};
|
||||
|
||||
const sourceUrl = useMemo(() => {
|
||||
if (item.installCommand) {
|
||||
const match = item.installCommand.match(pattern);
|
||||
if (item.install_methods[0]?.script) {
|
||||
const match = item.install_methods[0].script.match(pattern);
|
||||
return match ? transformUrlToInstallScript(match[0]) : null;
|
||||
}
|
||||
return null;
|
||||
}, [item.installCommand, pattern]);
|
||||
}, [item.install_methods, pattern]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap justify-end gap-2">
|
||||
@ -49,17 +49,7 @@ export default function Buttons({ item }: { item: Script }) {
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
{item.post_install && (
|
||||
<Button variant="secondary" asChild>
|
||||
<Link target="_blank" href={item.post_install}>
|
||||
<span className="flex items-center gap-2">
|
||||
<ExternalLink className="h-4 w-4" />
|
||||
Post Install
|
||||
</span>
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
{item.installCommand && sourceUrl && (
|
||||
{item.install_methods[0]?.script && sourceUrl && (
|
||||
<Button variant="secondary" asChild>
|
||||
<Link target="_blank" href={transformUrlToInstallScript(sourceUrl)}>
|
||||
<span className="flex items-center gap-2">
|
||||
|
@ -4,7 +4,7 @@ import handleCopy from "@/components/handleCopy";
|
||||
import { Script } from "@/lib/types";
|
||||
|
||||
export default function DefaultPassword({ item }: { item: Script }) {
|
||||
const hasDefaultLogin = item?.expand?.default_login !== undefined;
|
||||
const hasDefaultLogin = item.default_credentials.username != null && item.default_credentials.password != null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -17,7 +17,7 @@ export default function DefaultPassword({ item }: { item: Script }) {
|
||||
<div className="flex flex-col gap-2 p-4">
|
||||
<p className="mb-2 text-sm">
|
||||
You can use the following credentials to login to the {""}
|
||||
{item.title} {item.item_type}.
|
||||
{item.name} {item.type}.
|
||||
</p>
|
||||
<div className="text-sm">
|
||||
Username:{" "}
|
||||
@ -25,10 +25,10 @@ export default function DefaultPassword({ item }: { item: Script }) {
|
||||
variant={"secondary"}
|
||||
size={"null"}
|
||||
onClick={() =>
|
||||
handleCopy("username", item.expand.default_login.username)
|
||||
handleCopy("username", item.default_credentials.username ?? "")
|
||||
}
|
||||
>
|
||||
{item.expand.default_login.username}
|
||||
{item.default_credentials.username}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="text-sm">
|
||||
@ -37,10 +37,10 @@ export default function DefaultPassword({ item }: { item: Script }) {
|
||||
variant={"secondary"}
|
||||
size={"null"}
|
||||
onClick={() =>
|
||||
handleCopy("password", item.expand.default_login.password)
|
||||
handleCopy("password", item.default_credentials.password ?? "")
|
||||
}
|
||||
>
|
||||
{item.expand.default_login.password}
|
||||
{item.default_credentials.password}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,35 +1,41 @@
|
||||
import { Script } from "@/lib/types";
|
||||
|
||||
export default function DefaultSettings({ item }: { item: Script }) {
|
||||
const hasAlpineScript = item?.expand?.alpine_script !== undefined;
|
||||
const defaultSettings = item.install_methods.find(
|
||||
(method) => method.type === "default",
|
||||
);
|
||||
|
||||
const defaultAlpineSettings = item.install_methods.find(
|
||||
(method) => method.type === "alpine",
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{item.default_cpu && (
|
||||
{defaultSettings && (
|
||||
<div>
|
||||
<h2 className="text-md font-semibold">Default settings</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
CPU: {item.default_cpu}
|
||||
CPU: {defaultSettings.resources.cpu}vCPU
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
RAM: {item.default_ram}
|
||||
RAM: {defaultSettings.resources.ram}MB
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
HDD: {item.default_hdd}
|
||||
HDD: {defaultSettings.resources.hdd}GB
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{hasAlpineScript && (
|
||||
{defaultAlpineSettings && (
|
||||
<div>
|
||||
<h2 className="text-md font-semibold">Default Alpine settings</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
CPU: {item.expand.alpine_script.default_cpu}
|
||||
CPU: {defaultAlpineSettings?.resources.cpu}vCPU
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
RAM: {item.expand.alpine_script.default_ram}
|
||||
RAM: {defaultAlpineSettings?.resources.ram}MB
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
HDD: {item.expand.alpine_script.default_hdd}
|
||||
HDD: {defaultAlpineSettings?.resources.hdd}GB
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
@ -2,32 +2,41 @@ import CodeCopyButton from "@/components/ui/code-copy-button";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Script } from "@/lib/types";
|
||||
|
||||
const generateInstallCommand = (script: string) => {
|
||||
return `bash -c "$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/${script})"`;
|
||||
}
|
||||
|
||||
export default function InstallCommand({ item }: { item: Script }) {
|
||||
const { title, item_type, installCommand, expand } = item;
|
||||
const hasAlpineScript = expand?.alpine_script !== undefined;
|
||||
const alpineScript = item.install_methods.find(
|
||||
(method) => method.type === "alpine",
|
||||
);
|
||||
|
||||
const defaultScript = item.install_methods.find(
|
||||
(method) => method.type === "default"
|
||||
);
|
||||
|
||||
const renderInstructions = (isAlpine = false) => (
|
||||
<>
|
||||
<p className="text-sm mt-2">
|
||||
{isAlpine ? (
|
||||
<>
|
||||
As an alternative option, you can use Alpine Linux and the {title}{" "}
|
||||
package to create a {title} {item_type} container with faster
|
||||
As an alternative option, you can use Alpine Linux and the {item.name}{" "}
|
||||
package to create a {item.name} {item.type} container with faster
|
||||
creation time and minimal system resource usage. You are also
|
||||
obliged to adhere to updates provided by the package maintainer.
|
||||
</>
|
||||
) : item_type ? (
|
||||
) : item.type ? (
|
||||
<>
|
||||
To create a new Proxmox VE {title} {item_type}, run the command
|
||||
To create a new Proxmox VE {item.name} {item.type}, run the command
|
||||
below in the Proxmox VE Shell.
|
||||
</>
|
||||
) : (
|
||||
<>To use the {title} script, run the command below in the shell.</>
|
||||
<>To use the {item.name} script, run the command below in the shell.</>
|
||||
)}
|
||||
</p>
|
||||
{isAlpine && (
|
||||
<p className="mt-2 text-sm">
|
||||
To create a new Proxmox VE Alpine-{title} {item_type}, run the command
|
||||
To create a new Proxmox VE Alpine-{item.name} {item.type}, run the command
|
||||
below in the Proxmox VE Shell
|
||||
</p>
|
||||
)}
|
||||
@ -36,7 +45,7 @@ export default function InstallCommand({ item }: { item: Script }) {
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
{hasAlpineScript ? (
|
||||
{alpineScript ? (
|
||||
<Tabs defaultValue="default" className="mt-2 w-full max-w-4xl">
|
||||
<TabsList>
|
||||
<TabsTrigger value="default">Default</TabsTrigger>
|
||||
@ -44,25 +53,23 @@ export default function InstallCommand({ item }: { item: Script }) {
|
||||
</TabsList>
|
||||
<TabsContent value="default">
|
||||
{renderInstructions()}
|
||||
<CodeCopyButton>{installCommand}</CodeCopyButton>
|
||||
<CodeCopyButton>{defaultScript?.script}</CodeCopyButton>
|
||||
</TabsContent>
|
||||
<TabsContent value="alpine">
|
||||
{expand.alpine_script && (
|
||||
<>
|
||||
{renderInstructions(true)}
|
||||
<CodeCopyButton>
|
||||
{expand.alpine_script.installCommand}
|
||||
</CodeCopyButton>
|
||||
</>
|
||||
)}
|
||||
{renderInstructions(true)}
|
||||
<CodeCopyButton>
|
||||
{generateInstallCommand(alpineScript.script)}
|
||||
</CodeCopyButton>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
) : (
|
||||
) : defaultScript?.script ? (
|
||||
<>
|
||||
{renderInstructions()}
|
||||
{installCommand && <CodeCopyButton>{installCommand}</CodeCopyButton>}
|
||||
<CodeCopyButton>
|
||||
{generateInstallCommand(defaultScript.script)}
|
||||
</CodeCopyButton>
|
||||
</>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -37,11 +37,11 @@ export default function Tooltips({ item }: { item: Script }) {
|
||||
content="This script will be run in a privileged LXC"
|
||||
/>
|
||||
)}
|
||||
{item.isUpdateable && (
|
||||
{item.updateable && (
|
||||
<TooltipBadge
|
||||
variant="success"
|
||||
label="Updateable"
|
||||
content={`To Update ${item.title}, run the command below (or type update) in the LXC Console.`}
|
||||
content={`To Update ${item.name}, run the command below (or type update) in the LXC Console.`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -18,7 +18,7 @@ const Sidebar = ({
|
||||
<h1 className="text-xl font-bold">Categories</h1>
|
||||
<p className="text-xs italic text-muted-foreground">
|
||||
{items.reduce(
|
||||
(acc, category) => acc + category.expand.items.length,
|
||||
(acc, category) => acc + category.scripts.length,
|
||||
0,
|
||||
)}{" "}
|
||||
Total scripts
|
||||
|
@ -10,7 +10,7 @@ import Sidebar from "./_components/Sidebar";
|
||||
import { useQueryState } from "nuqs";
|
||||
import {
|
||||
LatestScripts,
|
||||
MostViewedScripts,
|
||||
// MostViewedScripts,
|
||||
} from "./_components/ScriptInfoBlocks";
|
||||
|
||||
function ScriptContent() {
|
||||
@ -21,39 +21,20 @@ function ScriptContent() {
|
||||
useEffect(() => {
|
||||
if (selectedScript && links.length > 0) {
|
||||
const script = links
|
||||
.map((category) => category.expand.items)
|
||||
.map((category) => category.scripts)
|
||||
.flat()
|
||||
.find((script) => script.title === selectedScript);
|
||||
.find((script) => script.name === selectedScript);
|
||||
setItem(script);
|
||||
}
|
||||
}, [selectedScript, links]);
|
||||
|
||||
const sortCategories = (categories: Category[]): Category[] => {
|
||||
return categories.sort((a: Category, b: Category) => {
|
||||
if (
|
||||
a.catagoryName === "Proxmox VE Tools" &&
|
||||
b.catagoryName !== "Proxmox VE Tools"
|
||||
) {
|
||||
return -1;
|
||||
} else if (
|
||||
a.catagoryName !== "Proxmox VE Tools" &&
|
||||
b.catagoryName === "Proxmox VE Tools"
|
||||
) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.catagoryName.localeCompare(b.catagoryName);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetch(
|
||||
`api/categories?_=${process.env.NEXT_PUBLIC_BUILD_TIME || Date.now()}`,
|
||||
`api/categories`,
|
||||
)
|
||||
.then((response) => response.json())
|
||||
.then((categories) => {
|
||||
const sortedCategories = sortCategories(categories);
|
||||
setLinks(sortedCategories);
|
||||
setLinks(categories);
|
||||
})
|
||||
.catch((error) => console.error(error));
|
||||
}, []);
|
||||
@ -74,7 +55,7 @@ function ScriptContent() {
|
||||
) : (
|
||||
<div className="flex w-full flex-col gap-5">
|
||||
<LatestScripts items={links} />
|
||||
<MostViewedScripts items={links} />
|
||||
{/* <MostViewedScripts items={links} /> */}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user