merge frontend website into scripts repo
This commit is contained in:
19
frontend/src/app/scripts/_components/ScriptItems/Alerts.tsx
Normal file
19
frontend/src/app/scripts/_components/ScriptItems/Alerts.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import TextCopyBlock from "@/lib/TextCopyBlock";
|
||||
import { Script } from "@/lib/types";
|
||||
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) => (
|
||||
<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>
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
74
frontend/src/app/scripts/_components/ScriptItems/Buttons.tsx
Normal file
74
frontend/src/app/scripts/_components/ScriptItems/Buttons.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Script } from "@/lib/types";
|
||||
import { BookOpenText, Code, ExternalLink, Globe } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export default function Buttons({ item }: { item: Script }) {
|
||||
const pattern = useMemo(
|
||||
() =>
|
||||
/(https:\/\/github\.com\/community-scripts\/ProxmoxVE\/raw\/main\/(ct|misc|vm)\/([^\/]+)\.sh)/,
|
||||
[],
|
||||
);
|
||||
|
||||
const transformUrlToInstallScript = (url: string): string => {
|
||||
if (url.includes("/pve/")) {
|
||||
return url;
|
||||
} else if (url.includes("/ct/")) {
|
||||
return url.replace("/ct/", "/install/").replace(/\.sh$/, "-install.sh");
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
const sourceUrl = useMemo(() => {
|
||||
if (item.installCommand) {
|
||||
const match = item.installCommand.match(pattern);
|
||||
return match ? transformUrlToInstallScript(match[0]) : null;
|
||||
}
|
||||
return null;
|
||||
}, [item.installCommand, pattern]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap justify-end gap-2">
|
||||
{item.website && (
|
||||
<Button variant="secondary" asChild>
|
||||
<Link target="_blank" href={item.website}>
|
||||
<span className="flex items-center gap-2">
|
||||
<Globe className="h-4 w-4" /> Website
|
||||
</span>
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
{item.documentation && (
|
||||
<Button variant="secondary" asChild>
|
||||
<Link target="_blank" href={item.documentation}>
|
||||
<span className="flex items-center gap-2">
|
||||
<BookOpenText className="h-4 w-4" />
|
||||
Documentation
|
||||
</span>
|
||||
</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 && (
|
||||
<Button variant="secondary" asChild>
|
||||
<Link target="_blank" href={transformUrlToInstallScript(sourceUrl)}>
|
||||
<span className="flex items-center gap-2">
|
||||
<Code className="h-4 w-4" />
|
||||
Source Code
|
||||
</span>
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import handleCopy from "@/components/handleCopy";
|
||||
import { Script } from "@/lib/types";
|
||||
|
||||
export default function DefaultPassword({ item }: { item: Script }) {
|
||||
const hasDefaultLogin = item?.expand?.default_login !== undefined;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{hasDefaultLogin && (
|
||||
<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">Default Login Credentials</h2>
|
||||
</div>
|
||||
<Separator className="w-full"></Separator>
|
||||
<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}.
|
||||
</p>
|
||||
<div className="text-sm">
|
||||
Username:{" "}
|
||||
<Button
|
||||
variant={"secondary"}
|
||||
size={"null"}
|
||||
onClick={() =>
|
||||
handleCopy("username", item.expand.default_login.username)
|
||||
}
|
||||
>
|
||||
{item.expand.default_login.username}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="text-sm">
|
||||
Password:{" "}
|
||||
<Button
|
||||
variant={"secondary"}
|
||||
size={"null"}
|
||||
onClick={() =>
|
||||
handleCopy("password", item.expand.default_login.password)
|
||||
}
|
||||
>
|
||||
{item.expand.default_login.password}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Script } from "@/lib/types";
|
||||
|
||||
export default function DefaultSettings({ item }: { item: Script }) {
|
||||
const hasAlpineScript = item?.expand?.alpine_script !== undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
{item.default_cpu && (
|
||||
<div>
|
||||
<h2 className="text-md font-semibold">Default settings</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
CPU: {item.default_cpu}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
RAM: {item.default_ram}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
HDD: {item.default_hdd}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{hasAlpineScript && (
|
||||
<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}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
RAM: {item.expand.alpine_script.default_ram}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
HDD: {item.expand.alpine_script.default_hdd}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import TextCopyBlock from "@/lib/TextCopyBlock";
|
||||
import { Script } from "@/lib/types";
|
||||
|
||||
export default function Description({ item }: { item: Script }) {
|
||||
return (
|
||||
<div className="p-2">
|
||||
<h2 className="mb-2 max-w-prose text-lg font-semibold">Description</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{TextCopyBlock(item.description)}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import CodeCopyButton from "@/components/ui/code-copy-button";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Script } from "@/lib/types";
|
||||
|
||||
export default function InstallCommand({ item }: { item: Script }) {
|
||||
const { title, item_type, installCommand, expand } = item;
|
||||
const hasAlpineScript = expand?.alpine_script !== undefined;
|
||||
|
||||
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
|
||||
creation time and minimal system resource usage. You are also
|
||||
obliged to adhere to updates provided by the package maintainer.
|
||||
</>
|
||||
) : item_type ? (
|
||||
<>
|
||||
To create a new Proxmox VE {title} {item_type}, run the command
|
||||
below in the Proxmox VE Shell.
|
||||
</>
|
||||
) : (
|
||||
<>To use the {title} 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
|
||||
below in the Proxmox VE Shell
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
{hasAlpineScript ? (
|
||||
<Tabs defaultValue="default" className="mt-2 w-full max-w-4xl">
|
||||
<TabsList>
|
||||
<TabsTrigger value="default">Default</TabsTrigger>
|
||||
<TabsTrigger value="alpine">Alpine Linux</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="default">
|
||||
{renderInstructions()}
|
||||
<CodeCopyButton>{installCommand}</CodeCopyButton>
|
||||
</TabsContent>
|
||||
<TabsContent value="alpine">
|
||||
{expand.alpine_script && (
|
||||
<>
|
||||
{renderInstructions(true)}
|
||||
<CodeCopyButton>
|
||||
{expand.alpine_script.installCommand}
|
||||
</CodeCopyButton>
|
||||
</>
|
||||
)}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
) : (
|
||||
<>
|
||||
{renderInstructions()}
|
||||
{installCommand && <CodeCopyButton>{installCommand}</CodeCopyButton>}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
import handleCopy from "@/components/handleCopy";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ClipboardIcon } from "lucide-react";
|
||||
|
||||
interface Item {
|
||||
interface?: string;
|
||||
port?: number;
|
||||
}
|
||||
|
||||
const CopyButton = ({
|
||||
label,
|
||||
value,
|
||||
}: {
|
||||
label: string;
|
||||
value: string | number;
|
||||
}) => (
|
||||
<span className={cn(buttonVariants({size: "sm", variant: "secondary"}), "flex items-center gap-2")}>
|
||||
{value}
|
||||
<ClipboardIcon
|
||||
onClick={() => handleCopy(label, String(value))}
|
||||
className="size-4 cursor-pointer"
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
|
||||
export default function InterFaces({ item }: { item: Item }) {
|
||||
const { interface: iface, port } = item;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
{iface || (port && port !== 0) ? (
|
||||
<div className="flex items-center justify-end">
|
||||
<h2 className="mr-2 text-end text-lg font-semibold">
|
||||
{iface ? "Interface:" : "Default Port:"}
|
||||
</h2>{" "}
|
||||
<CopyButton
|
||||
label={iface ? "interface" : "port"}
|
||||
value={iface || port!}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { Script } from "@/lib/types";
|
||||
import React from "react";
|
||||
|
||||
interface TooltipProps {
|
||||
variant: "warning" | "success";
|
||||
label: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
const TooltipBadge: React.FC<TooltipProps> = ({ variant, label, content }) => (
|
||||
<TooltipProvider>
|
||||
<Tooltip delayDuration={100}>
|
||||
<TooltipTrigger className="flex items-center">
|
||||
<Badge variant={variant}>{label}</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="text-sm">
|
||||
{content}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
|
||||
export default function Tooltips({ item }: { item: Script }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
{item.privileged && (
|
||||
<TooltipBadge
|
||||
variant="warning"
|
||||
label="Privileged"
|
||||
content="This script will be run in a privileged LXC"
|
||||
/>
|
||||
)}
|
||||
{item.isUpdateable && (
|
||||
<TooltipBadge
|
||||
variant="success"
|
||||
label="Updateable"
|
||||
content={`To Update ${item.title}, run the command below (or type update) in the LXC Console.`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user