Skip to content

Commit 76ef50a

Browse files
authored
Add Access Control Groups & various UI / UX improvements (#415)
* Update codespell * Add access control group, add various ui / ux improvements
1 parent 58cec8f commit 76ef50a

File tree

92 files changed

+3409
-918
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+3409
-918
lines changed

.github/workflows/codespell.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ jobs:
1313
with:
1414
only_warn: 1
1515
skip: package-lock.json,*.svg
16-
ignore_words_list: mappin
16+
ignore_words_list: mappin, allTime

src/app/(dashboard)/access-control/page.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import InlineLink from "@components/InlineLink";
55
import Paragraph from "@components/Paragraph";
66
import SkeletonTable from "@components/skeletons/SkeletonTable";
77
import { RestrictedAccess } from "@components/ui/RestrictedAccess";
8+
import { usePortalElement } from "@hooks/usePortalElement";
89
import useFetchApi from "@utils/api";
910
import { ExternalLinkIcon } from "lucide-react";
1011
import React, { lazy, Suspense } from "react";
@@ -20,6 +21,9 @@ const AccessControlTable = lazy(
2021
export default function AccessControlPage() {
2122
const { data: policies, isLoading } = useFetchApi<Policy[]>("/policies");
2223

24+
const { ref: headingRef, portalTarget } =
25+
usePortalElement<HTMLHeadingElement>();
26+
2327
return (
2428
<PageContainer>
2529
<GroupsProvider>
@@ -31,12 +35,7 @@ export default function AccessControlPage() {
3135
icon={<AccessControlIcon size={14} />}
3236
/>
3337
</Breadcrumbs>
34-
35-
<h1>
36-
{policies && policies.length > 1
37-
? `${policies.length} Access Control Policies`
38-
: "Access Control Policies"}
39-
</h1>
38+
<h1 ref={headingRef}>Access Control Policies</h1>
4039
<Paragraph>
4140
Create rules to manage access in your network and define what peers
4241
can connect.
@@ -57,7 +56,11 @@ export default function AccessControlPage() {
5756
<RestrictedAccess page={"Access Control"}>
5857
<PoliciesProvider>
5958
<Suspense fallback={<SkeletonTable />}>
60-
<AccessControlTable isLoading={isLoading} policies={policies} />
59+
<AccessControlTable
60+
isLoading={isLoading}
61+
policies={policies}
62+
headingTarget={portalTarget}
63+
/>
6164
</Suspense>
6265
</PoliciesProvider>
6366
</RestrictedAccess>

src/app/(dashboard)/activity/page.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Breadcrumbs from "@components/Breadcrumbs";
44
import InlineLink from "@components/InlineLink";
55
import Paragraph from "@components/Paragraph";
66
import { RestrictedAccess } from "@components/ui/RestrictedAccess";
7+
import { usePortalElement } from "@hooks/usePortalElement";
78
import useFetchApi from "@utils/api";
89
import { ExternalLinkIcon } from "lucide-react";
910
import React from "react";
@@ -15,6 +16,9 @@ import ActivityTable from "@/modules/activity/ActivityTable";
1516
export default function Activity() {
1617
const { data: events, isLoading } = useFetchApi<ActivityEvent[]>("/events");
1718

19+
const { ref: headingRef, portalTarget } =
20+
usePortalElement<HTMLHeadingElement>();
21+
1822
return (
1923
<PageContainer>
2024
<div className={"p-default py-6"}>
@@ -25,11 +29,7 @@ export default function Activity() {
2529
icon={<ActivityIcon size={13} />}
2630
/>
2731
</Breadcrumbs>
28-
<h1>
29-
{events && events.length > 1
30-
? `${events.length} Activity Events`
31-
: "Activity Events"}
32-
</h1>
32+
<h1 ref={headingRef}>Activity Events</h1>
3333
<Paragraph>
3434
Here you can see all the account and network activity events.
3535
</Paragraph>
@@ -48,7 +48,11 @@ export default function Activity() {
4848
</Paragraph>
4949
</div>
5050
<RestrictedAccess page={"Activity"}>
51-
<ActivityTable events={events} isLoading={isLoading} />
51+
<ActivityTable
52+
events={events}
53+
isLoading={isLoading}
54+
headingTarget={portalTarget}
55+
/>
5256
</RestrictedAccess>
5357
</PageContainer>
5458
);

src/app/(dashboard)/dns/nameservers/page.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import InlineLink from "@components/InlineLink";
55
import Paragraph from "@components/Paragraph";
66
import SkeletonTable from "@components/skeletons/SkeletonTable";
77
import { RestrictedAccess } from "@components/ui/RestrictedAccess";
8+
import { usePortalElement } from "@hooks/usePortalElement";
89
import useFetchApi from "@utils/api";
910
import { ExternalLinkIcon, ServerIcon } from "lucide-react";
1011
import React, { lazy, Suspense } from "react";
@@ -20,6 +21,9 @@ export default function NameServers() {
2021
const { data: nameserverGroups, isLoading } =
2122
useFetchApi<NameserverGroup[]>("/dns/nameservers");
2223

24+
const { ref: headingRef, portalTarget } =
25+
usePortalElement<HTMLHeadingElement>();
26+
2327
return (
2428
<PageContainer>
2529
<div className={"p-default py-6"}>
@@ -36,11 +40,7 @@ export default function NameServers() {
3640
icon={<ServerIcon size={13} />}
3741
/>
3842
</Breadcrumbs>
39-
<h1>
40-
{nameserverGroups && nameserverGroups.length > 1
41-
? `${nameserverGroups.length} Nameservers`
42-
: "Nameservers"}
43-
</h1>
43+
<h1 ref={headingRef}>Nameservers</h1>
4444
<Paragraph>
4545
Add nameservers for domain name resolution in your NetBird network.
4646
</Paragraph>
@@ -62,6 +62,7 @@ export default function NameServers() {
6262
<NameserverGroupTable
6363
nameserverGroups={nameserverGroups}
6464
isLoading={isLoading}
65+
headingTarget={portalTarget}
6566
/>
6667
</Suspense>
6768
</RestrictedAccess>

src/app/(dashboard)/network-routes/page.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import InlineLink from "@components/InlineLink";
55
import Paragraph from "@components/Paragraph";
66
import SkeletonTable from "@components/skeletons/SkeletonTable";
77
import { RestrictedAccess } from "@components/ui/RestrictedAccess";
8+
import { usePortalElement } from "@hooks/usePortalElement";
89
import useFetchApi from "@utils/api";
910
import { ExternalLinkIcon } from "lucide-react";
1011
import React, { lazy, Suspense } from "react";
@@ -23,6 +24,9 @@ export default function NetworkRoutes() {
2324
const { data: routes, isLoading } = useFetchApi<Route[]>("/routes");
2425
const groupedRoutes = useGroupedRoutes({ routes });
2526

27+
const { ref: headingRef, portalTarget } =
28+
usePortalElement<HTMLHeadingElement>();
29+
2630
return (
2731
<PageContainer>
2832
<RoutesProvider>
@@ -35,11 +39,7 @@ export default function NetworkRoutes() {
3539
icon={<NetworkRoutesIcon size={13} />}
3640
/>
3741
</Breadcrumbs>
38-
<h1>
39-
{groupedRoutes && groupedRoutes.length > 1
40-
? `${groupedRoutes.length} Network Routes`
41-
: "Network Routes"}
42-
</h1>
42+
<h1 ref={headingRef}>Network Routes</h1>
4343
<Paragraph>
4444
Network routes allow you to access other networks like LANs and
4545
VPCs without installing NetBird on every resource.
@@ -65,6 +65,7 @@ export default function NetworkRoutes() {
6565
isLoading={isLoading}
6666
groupedRoutes={groupedRoutes}
6767
routes={routes}
68+
headingTarget={portalTarget}
6869
/>
6970
</Suspense>
7071
</RestrictedAccess>

src/app/(dashboard)/peer/page.tsx

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import Separator from "@components/Separator";
2323
import FullScreenLoading from "@components/ui/FullScreenLoading";
2424
import LoginExpiredBadge from "@components/ui/LoginExpiredBadge";
2525
import TextWithTooltip from "@components/ui/TextWithTooltip";
26+
import { getOperatingSystem } from "@hooks/useOperatingSystem";
2627
import useRedirect from "@hooks/useRedirect";
2728
import { IconCloudLock, IconInfoCircle } from "@tabler/icons-react";
2829
import useFetchApi from "@utils/api";
@@ -54,15 +55,12 @@ import PeerProvider, { usePeer } from "@/contexts/PeerProvider";
5455
import RoutesProvider from "@/contexts/RoutesProvider";
5556
import { useLoggedInUser } from "@/contexts/UsersProvider";
5657
import { useHasChanges } from "@/hooks/useHasChanges";
57-
import { getOperatingSystem } from "@/hooks/useOperatingSystem";
5858
import { OperatingSystem } from "@/interfaces/OperatingSystem";
5959
import type { Peer } from "@/interfaces/Peer";
6060
import PageContainer from "@/layouts/PageContainer";
61-
import { AddExitNodeButton } from "@/modules/exit-node/AddExitNodeButton";
62-
import { useHasExitNodes } from "@/modules/exit-node/useHasExitNodes";
6361
import useGroupHelper from "@/modules/groups/useGroupHelper";
64-
import AddRouteDropdownButton from "@/modules/peer/AddRouteDropdownButton";
65-
import PeerRoutesTable from "@/modules/peer/PeerRoutesTable";
62+
import { AccessiblePeersSection } from "@/modules/peer/AccessiblePeersSection";
63+
import { PeerNetworkRoutesSection } from "@/modules/peer/PeerNetworkRoutesSection";
6664

6765
export default function PeerPage() {
6866
const queryParameter = useSearchParams();
@@ -72,7 +70,7 @@ export default function PeerPage() {
7270
useRedirect("/peers", false, !peerId);
7371

7472
return peer && !isLoading ? (
75-
<PeerProvider peer={peer}>
73+
<PeerProvider peer={peer} key={peerId}>
7674
<PeerOverview />
7775
</PeerProvider>
7876
) : (
@@ -133,7 +131,6 @@ function PeerOverview() {
133131
};
134132

135133
const { isUser } = useLoggedInUser();
136-
const hasExitNodes = useHasExitNodes(peer);
137134

138135
return (
139136
<PageContainer>
@@ -336,30 +333,19 @@ function PeerOverview() {
336333
</div>
337334
</div>
338335

339-
<Separator />
340-
341336
{isLinux && !isUser ? (
342-
<div className={"px-8 py-6"}>
343-
<div className={"max-w-6xl"}>
344-
<div className={"flex justify-between items-center"}>
345-
<div>
346-
<h2>Network Routes</h2>
347-
<Paragraph>
348-
Access other networks without installing NetBird on every
349-
resource.
350-
</Paragraph>
351-
</div>
352-
<div className={"inline-flex gap-4 justify-end"}>
353-
<div className={"gap-4 flex"}>
354-
<AddExitNodeButton peer={peer} firstTime={!hasExitNodes} />
355-
<AddRouteDropdownButton />
356-
</div>
357-
</div>
358-
</div>
359-
<PeerRoutesTable peer={peer} />
360-
</div>
361-
</div>
337+
<>
338+
<Separator />
339+
<PeerNetworkRoutesSection peer={peer} />
340+
</>
362341
) : null}
342+
343+
{peer?.id && (
344+
<>
345+
<Separator />
346+
<AccessiblePeersSection peerID={peer.id} />
347+
</>
348+
)}
363349
</RoutesProvider>
364350
</PageContainer>
365351
);

src/app/(dashboard)/posture-checks/page.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import InlineLink from "@components/InlineLink";
55
import Paragraph from "@components/Paragraph";
66
import SkeletonTable from "@components/skeletons/SkeletonTable";
77
import { RestrictedAccess } from "@components/ui/RestrictedAccess";
8+
import { usePortalElement } from "@hooks/usePortalElement";
89
import useFetchApi from "@utils/api";
910
import { ExternalLinkIcon, ShieldCheck } from "lucide-react";
1011
import React, { lazy, Suspense } from "react";
@@ -21,6 +22,9 @@ export default function PostureChecksPage() {
2122
const { data: postureChecks, isLoading } =
2223
useFetchApi<PostureCheck[]>("/posture-checks");
2324

25+
const { ref: headingRef, portalTarget } =
26+
usePortalElement<HTMLHeadingElement>();
27+
2428
return (
2529
<PageContainer>
2630
<GroupsProvider>
@@ -38,17 +42,16 @@ export default function PostureChecksPage() {
3842
icon={<ShieldCheck size={15} />}
3943
/>
4044
</Breadcrumbs>
41-
<h1>
42-
{postureChecks && postureChecks.length > 1
43-
? `${postureChecks.length} Posture Checks`
44-
: "Posture Checks"}
45-
</h1>
45+
<h1 ref={headingRef}>Posture Checks</h1>
4646
<Paragraph>
4747
Use posture checks to further restrict access in your network.
4848
</Paragraph>
4949
<Paragraph>
5050
Learn more about
51-
<InlineLink href={"https://docs.netbird.io/how-to/manage-posture-checks"} target={"_blank"}>
51+
<InlineLink
52+
href={"https://docs.netbird.io/how-to/manage-posture-checks"}
53+
target={"_blank"}
54+
>
5255
Posture Checks
5356
<ExternalLinkIcon size={12} />
5457
</InlineLink>
@@ -60,6 +63,7 @@ export default function PostureChecksPage() {
6063
<PoliciesProvider>
6164
<Suspense fallback={<SkeletonTable />}>
6265
<PostureCheckTable
66+
headingTarget={portalTarget}
6367
isLoading={isLoading}
6468
postureChecks={postureChecks}
6569
/>

src/app/(dashboard)/setup-keys/page.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import InlineLink from "@components/InlineLink";
55
import Paragraph from "@components/Paragraph";
66
import SkeletonTable from "@components/skeletons/SkeletonTable";
77
import { RestrictedAccess } from "@components/ui/RestrictedAccess";
8+
import { usePortalElement } from "@hooks/usePortalElement";
89
import useFetchApi from "@utils/api";
910
import { ExternalLinkIcon } from "lucide-react";
1011
import React, { lazy, Suspense, useMemo } from "react";
@@ -38,6 +39,9 @@ export default function SetupKeys() {
3839
});
3940
}, [setupKeys, groups]);
4041

42+
const { ref: headingRef, portalTarget } =
43+
usePortalElement<HTMLHeadingElement>();
44+
4145
return (
4246
<PageContainer>
4347
<div className={"p-default py-6"}>
@@ -48,11 +52,7 @@ export default function SetupKeys() {
4852
icon={<SetupKeysIcon size={13} />}
4953
/>
5054
</Breadcrumbs>
51-
<h1>
52-
{setupKeys && setupKeys.length > 1
53-
? `${setupKeys.length} Setup Keys`
54-
: "Setup Keys"}
55-
</h1>
55+
<h1 ref={headingRef}>Setup Keys</h1>
5656
<Paragraph>
5757
Setup keys are pre-authentication keys that allow to register new
5858
machines in your network.
@@ -74,6 +74,7 @@ export default function SetupKeys() {
7474
<RestrictedAccess page={"Setup Keys"}>
7575
<Suspense fallback={<SkeletonTable />}>
7676
<SetupKeysTable
77+
headingTarget={portalTarget}
7778
setupKeys={setupKeysWithGroups}
7879
isLoading={isLoading}
7980
/>

src/app/(dashboard)/team/service-users/page.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import InlineLink from "@components/InlineLink";
55
import Paragraph from "@components/Paragraph";
66
import SkeletonTable from "@components/skeletons/SkeletonTable";
77
import { RestrictedAccess } from "@components/ui/RestrictedAccess";
8+
import { usePortalElement } from "@hooks/usePortalElement";
89
import { IconSettings2 } from "@tabler/icons-react";
910
import useFetchApi from "@utils/api";
1011
import { ExternalLinkIcon } from "lucide-react";
@@ -22,6 +23,9 @@ export default function ServiceUsers() {
2223
"/users?service_user=true",
2324
);
2425

26+
const { ref: headingRef, portalTarget } =
27+
usePortalElement<HTMLHeadingElement>();
28+
2529
return (
2630
<PageContainer>
2731
<div className={"p-default py-6"}>
@@ -38,11 +42,7 @@ export default function ServiceUsers() {
3842
icon={<IconSettings2 size={17} />}
3943
/>
4044
</Breadcrumbs>
41-
<h1>
42-
{users && users.length > 1
43-
? `${users.length} Service Users`
44-
: "Service Users"}
45-
</h1>
45+
<h1 ref={headingRef}>Service Users</h1>
4646
<Paragraph>
4747
Use service users to create API tokens and avoid losing automated
4848
access.
@@ -61,7 +61,11 @@ export default function ServiceUsers() {
6161
</div>
6262
<RestrictedAccess page={"Service Users"}>
6363
<Suspense fallback={<SkeletonTable />}>
64-
<ServiceUsersTable users={users} isLoading={isLoading} />
64+
<ServiceUsersTable
65+
users={users}
66+
isLoading={isLoading}
67+
headingTarget={portalTarget}
68+
/>
6569
</Suspense>
6670
</RestrictedAccess>
6771
</PageContainer>

0 commit comments

Comments
 (0)