Skip to content

Commit

Permalink
UX changes for modals and refactoring (#380)
Browse files Browse the repository at this point in the history
  • Loading branch information
heisbrot authored May 8, 2024
1 parent 3f943bb commit 5caeab1
Show file tree
Hide file tree
Showing 27 changed files with 455 additions and 257 deletions.
3 changes: 3 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const nextConfig = {
unoptimized: true,
},
reactStrictMode: false,
env: {
APP_ENV: process.env.APP_ENV || "production",
},
};

module.exports = nextConfig;
38 changes: 32 additions & 6 deletions src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
"use client";

import FullScreenLoading from "@components/ui/FullScreenLoading";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { useLocalStorage } from "@hooks/useLocalStorage";
import { useRedirect } from "@hooks/useRedirect";
import { useEffect, useState } from "react";

type Props = {
url: string;
queryParams?: string;
};
export default function NotFound() {
const router = useRouter();
const [mounted, setMounted] = useState(false);
const [tempQueryParams, setTempQueryParams] = useLocalStorage(
"netbird-query-params",
"",
);
const [queryParams, setQueryParams] = useState("");

useEffect(() => {
router.push("/peers");
});
setQueryParams(tempQueryParams);
setTempQueryParams("");
setMounted(true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return <FullScreenLoading />;
return mounted ? (
<Redirect
url={window?.location?.pathname || "/"}
queryParams={queryParams}
/>
) : (
<FullScreenLoading />
);
}

const Redirect = ({ url, queryParams }: Props) => {
useRedirect(url == "/" ? "/peers" : url + (queryParams && `?${queryParams}`));
return <FullScreenLoading />;
};
2 changes: 2 additions & 0 deletions src/assets/icons/CircleIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export default function CircleIcon({
return (
<span
style={{ width: size + "px", height: size + "px" }}
data-cy="circle-icon"
data-cy-status={active ? "active" : "inactive"}
className={cn(
"rounded-full",
active
Expand Down
10 changes: 8 additions & 2 deletions src/components/ButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import React, { forwardRef } from "react";
type Props = {
children: React.ReactNode;
disabled?: boolean;
className?: string;
};

function ButtonGroup({ children, disabled }: Props) {
function ButtonGroup({ children, disabled, className }: Props) {
return (
<div
className={cn(
"rounded-lg border-[1px] dark:border-nb-gray-900 border-neutral-200 overflow-hidden flex items-center justify-center shrink-0 border-separate",
disabled ? "opacity-100 !border-nb-gray-900/20" : "",
className,
)}
>
{children}
Expand All @@ -21,7 +23,10 @@ function ButtonGroup({ children, disabled }: Props) {
}

const ButtonGroupButton = forwardRef(
({ ...props }: ButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) => {
(
{ className, ...props }: ButtonProps,
ref: React.ForwardedRef<HTMLButtonElement>,
) => {
return (
<Button
ref={ref}
Expand All @@ -31,6 +36,7 @@ const ButtonGroupButton = forwardRef(
className={cn(
"first:border-l-0 last:border-r-0 border-t-0 border-b-0 h-[41px]",
"!py-2.5 !px-4",
className,
)}
/>
);
Expand Down
1 change: 1 addition & 0 deletions src/components/PeerGroupSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ export function PeerGroupSelector({
<CommandList className={"w-full"}>
<div className={"relative"}>
<CommandInput
data-cy={"group-search-input"}
className={cn(
"min-h-[42px] w-full relative",
"border-b-0 border-t-0 border-r-0 border-l-0 border-neutral-200 dark:border-nb-gray-700 items-center",
Expand Down
15 changes: 11 additions & 4 deletions src/components/SegmentedTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@ function SegmentedTabs({ value, onChange, children }: Props) {
);
}

function List({ children }: { children: React.ReactNode }) {
function List({
children,
className = "",
}: {
children: React.ReactNode;
className?: string;
}) {
return (
<TabsList
className={
"bg-nb-gray-930/70 p-1.5 rounded-t-lg flex justify-center gap-1 border border-b-0 border-nb-gray-900"
}
className={cn(
"bg-nb-gray-930/70 p-1.5 rounded-t-lg flex justify-center gap-1 border border-b-0 border-nb-gray-900",
className,
)}
>
{children}
</TabsList>
Expand Down
5 changes: 4 additions & 1 deletion src/components/modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ const ModalContent = React.forwardRef<
<>
{children}
{showClose && (
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-neutral-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-neutral-100 data-[state=open]:text-neutral-500 dark:ring-offset-neutral-950 dark:focus:ring-neutral-300 dark:data-[state=open]:bg-neutral-800 dark:data-[state=open]:text-neutral-400">
<DialogPrimitive.Close
data-cy={"modal-close"}
className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-neutral-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-neutral-100 data-[state=open]:text-neutral-500 dark:ring-offset-neutral-950 dark:focus:ring-neutral-300 dark:data-[state=open]:bg-neutral-800 dark:data-[state=open]:text-neutral-400"
>
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
Expand Down
4 changes: 3 additions & 1 deletion src/components/table/DataTableRowsPerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ export function DataTableRowsPerPage<TData>({
role="combobox"
aria-expanded={open}
disabled={disabled}
data-cy={"rows-per-page"}
className="w-[200px] justify-between"
>
<RowsIcon size={15} className={"text-nb-gray-300"} />
<RowsIcon size={15} className={"text-nb-gray-300 shrink-0"} />
<div>
<span className={"text-white"}>
{table.getState().pagination.pageSize}
Expand All @@ -47,6 +48,7 @@ export function DataTableRowsPerPage<TData>({
<CommandItem
key={val}
value={val.toString()}
data-cy={`rows-per-page-value`}
onSelect={(currentValue) => {
table.setPageSize(Number(currentValue));
setOpen(false);
Expand Down
2 changes: 2 additions & 0 deletions src/contexts/DialogProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export default function DialogProvider({ children }: Props) {
className={"w-full"}
size={"sm"}
tabIndex={-1}
data-cy={"confirmation.cancel"}
onClick={() => fn.current && fn.current(false)}
>
{dialogOptions.cancelText || "Cancel"}
Expand All @@ -109,6 +110,7 @@ export default function DialogProvider({ children }: Props) {
}
className={"w-full"}
size={"sm"}
data-cy={"confirmation.confirm"}
onClick={() => fn.current && fn.current(true)}
>
{dialogOptions.confirmText || "Confirm"}
Expand Down
4 changes: 1 addition & 3 deletions src/contexts/PeerProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,7 @@ export default function PeerProvider({ children, peer }: Props) {
? loginExpiration
: peer.login_expiration_enabled,
approval_required:
approval_required != undefined
? approval_required
: peer.approval_required,
approval_required == undefined ? undefined : approval_required,
},
`/${peer.id}`,
);
Expand Down
111 changes: 84 additions & 27 deletions src/modules/access-control/AccessControlModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,6 @@ export function AccessControlModalContent({

const portAndDirectionDisabled = protocol == "icmp" || protocol == "all";

const buttonDisabled = useMemo(() => {
if (sourceGroups.length == 0 || destinationGroups.length == 0) return true;
if (name.length == 0) return true;
if (direction != "bi" && ports.length == 0) return true;
}, [sourceGroups, destinationGroups, direction, ports, name]);

const [postureChecks, setPostureChecks] = useState<PostureCheck[]>([]);
const postureChecksLoaded = useRef(false);

Expand All @@ -268,6 +262,16 @@ export function AccessControlModalContent({
}
}, [initialPostureChecks]);

const continuePostureChecksDisabled = useMemo(() => {
if (sourceGroups.length == 0 || destinationGroups.length == 0) return true;
if (direction != "bi" && ports.length == 0) return true;
}, [sourceGroups, destinationGroups, direction, ports]);

const submitDisabled = useMemo(() => {
if (name.length == 0) return true;
if (continuePostureChecksDisabled) return true;
}, [name, continuePostureChecksDisabled]);

return (
<ModalContent maxWidthClass={"max-w-2xl"}>
<ModalHeader
Expand All @@ -283,14 +287,17 @@ export function AccessControlModalContent({
color={"netbird"}
/>

<Tabs defaultValue={tab} onValueChange={(v) => setTab(v)}>
<Tabs defaultValue={tab} onValueChange={(v) => setTab(v)} value={tab}>
<TabsList justify={"start"} className={"px-8"}>
<TabsTrigger value={"policy"}>
<ArrowRightLeft size={16} />
Policy
</TabsTrigger>
<PostureCheckTabTrigger />
<TabsTrigger value={"general"}>
<PostureCheckTabTrigger disabled={continuePostureChecksDisabled} />
<TabsTrigger
value={"general"}
disabled={continuePostureChecksDisabled}
>
<Text
size={16}
className={
Expand Down Expand Up @@ -456,24 +463,74 @@ export function AccessControlModalContent({
</Paragraph>
</div>
<div className={"flex gap-3 w-full justify-end"}>
<ModalClose asChild={true}>
<Button variant={"secondary"}>Cancel</Button>
</ModalClose>

<Button
variant={"primary"}
disabled={buttonDisabled}
onClick={submit}
>
{policy ? (
<>Save Changes</>
) : (
<>
<PlusCircle size={16} />
Add Policy
</>
)}
</Button>
{!policy ? (
<>
{tab == "policy" && (
<ModalClose asChild={true}>
<Button variant={"secondary"}>Cancel</Button>
</ModalClose>
)}

{tab == "posture_checks" && (
<Button variant={"secondary"} onClick={() => setTab("policy")}>
Back
</Button>
)}

{tab == "policy" && (
<Button
variant={"primary"}
onClick={() => setTab("posture_checks")}
disabled={continuePostureChecksDisabled}
>
Continue
</Button>
)}

{tab == "posture_checks" && (
<Button
variant={"primary"}
onClick={() => setTab("general")}
disabled={continuePostureChecksDisabled}
>
Continue
</Button>
)}

{tab == "general" && (
<>
<Button
variant={"secondary"}
onClick={() => setTab("posture_checks")}
>
Back
</Button>

<Button
variant={"primary"}
disabled={submitDisabled}
onClick={submit}
>
<PlusCircle size={16} />
Add Policy
</Button>
</>
)}
</>
) : (
<>
<ModalClose asChild={true}>
<Button variant={"secondary"}>Cancel</Button>
</ModalClose>
<Button
variant={"primary"}
disabled={submitDisabled}
onClick={submit}
>
Save Changes
</Button>
</>
)}
</div>
</ModalFooter>
</ModalContent>
Expand Down
13 changes: 12 additions & 1 deletion src/modules/common-table-rows/GroupsRow.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Badge from "@components/Badge";
import Button from "@components/Button";
import {
Modal,
Expand All @@ -10,6 +11,7 @@ import ModalHeader from "@components/modal/ModalHeader";
import { PeerGroupSelector } from "@components/PeerGroupSelector";
import Separator from "@components/Separator";
import MultipleGroups from "@components/ui/MultipleGroups";
import { IconCirclePlus } from "@tabler/icons-react";
import { FolderGit2 } from "lucide-react";
import * as React from "react";
import { useMemo } from "react";
Expand All @@ -27,6 +29,7 @@ type Props = {
label?: string;
description?: string;
peer?: Peer;
showAddGroupButton?: boolean;
};

export default function GroupsRow({
Expand All @@ -37,6 +40,7 @@ export default function GroupsRow({
label = "Assigned Groups",
description = "Use groups to control what this peer can access",
peer,
showAddGroupButton = false,
}: Props) {
const { groups: allGroups } = useGroups();
const { isUser } = useLoggedInUser();
Expand All @@ -59,7 +63,14 @@ export default function GroupsRow({
setModal && !isUser && setModal(true);
}}
>
<MultipleGroups groups={foundGroups} label={label} />
{foundGroups?.length == 0 && showAddGroupButton ? (
<Badge variant={"gray"} useHover={true}>
<IconCirclePlus size={14} />
Add Groups
</Badge>
) : (
<MultipleGroups groups={foundGroups} label={label} />
)}
</ModalTrigger>
<EditGroupsModal
groups={foundGroups}
Expand Down
Loading

0 comments on commit 5caeab1

Please sign in to comment.