Compare commits

...
7 changed files with 219 additions and 110 deletions

No files matched your search

+16 -35
View File
@@ -1,5 +1,3 @@
"use client";
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import { cn } from "@utils/helpers";
import * as React from "react";
@@ -15,46 +13,31 @@ const ScrollArea = React.forwardRef<
>(({ className, children, withoutViewport = false, ...props }, ref) => (
<ScrollAreaPrimitive.Root
ref={ref}
className={cn(
"relative will-change-scroll webkit-scroll",
className,
"overflow-hidden",
)}
className={cn("relative overflow-hidden", className)}
{...props}
>
{withoutViewport ? (
children
) : (
<ScrollAreaViewport disableOverflowY={false}>
{children}
</ScrollAreaViewport>
<ScrollAreaViewport>{children}</ScrollAreaViewport>
)}
<ScrollBar />
<ScrollBar orientation="horizontal" />
<ScrollBar orientation="vertical" />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
));
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
type AdditionalScrollAreaViewportProps = {
disableOverflowY?: boolean;
};
const ScrollAreaViewport = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Viewport>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Viewport> &
AdditionalScrollAreaViewportProps
>(({ disableOverflowY = true, ...props }, ref) => {
return (
<ScrollAreaPrimitive.Viewport
ref={ref}
className="h-full w-full rounded-[inherit] will-change-scroll webkit-scroll"
{...props}
style={
disableOverflowY ? { overflowY: undefined, ...props.style } : undefined
}
/>
);
});
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Viewport>
>(({ className, ...props }, ref) => (
<ScrollAreaPrimitive.Viewport
ref={ref}
className={cn("h-full w-full rounded-[inherit]", className)}
{...props}
/>
));
ScrollAreaViewport.displayName = ScrollAreaPrimitive.Viewport.displayName;
const ScrollBar = React.forwardRef<
@@ -63,14 +46,11 @@ const ScrollBar = React.forwardRef<
>(({ className, orientation = "vertical", ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
style={{ boxSizing: "unset", overflow: undefined }}
orientation={orientation}
className={cn(
"flex touch-none select-none transition-colors",
orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent p-[1px]",
orientation === "horizontal" &&
"h-2.5 border-t border-t-transparent p-[1px]",
"flex select-none touch-none transition-colors",
orientation === "vertical" && "h-full w-2.5 p-[1px]",
orientation === "horizontal" && "w-full h-2.5 p-[1px] bottom-0",
className,
)}
{...props}
@@ -79,6 +59,7 @@ const ScrollBar = React.forwardRef<
className={cn(
"relative rounded-full bg-neutral-200 dark:bg-nb-gray-800",
orientation === "vertical" && "flex-1",
orientation === "horizontal" && "h-full",
)}
/>
</ScrollAreaPrimitive.ScrollAreaScrollbar>
+27 -7
View File
@@ -23,7 +23,7 @@ import { cn } from "@utils/helpers";
import { trim } from "lodash";
import {
AlarmClock,
CopyIcon,
DownloadIcon,
ExternalLinkIcon,
MonitorSmartphoneIcon,
PlusCircle,
@@ -35,20 +35,24 @@ import SetupKeysIcon from "@/assets/icons/SetupKeysIcon";
import useCopyToClipboard from "@/hooks/useCopyToClipboard";
import { SetupKey } from "@/interfaces/SetupKey";
import useGroupHelper from "@/modules/groups/useGroupHelper";
import SetupModal from "@/modules/setup-netbird-modal/SetupModal";
type Props = {
children?: React.ReactNode;
open: boolean;
setOpen: (open: boolean) => void;
name?: string;
};
const copyMessage = "Setup-Key was copied to your clipboard!";
export default function SetupKeyModal({
children,
open,
setOpen,
name,
}: Readonly<Props>) {
const [successModal, setSuccessModal] = useState(false);
const [setupKey, setSetupKey] = useState<SetupKey>();
const [installModal, setInstallModal] = useState(false);
const [, copy] = useCopyToClipboard(setupKey?.key);
const handleSuccess = (setupKey: SetupKey) => {
@@ -60,8 +64,20 @@ export default function SetupKeyModal({
<>
<Modal open={open} onOpenChange={setOpen} key={open ? 1 : 0}>
{children && <ModalTrigger asChild>{children}</ModalTrigger>}
<SetupKeyModalContent onSuccess={handleSuccess} />
<SetupKeyModalContent onSuccess={handleSuccess} predefinedName={name} />
</Modal>
<Modal
open={installModal}
onOpenChange={(state) => {
setInstallModal(state);
setOpen(false);
}}
key={installModal ? 2 : 3}
>
<SetupModal showClose={true} setupKey={setupKey?.key} />
</Modal>
<Modal
open={successModal}
onOpenChange={(open) => {
@@ -118,10 +134,10 @@ export default function SetupKeyModal({
variant={"primary"}
className={"w-full"}
data-cy={"setup-key-copy"}
onClick={() => copy(copyMessage)}
onClick={() => setInstallModal(true)}
>
<CopyIcon size={14} />
Copy to clipboard
<DownloadIcon size={14} />
Install NetBird
</Button>
</div>
</ModalFooter>
@@ -133,13 +149,17 @@ export default function SetupKeyModal({
type ModalProps = {
onSuccess?: (setupKey: SetupKey) => void;
predefinedName?: string;
};
export function SetupKeyModalContent({ onSuccess }: Readonly<ModalProps>) {
export function SetupKeyModalContent({
onSuccess,
predefinedName = "",
}: Readonly<ModalProps>) {
const setupKeyRequest = useApiCall<SetupKey>("/setup-keys", true);
const { mutate } = useSWRConfig();
const [name, setName] = useState("");
const [name, setName] = useState(predefinedName);
const [reusable, setReusable] = useState(false);
const [usageLimit, setUsageLimit] = useState("");
const [expiresIn, setExpiresIn] = useState("7");
@@ -10,7 +10,11 @@ import Link from "next/link";
import React from "react";
import { OperatingSystem } from "@/interfaces/OperatingSystem";
export default function DockerTab() {
type Props = {
setupKey?: string;
};
export default function DockerTab({ setupKey }: Readonly<Props>) {
return (
<TabsContent value={String(OperatingSystem.DOCKER)}>
<TabsContentPadding>
@@ -42,7 +46,10 @@ export default function DockerTab() {
<Code.Line>
{" "}
-e NB_SETUP_KEY=
<span className={"text-netbird"}>SETUP_KEY</span> \
<span className={"text-netbird"}>
{setupKey ?? "SETUP_KEY"}
</span>{" "}
\
</Code.Line>
<Code.Line> -v netbird-client:/etc/netbird \</Code.Line>
{GRPC_API_ORIGIN && (
+16 -5
View File
@@ -13,8 +13,13 @@ import { getNetBirdUpCommand } from "@utils/netbird";
import { TerminalSquareIcon } from "lucide-react";
import React from "react";
import { OperatingSystem } from "@/interfaces/OperatingSystem";
import { SetupKeyParameter } from "@/modules/setup-netbird-modal/SetupModal";
export default function LinuxTab() {
type Props = {
setupKey?: string;
};
export default function LinuxTab({ setupKey }: Readonly<Props>) {
return (
<TabsContent value={String(OperatingSystem.LINUX)}>
<TabsContentPadding>
@@ -27,9 +32,12 @@ export default function LinuxTab() {
<Code>curl -fsSL https://pkgs.netbird.io/install.sh | sh</Code>
</Steps.Step>
<Steps.Step step={2} line={false}>
<p>Run NetBird and log in the browser</p>
<p>Run NetBird {!setupKey && "and log in the browser"}</p>
<Code>
<Code.Line>{getNetBirdUpCommand()}</Code.Line>
<Code.Line>
{getNetBirdUpCommand()}
<SetupKeyParameter setupKey={setupKey} />
</Code.Line>
</Code>
</Steps.Step>
</Steps>
@@ -78,9 +86,12 @@ export default function LinuxTab() {
</Code>
</Steps.Step>
<Steps.Step step={3} line={false}>
<p>Run NetBird and log in the browser</p>
<p>Run NetBird {!setupKey && "and log in the browser"}</p>
<Code>
<Code.Line>{getNetBirdUpCommand()}</Code.Line>
<Code.Line>
{getNetBirdUpCommand()}
<SetupKeyParameter setupKey={setupKey} />
</Code.Line>
</Code>
</Steps.Step>
</Steps>
+38 -14
View File
@@ -23,8 +23,12 @@ import {
import Link from "next/link";
import React from "react";
import { OperatingSystem } from "@/interfaces/OperatingSystem";
import { SetupKeyParameter } from "@/modules/setup-netbird-modal/SetupModal";
export default function MacOSTab() {
type Props = {
setupKey?: string;
};
export default function MacOSTab({ setupKey }: Readonly<Props>) {
return (
<TabsContent value={String(OperatingSystem.APPLE)}>
<TabsContentPadding>
@@ -98,15 +102,29 @@ export default function MacOSTab() {
</Steps.Step>
)}
<Steps.Step step={GRPC_API_ORIGIN ? 3 : 2}>
<p>
{/* eslint-disable-next-line react/no-unescaped-entities */}
Click on "Connect" from the NetBird icon in your system tray
</p>
</Steps.Step>
<Steps.Step step={GRPC_API_ORIGIN ? 4 : 3} line={false}>
<p>Sign up using your email address</p>
</Steps.Step>
{setupKey ? (
<Steps.Step step={GRPC_API_ORIGIN ? 3 : 2} line={false}>
<p>Open Terminal and run NetBird</p>
<Code>
<Code.Line>
{getNetBirdUpCommand()}
<SetupKeyParameter setupKey={setupKey} />
</Code.Line>
</Code>
</Steps.Step>
) : (
<>
<Steps.Step step={GRPC_API_ORIGIN ? 3 : 2}>
<p>
{/* eslint-disable-next-line react/no-unescaped-entities */}
Click on "Connect" from the NetBird icon in your system tray
</p>
</Steps.Step>
<Steps.Step step={GRPC_API_ORIGIN ? 4 : 3} line={false}>
<p>Sign up using your email address</p>
</Steps.Step>
</>
)}
</Steps>
</TabsContentPadding>
<Separator />
@@ -125,9 +143,12 @@ export default function MacOSTab() {
</Code>
</Steps.Step>
<Steps.Step step={2} line={false}>
<p>Run NetBird and log in the browser</p>
<p>Run NetBird {!setupKey && "and log in the browser"}</p>
<Code>
<Code.Line>{getNetBirdUpCommand()}</Code.Line>
<Code.Line>
{getNetBirdUpCommand()}
<SetupKeyParameter setupKey={setupKey} />
</Code.Line>
</Code>
</Steps.Step>
</Steps>
@@ -179,9 +200,12 @@ export default function MacOSTab() {
</Code>
</Steps.Step>
<Steps.Step step={4} line={false}>
<p>Run NetBird and log in the browser</p>
<p>Run NetBird {!setupKey && "and log in the browser"}</p>
<Code>
<Code.Line>{getNetBirdUpCommand()}</Code.Line>
<Code.Line>
{getNetBirdUpCommand()}
<SetupKeyParameter setupKey={setupKey} />
</Code.Line>
</Code>
</Steps.Step>
</Steps>
+83 -36
View File
@@ -5,6 +5,7 @@ import { ModalContent, ModalFooter } from "@components/modal/Modal";
import Paragraph from "@components/Paragraph";
import SmallParagraph from "@components/SmallParagraph";
import { Tabs, TabsList, TabsTrigger } from "@components/Tabs";
import { cn } from "@utils/helpers";
import { ExternalLinkIcon } from "lucide-react";
import { usePathname } from "next/navigation";
import React from "react";
@@ -31,27 +32,36 @@ type OidcUserInfo = {
type Props = {
showClose?: boolean;
user?: OidcUserInfo;
setupKey?: string;
};
export default function SetupModal({ showClose = true, user }: Props) {
export default function SetupModal({
showClose = true,
user,
setupKey,
}: Readonly<Props>) {
return (
<ModalContent showClose={showClose}>
<SetupModalContent user={user} />
<SetupModalContent user={user} setupKey={setupKey} />
</ModalContent>
);
}
type SetupModalContentProps = {
user?: OidcUserInfo;
header?: boolean;
footer?: boolean;
tabAlignment?: "center" | "start" | "end";
setupKey?: string;
};
export function SetupModalContent({
user,
header = true,
footer = true,
tabAlignment = "center",
}: {
user?: OidcUserInfo;
header?: boolean;
footer?: boolean;
tabAlignment?: "center" | "start" | "end";
}) {
setupKey,
}: Readonly<SetupModalContentProps>) {
const os = useOperatingSystem();
const [isFirstRun] = useLocalStorage<boolean>("netbird-first-run", true);
const pathname = usePathname();
@@ -60,24 +70,33 @@ export function SetupModalContent({
return (
<>
{header && (
<div className={"text-center pb-8 pt-4 px-8"}>
<h2 className={"text-3xl max-w-lg mx-auto"}>
<div className={"text-center pb-5 pt-4 px-8"}>
<h2
className={cn(
"max-w-lg mx-auto",
setupKey ? "text-2xl" : "text-3xl",
)}
>
{isFirstRun && !isInstallPage ? (
<>
Hello {user?.given_name || "there"}! đź‘‹ <br />
{`It's time to add your first device.`}
</>
) : (
<>Install NetBird</>
<>Install NetBird{setupKey && " with Setup Key"}</>
)}
</h2>
<Paragraph className={"max-w-xs mx-auto mt-3"}>
To get started, install NetBird and log in with your email account.
<Paragraph
className={cn("mx-auto mt-3", setupKey ? "max-w-sm" : "max-w-xs")}
>
{setupKey
? "To get started, install and run NetBird with your recently created setup key as a parameter."
: "To get started, install NetBird and log in with your email account."}
</Paragraph>
</div>
)}
<Tabs defaultValue={String(os)}>
<Tabs defaultValue={String(setupKey ? OperatingSystem.LINUX : os)}>
<TabsList justify={tabAlignment} className={"pt-2 px-3"}>
<TabsTrigger value={String(OperatingSystem.LINUX)}>
<ShellIcon
@@ -103,22 +122,28 @@ export function SetupModalContent({
/>
macOS
</TabsTrigger>
<TabsTrigger value={String(OperatingSystem.IOS)}>
<IOSIcon
className={
"fill-nb-gray-500 group-data-[state=active]/trigger:fill-netbird transition-all"
}
/>
iOS
</TabsTrigger>
<TabsTrigger value={String(OperatingSystem.ANDROID)}>
<AndroidIcon
className={
"fill-nb-gray-500 group-data-[state=active]/trigger:fill-netbird transition-all"
}
/>
Android
</TabsTrigger>
{!setupKey && (
<>
<TabsTrigger value={String(OperatingSystem.IOS)}>
<IOSIcon
className={
"fill-nb-gray-500 group-data-[state=active]/trigger:fill-netbird transition-all"
}
/>
iOS
</TabsTrigger>
<TabsTrigger value={String(OperatingSystem.ANDROID)}>
<AndroidIcon
className={
"fill-nb-gray-500 group-data-[state=active]/trigger:fill-netbird transition-all"
}
/>
Android
</TabsTrigger>
</>
)}
<TabsTrigger value={String(OperatingSystem.DOCKER)}>
<DockerIcon
className={
@@ -128,12 +153,19 @@ export function SetupModalContent({
Docker
</TabsTrigger>
</TabsList>
<LinuxTab />
<WindowsTab />
<MacOSTab />
<AndroidTab />
<IOSTab />
<DockerTab />
<LinuxTab setupKey={setupKey} />
<WindowsTab setupKey={setupKey} />
<MacOSTab setupKey={setupKey} />
{!setupKey && (
<>
<AndroidTab />
<IOSTab />
</>
)}
<DockerTab setupKey={setupKey} />
</Tabs>
{footer && (
<ModalFooter variant={"setup"}>
@@ -158,3 +190,18 @@ export function SetupModalContent({
</>
);
}
type SetupKeyParameterProps = {
setupKey?: string;
};
export const SetupKeyParameter = ({ setupKey }: SetupKeyParameterProps) => {
return (
setupKey && (
<>
{" "}
--setup-key <span className={"text-netbird"}>{setupKey}</span>{" "}
</>
)
);
};
+30 -11
View File
@@ -2,13 +2,18 @@ import Button from "@components/Button";
import Code from "@components/Code";
import Steps from "@components/Steps";
import TabsContentPadding, { TabsContent } from "@components/Tabs";
import { GRPC_API_ORIGIN } from "@utils/netbird";
import { getNetBirdUpCommand, GRPC_API_ORIGIN } from "@utils/netbird";
import { DownloadIcon, PackageOpenIcon } from "lucide-react";
import Link from "next/link";
import React from "react";
import { OperatingSystem } from "@/interfaces/OperatingSystem";
import { SetupKeyParameter } from "@/modules/setup-netbird-modal/SetupModal";
export default function WindowsTab() {
type Props = {
setupKey?: string;
};
export default function WindowsTab({ setupKey }: Readonly<Props>) {
return (
<TabsContent value={String(OperatingSystem.WINDOWS)}>
<TabsContentPadding>
@@ -44,15 +49,29 @@ export default function WindowsTab() {
</Steps.Step>
)}
<Steps.Step step={GRPC_API_ORIGIN ? 3 : 2}>
<p>
{/* eslint-disable-next-line react/no-unescaped-entities */}
Click on "Connect" from the NetBird icon in your system tray
</p>
</Steps.Step>
<Steps.Step step={GRPC_API_ORIGIN ? 4 : 3} line={false}>
<p>Sign up using your email address</p>
</Steps.Step>
{setupKey ? (
<Steps.Step step={GRPC_API_ORIGIN ? 3 : 2} line={false}>
<p>Open Command-line and run NetBird</p>
<Code>
<Code.Line>
{getNetBirdUpCommand()}
<SetupKeyParameter setupKey={setupKey} />
</Code.Line>
</Code>
</Steps.Step>
) : (
<>
<Steps.Step step={GRPC_API_ORIGIN ? 3 : 2}>
<p>
{/* eslint-disable-next-line react/no-unescaped-entities */}
Click on "Connect" from the NetBird icon in your system tray
</p>
</Steps.Step>
<Steps.Step step={GRPC_API_ORIGIN ? 4 : 3} line={false}>
<p>Sign up using your email address</p>
</Steps.Step>
</>
)}
</Steps>
</TabsContentPadding>
</TabsContent>