Compare commits

..
Author SHA1 Message Date
Eduard Gert 0e553f3c83 Update redirect logic, fix exit node dropdown, fix route search 2024-06-12 16:58:10 +02:00
164 changed files with 1924 additions and 6564 deletions

No files matched your search

+1 -2
View File
@@ -12,5 +12,4 @@ jobs:
uses: codespell-project/actions-codespell@v2
with:
only_warn: 1
skip: package-lock.json,*.svg
ignore_words_list: mappin, allTime
skip: package-lock.json,*.svg
+1 -2
View File
@@ -12,6 +12,5 @@
"tokenSource": "$NETBIRD_TOKEN_SOURCE",
"dragQueryParams": "$NETBIRD_DRAG_QUERY_PARAMS",
"hotjarTrackID": "$NETBIRD_HOTJAR_TRACK_ID",
"googleAnalyticsID": "$NETBIRD_GOOGLE_ANALYTICS_ID",
"authServiceUrl": "$NETBIRD_AUTH_SERVICE_URL"
"googleAnalyticsID": "$NETBIRD_GOOGLE_ANALYTICS_ID"
}
+95 -439
View File
File diff suppressed because it is too large. Load diff
+3 -6
View File
@@ -13,7 +13,7 @@
"cypress:open": "cypress open"
},
"dependencies": {
"@axa-fr/react-oidc": "^7.22.18",
"@axa-fr/react-oidc": "^5.14.0",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3",
@@ -22,9 +22,8 @@
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
@@ -38,7 +37,6 @@
"@types/node": "20.10.6",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-window": "^1.8.8",
"autoprefixer": "^10",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
@@ -55,7 +53,7 @@
"framer-motion": "^10.16.4",
"ip-cidr": "^3.1.0",
"lodash": "^4.17.21",
"lucide-react": "^0.383.0",
"lucide-react": "^0.287.0",
"next": "13.5.5",
"next-themes": "^0.2.1",
"punycode": "^2.3.1",
@@ -69,7 +67,6 @@
"react-jwt": "^1.2.0",
"react-loading-skeleton": "^3.3.1",
"react-responsive": "^9.0.2",
"react-virtuoso": "^4.9.0",
"swr": "^2.2.4",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
+7 -10
View File
@@ -5,7 +5,6 @@ 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, { lazy, Suspense } from "react";
@@ -21,9 +20,6 @@ const AccessControlTable = lazy(
export default function AccessControlPage() {
const { data: policies, isLoading } = useFetchApi<Policy[]>("/policies");
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
return (
<PageContainer>
<GroupsProvider>
@@ -35,7 +31,12 @@ export default function AccessControlPage() {
icon={<AccessControlIcon size={14} />}
/>
</Breadcrumbs>
<h1 ref={headingRef}>Access Control Policies</h1>
<h1>
{policies && policies.length > 1
? `${policies.length} Access Control Policies`
: "Access Control Policies"}
</h1>
<Paragraph>
Create rules to manage access in your network and define what peers
can connect.
@@ -56,11 +57,7 @@ export default function AccessControlPage() {
<RestrictedAccess page={"Access Control"}>
<PoliciesProvider>
<Suspense fallback={<SkeletonTable />}>
<AccessControlTable
isLoading={isLoading}
policies={policies}
headingTarget={portalTarget}
/>
<AccessControlTable isLoading={isLoading} policies={policies} />
</Suspense>
</PoliciesProvider>
</RestrictedAccess>
+6 -10
View File
@@ -4,7 +4,6 @@ import Breadcrumbs from "@components/Breadcrumbs";
import InlineLink from "@components/InlineLink";
import Paragraph from "@components/Paragraph";
import { RestrictedAccess } from "@components/ui/RestrictedAccess";
import { usePortalElement } from "@hooks/usePortalElement";
import useFetchApi from "@utils/api";
import { ExternalLinkIcon } from "lucide-react";
import React from "react";
@@ -16,9 +15,6 @@ import ActivityTable from "@/modules/activity/ActivityTable";
export default function Activity() {
const { data: events, isLoading } = useFetchApi<ActivityEvent[]>("/events");
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
return (
<PageContainer>
<div className={"p-default py-6"}>
@@ -29,7 +25,11 @@ export default function Activity() {
icon={<ActivityIcon size={13} />}
/>
</Breadcrumbs>
<h1 ref={headingRef}>Activity Events</h1>
<h1>
{events && events.length > 1
? `${events.length} Activity Events`
: "Activity Events"}
</h1>
<Paragraph>
Here you can see all the account and network activity events.
</Paragraph>
@@ -48,11 +48,7 @@ export default function Activity() {
</Paragraph>
</div>
<RestrictedAccess page={"Activity"}>
<ActivityTable
events={events}
isLoading={isLoading}
headingTarget={portalTarget}
/>
<ActivityTable events={events} isLoading={isLoading} />
</RestrictedAccess>
</PageContainer>
);
+5 -6
View File
@@ -5,7 +5,6 @@ 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, ServerIcon } from "lucide-react";
import React, { lazy, Suspense } from "react";
@@ -21,9 +20,6 @@ export default function NameServers() {
const { data: nameserverGroups, isLoading } =
useFetchApi<NameserverGroup[]>("/dns/nameservers");
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
return (
<PageContainer>
<div className={"p-default py-6"}>
@@ -40,7 +36,11 @@ export default function NameServers() {
icon={<ServerIcon size={13} />}
/>
</Breadcrumbs>
<h1 ref={headingRef}>Nameservers</h1>
<h1>
{nameserverGroups && nameserverGroups.length > 1
? `${nameserverGroups.length} Nameservers`
: "Nameservers"}
</h1>
<Paragraph>
Add nameservers for domain name resolution in your NetBird network.
</Paragraph>
@@ -62,7 +62,6 @@ export default function NameServers() {
<NameserverGroupTable
nameserverGroups={nameserverGroups}
isLoading={isLoading}
headingTarget={portalTarget}
/>
</Suspense>
</RestrictedAccess>
+7 -22
View File
@@ -14,24 +14,17 @@ import { IconSettings2 } from "@tabler/icons-react";
import useFetchApi, { useApiCall } from "@utils/api";
import { ExternalLinkIcon } from "lucide-react";
import React from "react";
import Skeleton from "react-loading-skeleton";
import { useSWRConfig } from "swr";
import DNSIcon from "@/assets/icons/DNSIcon";
import { useHasChanges } from "@/hooks/useHasChanges";
import { Group } from "@/interfaces/Group";
import { NameserverSettings } from "@/interfaces/NameserverSettings";
import PageContainer from "@/layouts/PageContainer";
import useGroupHelper from "@/modules/groups/useGroupHelper";
import { useGroupIdsToGroups } from "@/modules/groups/useGroupIdsToGroups";
export default function NameServerSettings() {
const { data: settings, isLoading } =
useFetchApi<NameserverSettings>("/dns/settings");
const initialDNSGroups = useGroupIdsToGroups(
settings?.disabled_management_groups,
);
return (
<PageContainer>
<div className={"p-default py-6"}>
@@ -62,16 +55,10 @@ export default function NameServerSettings() {
in our documentation.
</Paragraph>
<RestrictedAccess page={"DNS Settings"}>
{!isLoading && initialDNSGroups !== undefined ? (
<SettingDisabledManagementGroups initialGroups={initialDNSGroups} />
) : (
<div>
<Skeleton
width={"100%"}
className={"mt-8 max-w-xl"}
height={240}
/>
</div>
{!isLoading && (
<SettingDisabledManagementGroups
initial={settings?.disabled_management_groups}
/>
)}
</RestrictedAccess>
</div>
@@ -80,16 +67,16 @@ export default function NameServerSettings() {
}
const SettingDisabledManagementGroups = ({
initialGroups,
initial,
}: {
initialGroups: Group[];
initial: string[] | undefined;
}) => {
const settingRequest = useApiCall<NameserverSettings>("/dns/settings");
const { mutate } = useSWRConfig();
const [selectedGroups, setSelectedGroups, { save: saveGroups }] =
useGroupHelper({
initial: initialGroups,
initial: initial || [],
});
const { hasChanges, updateRef: updateChangesRef } = useHasChanges([
@@ -121,7 +108,6 @@ const SettingDisabledManagementGroups = ({
Peers in these groups will require manual domain name resolution
</HelpText>
<PeerGroupSelector
dataCy={"dns-groups-selector"}
onChange={setSelectedGroups}
values={selectedGroups}
/>
@@ -136,7 +122,6 @@ const SettingDisabledManagementGroups = ({
size={"sm"}
onClick={saveSettings}
disabled={!hasChanges}
data-cy={"save-changes"}
>
Save Changes
</Button>
+5 -6
View File
@@ -5,7 +5,6 @@ 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, { lazy, Suspense } from "react";
@@ -24,9 +23,6 @@ export default function NetworkRoutes() {
const { data: routes, isLoading } = useFetchApi<Route[]>("/routes");
const groupedRoutes = useGroupedRoutes({ routes });
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
return (
<PageContainer>
<RoutesProvider>
@@ -39,7 +35,11 @@ export default function NetworkRoutes() {
icon={<NetworkRoutesIcon size={13} />}
/>
</Breadcrumbs>
<h1 ref={headingRef}>Network Routes</h1>
<h1>
{groupedRoutes && groupedRoutes.length > 1
? `${groupedRoutes.length} Network Routes`
: "Network Routes"}
</h1>
<Paragraph>
Network routes allow you to access other networks like LANs and
VPCs without installing NetBird on every resource.
@@ -65,7 +65,6 @@ export default function NetworkRoutes() {
isLoading={isLoading}
groupedRoutes={groupedRoutes}
routes={routes}
headingTarget={portalTarget}
/>
</Suspense>
</RestrictedAccess>
+30 -17
View File
@@ -23,7 +23,6 @@ import Separator from "@components/Separator";
import FullScreenLoading from "@components/ui/FullScreenLoading";
import LoginExpiredBadge from "@components/ui/LoginExpiredBadge";
import TextWithTooltip from "@components/ui/TextWithTooltip";
import { getOperatingSystem } from "@hooks/useOperatingSystem";
import useRedirect from "@hooks/useRedirect";
import { IconCloudLock, IconInfoCircle } from "@tabler/icons-react";
import useFetchApi from "@utils/api";
@@ -55,12 +54,15 @@ import PeerProvider, { usePeer } from "@/contexts/PeerProvider";
import RoutesProvider from "@/contexts/RoutesProvider";
import { useLoggedInUser } from "@/contexts/UsersProvider";
import { useHasChanges } from "@/hooks/useHasChanges";
import { getOperatingSystem } from "@/hooks/useOperatingSystem";
import { OperatingSystem } from "@/interfaces/OperatingSystem";
import type { Peer } from "@/interfaces/Peer";
import PageContainer from "@/layouts/PageContainer";
import { AddExitNodeButton } from "@/modules/exit-node/AddExitNodeButton";
import { useHasExitNodes } from "@/modules/exit-node/useHasExitNodes";
import useGroupHelper from "@/modules/groups/useGroupHelper";
import { AccessiblePeersSection } from "@/modules/peer/AccessiblePeersSection";
import { PeerNetworkRoutesSection } from "@/modules/peer/PeerNetworkRoutesSection";
import AddRouteDropdownButton from "@/modules/peer/AddRouteDropdownButton";
import PeerRoutesTable from "@/modules/peer/PeerRoutesTable";
export default function PeerPage() {
const queryParameter = useSearchParams();
@@ -70,7 +72,7 @@ export default function PeerPage() {
useRedirect("/peers", false, !peerId);
return peer && !isLoading ? (
<PeerProvider peer={peer} key={peerId}>
<PeerProvider peer={peer}>
<PeerOverview />
</PeerProvider>
) : (
@@ -131,6 +133,7 @@ function PeerOverview() {
};
const { isUser } = useLoggedInUser();
const hasExitNodes = useHasExitNodes(peer);
return (
<PageContainer>
@@ -324,7 +327,6 @@ function PeerOverview() {
disabled={isUser}
onChange={setSelectedGroups}
values={selectedGroups}
hideAllGroup={true}
peer={peer}
/>
</FullTooltip>
@@ -333,19 +335,30 @@ function PeerOverview() {
</div>
</div>
{isLinux && !isUser ? (
<>
<Separator />
<PeerNetworkRoutesSection peer={peer} />
</>
) : null}
<Separator />
{peer?.id && (
<>
<Separator />
<AccessiblePeersSection peerID={peer.id} />
</>
)}
{isLinux && !isUser ? (
<div className={"px-8 py-6"}>
<div className={"max-w-6xl"}>
<div className={"flex justify-between items-center"}>
<div>
<h2>Network Routes</h2>
<Paragraph>
Access other networks without installing NetBird on every
resource.
</Paragraph>
</div>
<div className={"inline-flex gap-4 justify-end"}>
<div className={"gap-4 flex"}>
<AddExitNodeButton peer={peer} firstTime={!hasExitNodes} />
<AddRouteDropdownButton />
</div>
</div>
</div>
<PeerRoutesTable peer={peer} />
</div>
</div>
) : null}
</RoutesProvider>
</PageContainer>
);
+15 -16
View File
@@ -4,12 +4,13 @@ import Breadcrumbs from "@components/Breadcrumbs";
import InlineLink from "@components/InlineLink";
import Paragraph from "@components/Paragraph";
import SkeletonTable from "@components/skeletons/SkeletonTable";
import { usePortalElement } from "@hooks/usePortalElement";
import useFetchApi from "@utils/api";
import { ExternalLinkIcon } from "lucide-react";
import React, { lazy, Suspense } from "react";
import React, { lazy, Suspense, useEffect } from "react";
import PeerIcon from "@/assets/icons/PeerIcon";
import PeersProvider, { usePeers } from "@/contexts/PeersProvider";
import { useGroups } from "@/contexts/GroupsProvider";
import { useLoggedInUser, useUsers } from "@/contexts/UsersProvider";
import { Peer } from "@/interfaces/Peer";
import PageContainer from "@/layouts/PageContainer";
import { SetupModalContent } from "@/modules/setup-netbird-modal/SetupModal";
@@ -20,22 +21,24 @@ export default function Peers() {
return (
<PageContainer>
{permission.dashboard_view === "blocked" ? (
{permission?.dashboard_view === "blocked" ? (
<PeersBlockedView />
) : (
<PeersProvider>
<PeersView />
</PeersProvider>
<PeersView />
)}
</PageContainer>
);
}
function PeersView() {
const { peers, isLoading } = usePeers();
const { data: peers, isLoading } = useFetchApi<Peer[]>("/peers");
const { users } = useUsers();
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
const { refresh } = useGroups();
useEffect(() => {
refresh();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const peersWithUser = peers?.map((peer) => {
if (!users) return peer;
@@ -55,7 +58,7 @@ function PeersView() {
icon={<PeerIcon size={13} />}
/>
</Breadcrumbs>
<h1 ref={headingRef}>Peers</h1>
<h1>{peers && peers.length > 1 ? `${peers.length} Peers` : "Peers"}</h1>
<Paragraph>
A list of all machines and devices connected to your private network.
Use this view to manage peers.
@@ -73,11 +76,7 @@ function PeersView() {
</Paragraph>
</div>
<Suspense fallback={<SkeletonTable />}>
<PeersTable
isLoading={isLoading}
peers={peersWithUser}
headingTarget={portalTarget}
/>
<PeersTable isLoading={isLoading} peers={peersWithUser} />
</Suspense>
</>
);
+6 -10
View File
@@ -5,7 +5,6 @@ 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, ShieldCheck } from "lucide-react";
import React, { lazy, Suspense } from "react";
@@ -22,9 +21,6 @@ export default function PostureChecksPage() {
const { data: postureChecks, isLoading } =
useFetchApi<PostureCheck[]>("/posture-checks");
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
return (
<PageContainer>
<GroupsProvider>
@@ -42,16 +38,17 @@ export default function PostureChecksPage() {
icon={<ShieldCheck size={15} />}
/>
</Breadcrumbs>
<h1 ref={headingRef}>Posture Checks</h1>
<h1>
{postureChecks && postureChecks.length > 1
? `${postureChecks.length} Posture Checks`
: "Posture Checks"}
</h1>
<Paragraph>
Use posture checks to further restrict access in your network.
</Paragraph>
<Paragraph>
Learn more about
<InlineLink
href={"https://docs.netbird.io/how-to/manage-posture-checks"}
target={"_blank"}
>
<InlineLink href={"https://docs.netbird.io/how-to/manage-posture-checks"} target={"_blank"}>
Posture Checks
<ExternalLinkIcon size={12} />
</InlineLink>
@@ -63,7 +60,6 @@ export default function PostureChecksPage() {
<PoliciesProvider>
<Suspense fallback={<SkeletonTable />}>
<PostureCheckTable
headingTarget={portalTarget}
isLoading={isLoading}
postureChecks={postureChecks}
/>
+1 -5
View File
@@ -11,7 +11,6 @@ import {
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";
@@ -53,10 +52,7 @@ export default function NetBirdSettings() {
Danger zone
</VerticalTabs.Trigger>
</VerticalTabs.List>
<RestrictedAccess
page={"Settings"}
allow={[Role.Admin, Role.Owner, Role.BillingAdmin]}
>
<RestrictedAccess page={"Settings"}>
<div className={"border-l border-nb-gray-930 w-full"}>
{account && <AuthenticationTab account={account} />}
{account && <PermissionsTab account={account} />}
+5 -6
View File
@@ -5,7 +5,6 @@ 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, { lazy, Suspense, useMemo } from "react";
@@ -39,9 +38,6 @@ export default function SetupKeys() {
});
}, [setupKeys, groups]);
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
return (
<PageContainer>
<div className={"p-default py-6"}>
@@ -52,7 +48,11 @@ export default function SetupKeys() {
icon={<SetupKeysIcon size={13} />}
/>
</Breadcrumbs>
<h1 ref={headingRef}>Setup Keys</h1>
<h1>
{setupKeys && setupKeys.length > 1
? `${setupKeys.length} Setup Keys`
: "Setup Keys"}
</h1>
<Paragraph>
Setup keys are pre-authentication keys that allow to register new
machines in your network.
@@ -74,7 +74,6 @@ export default function SetupKeys() {
<RestrictedAccess page={"Setup Keys"}>
<Suspense fallback={<SkeletonTable />}>
<SetupKeysTable
headingTarget={portalTarget}
setupKeys={setupKeysWithGroups}
isLoading={isLoading}
/>
@@ -5,7 +5,6 @@ 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 { IconSettings2 } from "@tabler/icons-react";
import useFetchApi from "@utils/api";
import { ExternalLinkIcon } from "lucide-react";
@@ -23,9 +22,6 @@ export default function ServiceUsers() {
"/users?service_user=true",
);
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
return (
<PageContainer>
<div className={"p-default py-6"}>
@@ -42,7 +38,11 @@ export default function ServiceUsers() {
icon={<IconSettings2 size={17} />}
/>
</Breadcrumbs>
<h1 ref={headingRef}>Service Users</h1>
<h1>
{users && users.length > 1
? `${users.length} Service Users`
: "Service Users"}
</h1>
<Paragraph>
Use service users to create API tokens and avoid losing automated
access.
@@ -61,11 +61,7 @@ export default function ServiceUsers() {
</div>
<RestrictedAccess page={"Service Users"}>
<Suspense fallback={<SkeletonTable />}>
<ServiceUsersTable
users={users}
isLoading={isLoading}
headingTarget={portalTarget}
/>
<ServiceUsersTable users={users} isLoading={isLoading} />
</Suspense>
</RestrictedAccess>
</PageContainer>
+15 -31
View File
@@ -22,13 +22,11 @@ import { useSWRConfig } from "swr";
import TeamIcon from "@/assets/icons/TeamIcon";
import { useLoggedInUser } from "@/contexts/UsersProvider";
import { useHasChanges } from "@/hooks/useHasChanges";
import { Group } from "@/interfaces/Group";
import { Role, User } from "@/interfaces/User";
import PageContainer from "@/layouts/PageContainer";
import AccessTokensTable from "@/modules/access-tokens/AccessTokensTable";
import CreateAccessTokenModal from "@/modules/access-tokens/CreateAccessTokenModal";
import useGroupHelper from "@/modules/groups/useGroupHelper";
import { useGroupIdsToGroups } from "@/modules/groups/useGroupIdsToGroups";
import UserBlockCell from "@/modules/users/table-cells/UserBlockCell";
import UserStatusCell from "@/modules/users/table-cells/UserStatusCell";
import { UserRoleSelector } from "@/modules/users/UserRoleSelector";
@@ -47,10 +45,8 @@ export default function UserPage() {
useRedirect("/team/users", false, !userId);
const userGroups = useGroupIdsToGroups(user?.auto_groups);
return !isLoading && user && userGroups !== undefined ? (
<UserOverview user={user} initialGroups={userGroups} />
return !isLoading && user ? (
<UserOverview user={user} />
) : (
<FullScreenLoading />
);
@@ -58,16 +54,16 @@ export default function UserPage() {
type Props = {
user: User;
initialGroups: Group[];
};
function UserOverview({ user, initialGroups }: Readonly<Props>) {
function UserOverview({ user }: Props) {
const router = useRouter();
const userRequest = useApiCall<User>("/users");
const { mutate } = useSWRConfig();
const { loggedInUser, isOwnerOrAdmin, isUser } = useLoggedInUser();
const isLoggedInUser = loggedInUser ? loggedInUser?.id === user.id : false;
const initialGroups = user.auto_groups;
const [selectedGroups, setSelectedGroups, { save: saveGroups }] =
useGroupHelper({
initial: initialGroups,
@@ -184,7 +180,6 @@ function UserOverview({ user, initialGroups }: Readonly<Props>) {
className={"w-full"}
disabled={!hasChanges}
onClick={save}
data-cy={"save-changes"}
>
Save Changes
</Button>
@@ -192,7 +187,7 @@ function UserOverview({ user, initialGroups }: Readonly<Props>) {
)}
</div>
<div className={"flex gap-10 w-full mt-8 max-w-6xl items-start"}>
<div className={"flex gap-10 w-full mt-8 max-w-6xl"}>
<UserInformationCard user={user} />
<div className={"flex flex-col gap-8 w-1/2 "}>
{!user.is_service_user && (
@@ -205,8 +200,6 @@ function UserOverview({ user, initialGroups }: Readonly<Props>) {
disabled={isUser}
onChange={setSelectedGroups}
values={selectedGroups}
hideAllGroup={true}
dataCy={"user-group-selector"}
/>
</div>
)}
@@ -221,8 +214,6 @@ function UserOverview({ user, initialGroups }: Readonly<Props>) {
<UserRoleSelector
value={role}
onChange={setRole}
hideOwner={user.is_service_user}
currentUser={user}
disabled={
isLoggedInUser ||
!isOwnerOrAdmin ||
@@ -250,10 +241,7 @@ function UserOverview({ user, initialGroups }: Readonly<Props>) {
<div className={"inline-flex gap-4 justify-end"}>
<div>
<CreateAccessTokenModal user={user}>
<Button
variant={"primary"}
data-cy={"access-token-open-modal"}
>
<Button variant={"primary"}>
<IconCirclePlus size={16} />
Create Access Token
</Button>
@@ -302,7 +290,6 @@ function UserInformationCard({ user }: { user: User }) {
)}
<Card.ListItem
tooltip={false}
label={
<>
<GalleryHorizontalEnd size={16} />
@@ -314,18 +301,15 @@ function UserInformationCard({ user }: { user: User }) {
{!isServiceUser && (
<>
{!user.is_current && user.role != Role.Owner && (
<Card.ListItem
label={
<>
<Ban size={16} />
Block User
</>
}
value={<UserBlockCell user={user} isUserPage={true} />}
/>
)}
<Card.ListItem
label={
<>
<Ban size={16} />
Block User
</>
}
value={<UserBlockCell user={user} isUserPage={true} />}
/>
<Card.ListItem
label={
<>
+2 -12
View File
@@ -5,26 +5,20 @@ 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, User2 } from "lucide-react";
import React, { lazy, Suspense } from "react";
import TeamIcon from "@/assets/icons/TeamIcon";
import { useGroups } from "@/contexts/GroupsProvider";
import { User } from "@/interfaces/User";
import PageContainer from "@/layouts/PageContainer";
const UsersTable = lazy(() => import("@/modules/users/UsersTable"));
export default function TeamUsers() {
const { isLoading: isGroupsLoading } = useGroups();
const { data: users, isLoading } = useFetchApi<User[]>(
"/users?service_user=false",
);
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
return (
<PageContainer>
<div className={"p-default py-6"}>
@@ -41,7 +35,7 @@ export default function TeamUsers() {
icon={<User2 size={16} />}
/>
</Breadcrumbs>
<h1 ref={headingRef}>Users</h1>
<h1>{users && users.length > 1 ? `${users.length} Users` : "Users"}</h1>
<Paragraph>
Manage users and their permissions. Same-domain email users are added
automatically on first sign-in.
@@ -60,11 +54,7 @@ export default function TeamUsers() {
</div>
<RestrictedAccess page={"Users"}>
<Suspense fallback={<SkeletonTable />}>
<UsersTable
users={users}
isLoading={isLoading || isGroupsLoading}
headingTarget={portalTarget}
/>
<UsersTable users={users} isLoading={isLoading} />
</Suspense>
</RestrictedAccess>
</PageContainer>
-5
View File
@@ -68,9 +68,4 @@ p {
.stepper-bg-variant .step-circle {
@apply !border-[#1d2024];
}
.webkit-scroll{
-webkit-overflow-scrolling: touch;
-webkit-transform: translate3d(0, 0, 0);
}
+2 -10
View File
@@ -5,17 +5,9 @@ import NetBirdLogo from "@/assets/netbird.svg";
type Props = {
size?: number;
className?: string;
};
function NetBirdIcon({ size = 16, className }: Props) {
return (
<Image
src={NetBirdLogo}
alt={"Netbird Icon"}
width={size}
className={className}
/>
);
function NetBirdIcon({ size = 16 }: Props) {
return <Image src={NetBirdLogo} alt={"Netbird Icon"} width={size} />;
}
export default memo(NetBirdIcon);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Loaded 20 of 164 files, more files were not shown because too many files have changed in this diff. Show more