Compare commits

..
Author SHA1 Message Date
Eduard Gert e167afb06b Fix missing groups on users and dns settings page 2024-11-13 16:18:54 +01:00
100 changed files with 631 additions and 5612 deletions

No files matched your search

+2 -1
View File
@@ -12,5 +12,6 @@
"tokenSource": "$NETBIRD_TOKEN_SOURCE",
"dragQueryParams": "$NETBIRD_DRAG_QUERY_PARAMS",
"hotjarTrackID": "$NETBIRD_HOTJAR_TRACK_ID",
"googleAnalyticsID": "$NETBIRD_GOOGLE_ANALYTICS_ID"
"googleAnalyticsID": "$NETBIRD_GOOGLE_ANALYTICS_ID",
"authServiceUrl": "$NETBIRD_AUTH_SERVICE_URL"
}
+196 -720
View File
File diff suppressed because it is too large. Load diff
+3 -4
View File
@@ -19,7 +19,6 @@
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-hover-card": "^1.1.4",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-radio-group": "^1.1.3",
@@ -56,8 +55,8 @@
"framer-motion": "^10.16.4",
"ip-cidr": "^3.1.0",
"lodash": "^4.17.21",
"lucide-react": "^0.460.0",
"next": "13.5.7",
"lucide-react": "^0.383.0",
"next": "13.5.5",
"next-themes": "^0.2.1",
"punycode": "^2.3.1",
"react": "^18",
@@ -77,7 +76,7 @@
"typescript": "^5"
},
"devDependencies": {
"cypress": "^13.13.0",
"cypress": "^13.3.3",
"postcss": "^8",
"prettier": "3.0.3",
"tailwindcss": "^3"
-8
View File
@@ -1,8 +0,0 @@
import { globalMetaTitle } from "@utils/meta";
import type { Metadata } from "next";
import BlankLayout from "@/layouts/BlankLayout";
export const metadata: Metadata = {
title: `Network - Networks - ${globalMetaTitle}`,
};
export default BlankLayout;
-229
View File
@@ -1,229 +0,0 @@
"use client";
import Breadcrumbs from "@components/Breadcrumbs";
import Card from "@components/Card";
import FullTooltip from "@components/FullTooltip";
import InlineLink from "@components/InlineLink";
import Separator from "@components/Separator";
import FullScreenLoading from "@components/ui/FullScreenLoading";
import useRedirect from "@hooks/useRedirect";
import useFetchApi from "@utils/api";
import { cn } from "@utils/helpers";
import {
ArrowUpRightIcon,
HelpCircle,
PencilLineIcon,
ServerIcon,
ShieldCheckIcon,
ShieldXIcon,
} from "lucide-react";
import { useSearchParams } from "next/navigation";
import React, { useMemo, useState } from "react";
import { useSWRConfig } from "swr";
import NetworkRoutesIcon from "@/assets/icons/NetworkRoutesIcon";
import { useLoggedInUser } from "@/contexts/UsersProvider";
import { Network } from "@/interfaces/Network";
import PageContainer from "@/layouts/PageContainer";
import { NetworkInformationSquare } from "@/modules/networks/misc/NetworkInformationSquare";
import NetworkModal from "@/modules/networks/NetworkModal";
import { NetworkProvider } from "@/modules/networks/NetworkProvider";
import { ResourcesSection } from "@/modules/networks/resources/ResourcesSection";
import { NetworkRoutingPeersSection } from "@/modules/networks/routing-peers/NetworkRoutingPeersSection";
export default function NetworkDetailPage() {
const queryParameter = useSearchParams();
const networkId = queryParameter.get("id");
const { data: network, isLoading } = useFetchApi<Network>(
`/networks/${networkId}`,
true,
);
useRedirect("/networks", false, !networkId);
return network && !isLoading ? (
<NetworkOverview network={network} />
) : (
<FullScreenLoading />
);
}
function NetworkOverview({ network }: Readonly<{ network: Network }>) {
const { isUser } = useLoggedInUser();
const [networkModal, setNetworkModal] = useState(false);
const { mutate } = useSWRConfig();
const isActive = !!(
network?.routing_peers_count && network.routing_peers_count > 0
);
return (
<PageContainer>
<NetworkProvider network={network}>
<div className={"p-default py-6 mb-4"}>
<Breadcrumbs>
<Breadcrumbs.Item
href={"/networks"}
label={"Networks"}
disabled={isUser}
icon={<NetworkRoutesIcon size={13} />}
/>
<Breadcrumbs.Item
href={"/network"}
label={network.name}
active={true}
/>
</Breadcrumbs>
<div className={"flex justify-between max-w-6xl"}>
<div
className={cn(
"flex items-center",
!network.description && "gap-2",
)}
>
<NetworkInformationSquare
name={network.name}
active={isActive}
size={"lg"}
description={network.description}
/>
<button
className={
"flex items-center gap-2 dark:text-neutral-300 text-neutral-500 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 py-2 px-3 rounded-md cursor-pointer"
}
onClick={() => setNetworkModal(true)}
>
<PencilLineIcon size={18} />
</button>
<NetworkModal
open={networkModal}
setOpen={setNetworkModal}
onUpdated={() => {
mutate(`/networks/${network.id}`);
}}
network={network}
/>
</div>
</div>
<div className={"flex gap-10 w-full mt-8 max-w-6xl items-start"}>
<NetworkInformationCard network={network} />
</div>
</div>
<Separator />
<ResourcesSection network={network} />
<div className={"h-3"} />
<Separator />
<NetworkRoutingPeersSection network={network} />
</NetworkProvider>
</PageContainer>
);
}
function NetworkInformationCard({ network }: Readonly<{ network: Network }>) {
const isHighlyAvailable = !!(
network?.routing_peers_count && network?.routing_peers_count >= 2
);
const disabledText = useMemo(
() => (
<>
High availability is currently{" "}
<span className={"text-yellow-400 font-medium"}>inactive</span> for this
network.
</>
),
[],
);
const enabledText = useMemo(
() => (
<>
High availability is{" "}
<span className={"text-green-500 font-medium"}>active</span> for this
network.
</>
),
[],
);
const policyCount = network.policies?.length ?? 0;
return (
<Card>
<Card.List>
<Card.ListItem
tooltip={false}
label={
<>
<ServerIcon size={16} />
High Availability
</>
}
value={
<FullTooltip
interactive={false}
content={
<div className={"max-w-xs text-xs"}>
{isHighlyAvailable ? enabledText : disabledText}
{isHighlyAvailable ? (
<div className={"inline-flex mt-2"}>
You can add more routing peers to increase the
availability of this network.
</div>
) : (
<div className={"inline-flex mt-2"}>
Go ahead and add more routing peers or groups with routing
peers to enable high availability for this network.
</div>
)}
</div>
}
>
<div
className={cn(
"flex gap-2.5 items-center text-nb-gray-300 text-sm cursor-help",
)}
>
<span
className={cn(
"h-2 w-2 rounded-full",
!isHighlyAvailable ? "bg-yellow-400" : "bg-green-500",
)}
></span>
{isHighlyAvailable ? "Active" : "Inactive"}
<HelpCircle size={12} />
</div>
</FullTooltip>
}
/>
<Card.ListItem
tooltip={false}
label={
policyCount > 0 ? (
<>
<ShieldCheckIcon size={16} className={"text-green-500"} />
{policyCount}{" "}
{policyCount === 1 ? "Active Policy" : "Active Policies"}
</>
) : (
<>
<ShieldXIcon size={16} className={"text-red-500"} />
No Active Policies
</>
)
}
value={
policyCount > 0 ? (
<InlineLink href={"/access-control"}>
Go to Policies
<ArrowUpRightIcon size={14} />
</InlineLink>
) : null
}
/>
</Card.List>
</Card>
);
}
-8
View File
@@ -1,8 +0,0 @@
import { globalMetaTitle } from "@utils/meta";
import type { Metadata } from "next";
import BlankLayout from "@/layouts/BlankLayout";
export const metadata: Metadata = {
title: `Networks - ${globalMetaTitle}`,
};
export default BlankLayout;
-61
View File
@@ -1,61 +0,0 @@
"use client";
import Breadcrumbs from "@components/Breadcrumbs";
import InlineLink from "@components/InlineLink";
import Paragraph from "@components/Paragraph";
import SkeletonTable from "@components/skeletons/SkeletonTable";
import { RestrictedAccess } from "@components/ui/RestrictedAccess";
import { usePortalElement } from "@hooks/usePortalElement";
import useFetchApi from "@utils/api";
import { ExternalLinkIcon } from "lucide-react";
import React, { Suspense } from "react";
import NetworkRoutesIcon from "@/assets/icons/NetworkRoutesIcon";
import { Network } from "@/interfaces/Network";
import PageContainer from "@/layouts/PageContainer";
import NetworksTable from "@/modules/networks/table/NetworksTable";
export default function Networks() {
const { data: networks, isLoading } = useFetchApi<Network[]>("/networks");
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
return (
<PageContainer>
<div className={"p-default py-6"}>
<Breadcrumbs>
<Breadcrumbs.Item
href={"/networks"}
label={"Networks"}
icon={<NetworkRoutesIcon size={13} />}
/>
</Breadcrumbs>
<h1 ref={headingRef}>Networks</h1>
<Paragraph>
Networks allow you to access internal resources in LANs and VPCs without
installing NetBird on every machine.
</Paragraph>
<Paragraph>
Learn more about
<InlineLink
href={"https://docs.netbird.io/how-to/networks"}
target={"_blank"}
>
Networks
<ExternalLinkIcon size={12} />
</InlineLink>
in our documentation.
</Paragraph>
</div>
<RestrictedAccess>
<Suspense fallback={<SkeletonTable />}>
<NetworksTable
data={networks}
isLoading={isLoading}
headingTarget={portalTarget}
/>
</Suspense>
</RestrictedAccess>
</PageContainer>
);
}
+25 -8
View File
@@ -297,12 +297,29 @@ function PeerOverview() {
/>
</FullTooltip>
{!isUser && (
<div>
<Label>Assigned Groups</Label>
<HelpText>
Use groups to control what this peer can access.
</HelpText>
<div>
<Label>Assigned Groups</Label>
<HelpText>
Use groups to control what this peer can access.
</HelpText>
<FullTooltip
content={
<div
className={
"flex gap-2 items-center !text-nb-gray-300 text-xs"
}
>
<LockIcon size={14} />
<span>
{`You don't have the required permissions to update this
setting.`}
</span>
</div>
}
interactive={false}
className={"w-full block"}
disabled={!isUser}
>
<PeerGroupSelector
disabled={isUser}
onChange={setSelectedGroups}
@@ -310,8 +327,8 @@ function PeerOverview() {
hideAllGroup={true}
peer={peer}
/>
</div>
)}
</FullTooltip>
</div>
</div>
</div>
</div>
+5 -8
View File
@@ -6,18 +6,17 @@ import {
AlertOctagonIcon,
FolderGit2Icon,
LockIcon,
NetworkIcon,
ShieldIcon,
} from "lucide-react";
import { useSearchParams } from "next/navigation";
import React, { useEffect, useState } from "react";
import { useLoggedInUser } from "@/contexts/UsersProvider";
import { Role } from "@/interfaces/User";
import PageContainer from "@/layouts/PageContainer";
import { useAccount } from "@/modules/account/useAccount";
import AuthenticationTab from "@/modules/settings/AuthenticationTab";
import DangerZoneTab from "@/modules/settings/DangerZoneTab";
import GroupsTab from "@/modules/settings/GroupsTab";
import NetworkSettingsTab from "@/modules/settings/NetworkSettingsTab";
import PermissionsTab from "@/modules/settings/PermissionsTab";
export default function NetBirdSettings() {
@@ -49,21 +48,19 @@ export default function NetBirdSettings() {
<LockIcon size={14} />
Permissions
</VerticalTabs.Trigger>
<VerticalTabs.Trigger value="networks">
<NetworkIcon size={14} />
Networks
</VerticalTabs.Trigger>
<VerticalTabs.Trigger value="danger-zone" disabled={!isOwner}>
<AlertOctagonIcon size={14} />
Danger zone
</VerticalTabs.Trigger>
</VerticalTabs.List>
<RestrictedAccess page={"Settings"}>
<RestrictedAccess
page={"Settings"}
allow={[Role.Admin, Role.Owner, Role.BillingAdmin]}
>
<div className={"border-l border-nb-gray-930 w-full"}>
{account && <AuthenticationTab account={account} />}
{account && <PermissionsTab account={account} />}
{account && <GroupsTab account={account} />}
{account && <NetworkSettingsTab account={account} />}
{account && <DangerZoneTab account={account} />}
</div>
</RestrictedAccess>
-1
View File
@@ -316,7 +316,6 @@ function UserInformationCard({ user }: { user: User }) {
<>
{!user.is_current && user.role != Role.Owner && (
<Card.ListItem
tooltip={false}
label={
<>
<Ban size={16} />
+2 -9
View File
@@ -15,9 +15,7 @@ export const OIDCError = () => {
const params = useSearchParams();
const errorParam = params.get("error");
const accessDenied = errorParam === "access_denied";
const invalidRequest = errorParam === "invalid_request";
const [title, setTitle] = useState(params.get("error_description"));
const errorDescription = params.get("error_description");
const { logout, login } = useOidc();
useEffect(() => {
@@ -74,14 +72,9 @@ export const OIDCError = () => {
</>
) : (
<>
<Paragraph className={"text-center mt-2 block"}>
<Paragraph className={"text-center mt-2"}>
There was an error logging you in. <br />
Error:{" "}
<span className={"inline capitalize"}>
{invalidRequest && errorDescription
? errorDescription
: oidcUserLoadingState}
</span>
Error: {oidcUserLoadingState}
</Paragraph>
<Button
variant={"primary"}
-5
View File
@@ -35,11 +35,6 @@ export const buttonVariants = cva(
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20 ",
"dark:bg-nb-gray-900/30 dark:text-gray-400 dark:border-gray-700/40 dark:hover:text-white dark:hover:bg-zinc-800/50",
],
secondaryLighter: [
"bg-white hover:text-black focus:ring-zinc-200/50 hover:bg-gray-100 border-gray-200 text-gray-900",
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20 ",
"dark:bg-nb-gray-900/70 dark:text-gray-400 dark:border-gray-700/70 dark:hover:text-white dark:hover:bg-nb-gray-800/60",
],
input: [
"bg-white hover:text-black focus:ring-zinc-200/50 hover:bg-gray-100 border-neutral-200 text-gray-900",
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20 ",
+2 -6
View File
@@ -1,15 +1,11 @@
import { cn } from "@utils/helpers";
import * as React from "react";
type Props = {
children: React.ReactNode;
className?: string;
};
export const DropdownInfoText = ({ children, className }: Props) => {
export const DropdownInfoText = ({ children }: Props) => {
return (
<div className={cn("text-center pt-2 mb-6 text-nb-gray-400", className)}>
{children}
</div>
<div className={"text-center pt-2 mb-6 text-nb-gray-400"}>{children}</div>
);
};
+3 -14
View File
@@ -1,15 +1,12 @@
import FullTooltip from "@components/FullTooltip";
import Paragraph from "@components/Paragraph";
import { cn } from "@utils/helpers";
import { cva, VariantProps } from "class-variance-authority";
import { cva } from "class-variance-authority";
import { AlertCircle } from "lucide-react";
import * as React from "react";
type InputVariants = VariantProps<typeof inputVariants>;
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement>,
InputVariants {
extends React.InputHTMLAttributes<HTMLInputElement> {
customPrefix?: React.ReactNode;
customSuffix?: React.ReactNode;
maxWidthClass?: string;
@@ -17,7 +14,6 @@ export interface InputProps
error?: string;
errorTooltip?: boolean;
errorTooltipPosition?: "top" | "top-right";
prefixClassName?: string;
}
const inputVariants = cva("", {
@@ -27,10 +23,6 @@ const inputVariants = cva("", {
"dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70 placeholder:text-neutral-500 border-neutral-200 dark:border-nb-gray-700",
"ring-offset-neutral-200/20 dark:ring-offset-neutral-950/50 dark:focus-visible:ring-neutral-500/20 focus-visible:ring-neutral-300/10",
],
darker: [
"dark:bg-nb-gray-920 dark:placeholder:text-neutral-400/70 placeholder:text-neutral-500 border-neutral-300 dark:border-nb-gray-800",
"ring-offset-neutral-200/20 dark:ring-offset-neutral-950/50 dark:focus-visible:ring-neutral-500/20 focus-visible:ring-neutral-300/10",
],
error: [
"dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70 placeholder:text-neutral-500 border-neutral-200 dark:border-red-500 text-red-500",
"ring-offset-red-500/10 dark:ring-offset-red-500/10 dark:focus-visible:ring-red-500/10 focus-visible:ring-red-500/10",
@@ -59,8 +51,6 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
error,
errorTooltip = false,
errorTooltipPosition = "top",
variant = "default",
prefixClassName,
...props
},
ref,
@@ -77,7 +67,6 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
"flex h-[42px] w-auto rounded-l-md bg-white px-3 py-2 text-sm ",
"border items-center whitespace-nowrap",
props.disabled && "opacity-20",
prefixClassName,
)}
>
{customPrefix}
@@ -98,7 +87,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
ref={ref}
{...props}
className={cn(
inputVariants({ variant: error ? "error" : variant }),
inputVariants({ variant: error ? "error" : "default" }),
"flex h-[42px] w-full rounded-md bg-white px-3 py-2 text-sm file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-20 ",
"file:border-0",
"focus-visible:ring-2 focus-visible:ring-offset-2",
File diff suppressed because it is too large. Load diff
+7 -9
View File
@@ -166,17 +166,15 @@ export function PeerSelector({
placeholder={"Search for peers by name or ip..."}
/>
{unfilteredItems.length == 0 && !search && (
<div className={"max-w-xs mx-auto"}>
<DropdownInfoText>
{
"Seems like you don't have any Linux peers to assign as a routing peer."
}
</DropdownInfoText>
</div>
{unfilteredItems.length == 0 && (
<DropdownInfoText>
{
"Seems like you don't have any linux peers to assign as a routing peer."
}
</DropdownInfoText>
)}
{filteredItems.length == 0 && search != "" && (
{filteredItems.length == 0 && (
<DropdownInfoText>
There are no peers matching your search.
</DropdownInfoText>
-71
View File
@@ -1,71 +0,0 @@
import * as RadioPrimitive from "@radix-ui/react-radio-group";
import { cn } from "@utils/helpers";
import { cva, VariantProps } from "class-variance-authority";
import * as React from "react";
import { forwardRef } from "react";
type RadioVariants = VariantProps<typeof variants>;
const variants = cva([], {
variants: {
variant: {
default: [
"dark:data-[state=unchecked]:bg-nb-gray-950 dark:border-nb-gray-900 dark:ring-offset-neutral-950 dark:focus-visible:ring-neutral-300",
"dark:data-[state=checked]:bg-netbird",
],
},
},
});
const Radio = forwardRef<
React.ElementRef<typeof RadioPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof RadioPrimitive.Root> & RadioVariants
>(
(
{ className, children, variant = "default", defaultValue, ...props },
ref,
) => (
<RadioPrimitive.Root
ref={ref}
defaultValue={defaultValue}
name={props.name}
{...props}
>
{children}
</RadioPrimitive.Root>
),
);
Radio.displayName = RadioPrimitive.Root.displayName;
type Props = {
value: string;
className?: string;
} & RadioVariants;
const RadioItem = ({ value, className, variant = "default" }: Props) => {
return (
<RadioPrimitive.Item
value={value}
className={cn(
variants({ variant }),
"border-neutral-900",
"peer h-5 w-5 shrink-0 rounded-full border",
"ring-offset-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2",
"disabled:cursor-not-allowed disabled:opacity-50 relative",
className,
)}
>
<RadioPrimitive.Indicator asChild={true}>
<div
className={cn(
"h-2 w-2 bg-netbird absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 flex items-center justify-center rounded-full",
"data-[state=checked]:bg-white data-[state=checked]:text-neutral-50 ",
)}
></div>
</RadioPrimitive.Indicator>
</RadioPrimitive.Item>
);
};
RadioItem.displayName = RadioPrimitive.Item.displayName;
export { Radio, RadioItem };
+35 -16
View File
@@ -1,3 +1,5 @@
"use client";
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import { cn } from "@utils/helpers";
import * as React from "react";
@@ -13,31 +15,46 @@ const ScrollArea = React.forwardRef<
>(({ className, children, withoutViewport = false, ...props }, ref) => (
<ScrollAreaPrimitive.Root
ref={ref}
className={cn("relative overflow-hidden", className)}
className={cn(
"relative will-change-scroll webkit-scroll",
className,
"overflow-hidden",
)}
{...props}
>
{withoutViewport ? (
children
) : (
<ScrollAreaViewport>{children}</ScrollAreaViewport>
<ScrollAreaViewport disableOverflowY={false}>
{children}
</ScrollAreaViewport>
)}
<ScrollBar orientation="horizontal" />
<ScrollBar orientation="vertical" />
<ScrollBar />
<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>
>(({ className, ...props }, ref) => (
<ScrollAreaPrimitive.Viewport
ref={ref}
className={cn("h-full w-full rounded-[inherit]", className)}
{...props}
/>
));
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
}
/>
);
});
ScrollAreaViewport.displayName = ScrollAreaPrimitive.Viewport.displayName;
const ScrollBar = React.forwardRef<
@@ -46,11 +63,14 @@ const ScrollBar = React.forwardRef<
>(({ className, orientation = "vertical", ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
style={{ boxSizing: "unset", overflow: undefined }}
orientation={orientation}
className={cn(
"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",
"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]",
className,
)}
{...props}
@@ -59,7 +79,6 @@ 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>
+2 -4
View File
@@ -60,12 +60,10 @@ export default function SidebarItem({
<li className={"px-4 cursor-pointer"}>
<button
className={classNames(
"rounded-lg text-[.87rem] w-full ",
"rounded-lg text-[.95rem] w-full ",
"font-normal ",
className,
isChild
? "pl-7 pr-2 py-[.45rem] mt-1 mb-0.5"
: "py-[.45rem] px-3",
isChild ? "pl-7 pr-2 py-2 mt-1 mb-0.5" : "py-2 px-3",
isActive
? "text-gray-900 bg-gray-200 dark:text-white dark:bg-nb-gray-900"
: "text-gray-600 hover:bg-gray-200 dark:text-nb-gray-400 dark:hover:bg-nb-gray-900/50",
+7 -27
View File
@@ -4,18 +4,9 @@ import React from "react";
type Props = {
children: React.ReactNode;
className?: string;
horizontal?: boolean;
};
export default function Steps({
children,
className,
horizontal = false,
}: Readonly<Props>) {
return (
<div className={cn("pt-4", horizontal && "flex", className)}>
{children}
</div>
);
export default function Steps({ children, className }: Props) {
return <div className={cn("pt-4", className)}>{children}</div>;
}
type StepProps = {
@@ -23,32 +14,21 @@ type StepProps = {
step: number;
line?: boolean;
center?: boolean;
horizontal?: boolean;
};
const Step = ({
children,
step,
line = true,
center = false,
horizontal,
}: StepProps) => {
const Step = ({ children, step, line = true, center = false }: StepProps) => {
return (
<div
className={cn(
"flex gap-4 items-start justify-start relative pb-6 -mx-1.5 group px-[2px]",
"flex gap-4 items-start min-w-full justify-start relative pb-6 -mx-1.5 group px-[2px]",
center && "items-center",
horizontal ? "flex-col items-center" : "min-w-full",
)}
>
{line && (
<span
className={cn(
"bg-nb-gray-100 dark:bg-nb-gray-800 z-0 transition-all",
horizontal
? "w-full h-[2px] absolute mt-[16px] transform translate-x-1/2"
: "h-full w-[2px] absolute left-0 ml-[18px]",
)}
className={
"h-full w-[2px] bg-nb-gray-100 dark:bg-nb-gray-800 absolute left-0 ml-[18px] z-0 transition-all"
}
></span>
)}
Loaded 20 of 100 files, more files were not shown because too many files have changed in this diff. Show more