mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
525fb11b25 | ||
|
|
5a5ee14a0e | ||
|
|
994b526b9b | ||
|
|
b5922f4fb4 | ||
|
|
42bdaaf82d | ||
|
|
1d75c9a35c | ||
|
|
c7775ade8c |
No files matched your search
+1
-2
@@ -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"
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -297,29 +297,12 @@ function PeerOverview() {
|
||||
/>
|
||||
</FullTooltip>
|
||||
|
||||
<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}
|
||||
>
|
||||
{!isUser && (
|
||||
<div>
|
||||
<Label>Assigned Groups</Label>
|
||||
<HelpText>
|
||||
Use groups to control what this peer can access.
|
||||
</HelpText>
|
||||
<PeerGroupSelector
|
||||
disabled={isUser}
|
||||
onChange={setSelectedGroups}
|
||||
@@ -327,8 +310,8 @@ function PeerOverview() {
|
||||
hideAllGroup={true}
|
||||
peer={peer}
|
||||
/>
|
||||
</FullTooltip>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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} />}
|
||||
|
||||
@@ -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>
|
||||
@@ -206,7 +201,6 @@ function UserOverview({ user, initialGroups }: Readonly<Props>) {
|
||||
onChange={setSelectedGroups}
|
||||
values={selectedGroups}
|
||||
hideAllGroup={true}
|
||||
dataCy={"user-group-selector"}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -250,10 +244,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 +293,6 @@ function UserInformationCard({ user }: { user: User }) {
|
||||
)}
|
||||
|
||||
<Card.ListItem
|
||||
tooltip={false}
|
||||
label={
|
||||
<>
|
||||
<GalleryHorizontalEnd size={16} />
|
||||
|
||||
@@ -10,14 +10,12 @@ 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",
|
||||
);
|
||||
@@ -62,7 +60,7 @@ export default function TeamUsers() {
|
||||
<Suspense fallback={<SkeletonTable />}>
|
||||
<UsersTable
|
||||
users={users}
|
||||
isLoading={isLoading || isGroupsLoading}
|
||||
isLoading={isLoading}
|
||||
headingTarget={portalTarget}
|
||||
/>
|
||||
</Suspense>
|
||||
|
||||
@@ -13,6 +13,7 @@ function NetBirdIcon({ size = 16, className }: Props) {
|
||||
src={NetBirdLogo}
|
||||
alt={"Netbird Icon"}
|
||||
width={size}
|
||||
priority={true}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -20,7 +20,7 @@ export const DropdownInput = forwardRef<HTMLInputElement, Props>(
|
||||
"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",
|
||||
"bg-transparent text-sm outline-none focus-visible:outline-none ring-0 focus-visible:ring-0",
|
||||
"dark:placeholder:text-nb-gray-400 font-light placeholder:text-neutral-500 pl-10",
|
||||
"dark:placeholder:text-nb-gray-400 font-light placeholder:text-neutral-500 pl-10 select-none",
|
||||
)}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
|
||||
@@ -11,7 +11,7 @@ import useSortedDropdownOptions from "@hooks/useSortedDropdownOptions";
|
||||
import { IconArrowBack } from "@tabler/icons-react";
|
||||
import { cn } from "@utils/helpers";
|
||||
import { Command, CommandGroup, CommandInput, CommandList } from "cmdk";
|
||||
import { sortBy, trim, unionBy } from "lodash";
|
||||
import { sortBy, trim, unionBy, uniqBy } from "lodash";
|
||||
import {
|
||||
ChevronsUpDown,
|
||||
FolderGit2,
|
||||
@@ -55,8 +55,13 @@ export function PeerGroupSelector({
|
||||
disabledGroups,
|
||||
dataCy = "group-selector-dropdown",
|
||||
}: Readonly<MultiSelectProps>) {
|
||||
const { groups, dropdownOptions, setDropdownOptions, addDropdownOptions } =
|
||||
useGroups();
|
||||
const {
|
||||
groups,
|
||||
dropdownOptions,
|
||||
setDropdownOptions,
|
||||
addDropdownOptions,
|
||||
updateGroupDropdown,
|
||||
} = useGroups();
|
||||
const searchRef = React.useRef<HTMLInputElement>(null);
|
||||
const [inputRef, { width }] = useElementSize<HTMLButtonElement>();
|
||||
const [search, setSearch] = useState("");
|
||||
@@ -93,10 +98,18 @@ export function PeerGroupSelector({
|
||||
const selectGroup = (name: string) => {
|
||||
const group = groups?.find((group) => group.name == name);
|
||||
const option = dropdownOptions.find((option) => option.name == name);
|
||||
const groupPeers: GroupPeer[] | undefined =
|
||||
(group?.peers as GroupPeer[]) || [];
|
||||
let groupPeers: GroupPeer[] = [...((group?.peers as GroupPeer[]) || [])];
|
||||
|
||||
if (peer) groupPeers?.push({ id: peer?.id as string, name: peer?.name });
|
||||
if (peer) {
|
||||
groupPeers.push({ id: peer.id as string, name: peer.name });
|
||||
groupPeers = uniqBy(groupPeers, "id");
|
||||
updateGroupDropdown(name, {
|
||||
...group,
|
||||
name: name,
|
||||
peers: groupPeers,
|
||||
peers_count: groupPeers.length,
|
||||
});
|
||||
}
|
||||
|
||||
if (!group && !option) {
|
||||
addDropdownOptions([{ name: name, peers: groupPeers }]);
|
||||
@@ -117,6 +130,18 @@ export function PeerGroupSelector({
|
||||
// Remove group from the groupOptions if it does not have an id
|
||||
const deselectGroup = (name: string) => {
|
||||
onChange((previous) => {
|
||||
if (peer) {
|
||||
const option = dropdownOptions?.find((group) => group.name === name);
|
||||
const groupPeers: GroupPeer[] = [
|
||||
...((option?.peers as GroupPeer[]) || []),
|
||||
].filter((p) => p?.id !== peer?.id);
|
||||
updateGroupDropdown(name, {
|
||||
...option,
|
||||
name,
|
||||
peers: groupPeers,
|
||||
peers_count: groupPeers.length,
|
||||
});
|
||||
}
|
||||
return previous.filter((group) => group.name != name);
|
||||
});
|
||||
};
|
||||
@@ -162,6 +187,7 @@ export function PeerGroupSelector({
|
||||
dropdownOptions,
|
||||
values,
|
||||
open,
|
||||
hideAllGroup,
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -241,7 +267,7 @@ export function PeerGroupSelector({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={"pl-2"} data-cy={"group-selector-open-close"}>
|
||||
<div className={"pl-2"}>
|
||||
<ChevronsUpDown
|
||||
size={18}
|
||||
className={"shrink-0 group-hover:text-nb-gray-300 transition-all"}
|
||||
@@ -276,7 +302,7 @@ export function PeerGroupSelector({
|
||||
"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",
|
||||
"bg-transparent text-sm outline-none focus-visible:outline-none ring-0 focus-visible:ring-0",
|
||||
"dark:placeholder:text-nb-gray-400 font-light placeholder:text-neutral-500 pl-10",
|
||||
"dark:placeholder:text-nb-gray-400 font-light placeholder:text-neutral-500 pl-10 select-none",
|
||||
)}
|
||||
ref={searchRef}
|
||||
value={search}
|
||||
@@ -311,10 +337,7 @@ export function PeerGroupSelector({
|
||||
|
||||
<CommandGroup>
|
||||
<ScrollArea
|
||||
className={cn(
|
||||
"max-h-[195px] flex flex-col gap-1 pl-2 py-2 pr-3",
|
||||
sortedDropdownOptions.length == 0 && !search && "py-0",
|
||||
)}
|
||||
className={"max-h-[195px] flex flex-col gap-1 pl-2 py-2 pr-3"}
|
||||
>
|
||||
{searchedGroupNotFound && (
|
||||
<CommandItem
|
||||
@@ -387,7 +410,7 @@ export function PeerGroupSelector({
|
||||
|
||||
<div
|
||||
className={
|
||||
"text-neutral-500 dark:text-nb-gray-300 font-medium flex items-center gap-2"
|
||||
"text-neutral-500 dark:text-nb-gray-300 font-medium flex items-center gap-2 select-none"
|
||||
}
|
||||
>
|
||||
{peerIcon}
|
||||
|
||||
@@ -166,7 +166,7 @@ export function PeerSelector({
|
||||
placeholder={"Search for peers by name or ip..."}
|
||||
/>
|
||||
|
||||
{unfilteredItems.length == 0 && (
|
||||
{unfilteredItems.length == 0 && search == "" && (
|
||||
<DropdownInfoText>
|
||||
{
|
||||
"Seems like you don't have any linux peers to assign as a routing peer."
|
||||
@@ -174,7 +174,7 @@ export function PeerSelector({
|
||||
</DropdownInfoText>
|
||||
)}
|
||||
|
||||
{filteredItems.length == 0 && (
|
||||
{filteredItems.length == 0 && search != "" && (
|
||||
<DropdownInfoText>
|
||||
There are no peers matching your search.
|
||||
</DropdownInfoText>
|
||||
|
||||
@@ -4,7 +4,7 @@ export const GradientFadedBackground = () => {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
"h-full w-full absolute left-0 top-0 rounded-md overflow-hidden z-0 pointer-events-none"
|
||||
"h-full w-full absolute left-0 top-0 rounded-md overflow-hidden z-0"
|
||||
}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -27,7 +27,6 @@ export default function GroupBadge({
|
||||
<Badge
|
||||
key={group.id || group.name}
|
||||
useHover={true}
|
||||
data-cy={"group-badge"}
|
||||
variant={"gray-ghost"}
|
||||
className={cn("transition-all group whitespace-nowrap", className)}
|
||||
onClick={(e) => {
|
||||
|
||||
@@ -34,10 +34,7 @@ export default function MultipleGroups({
|
||||
<TooltipProvider disableHoverableContent={false}>
|
||||
<Tooltip delayDuration={1}>
|
||||
<TooltipTrigger asChild={true}>
|
||||
<div
|
||||
className={"inline-flex items-center gap-2 z-0"}
|
||||
data-cy={"multiple-groups"}
|
||||
>
|
||||
<div className={"inline-flex items-center gap-2 z-0"}>
|
||||
{firstGroup && <GroupBadge group={firstGroup} />}
|
||||
{otherGroups && otherGroups.length > 0 && (
|
||||
<Badge
|
||||
|
||||
@@ -34,8 +34,9 @@ export default function UserDropdown() {
|
||||
};
|
||||
|
||||
useHotkeys("shift+mod+l", () => logout(), []);
|
||||
const { isOwnerOrAdmin, permission, isBillingAdmin } = useLoggedInUser();
|
||||
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const { permission } = useLoggedInUser();
|
||||
|
||||
return (
|
||||
<DropdownMenu
|
||||
|
||||
@@ -24,17 +24,29 @@ const GroupContext = React.createContext(
|
||||
);
|
||||
|
||||
export default function GroupsProvider({ children }: Props) {
|
||||
const { permission } = useLoggedInUser();
|
||||
const { permission, isUser } = useLoggedInUser();
|
||||
|
||||
return permission.dashboard_view == "blocked" ? (
|
||||
<>{children}</>
|
||||
) : (
|
||||
<GroupsProviderContent>{children}</GroupsProviderContent>
|
||||
<GroupsProviderContent isUser={isUser}>{children}</GroupsProviderContent>
|
||||
);
|
||||
}
|
||||
|
||||
export function GroupsProviderContent({ children }: Props) {
|
||||
const { data: groups, mutate, isLoading } = useFetchApi<Group[]>("/groups");
|
||||
type ProviderContentProps = {
|
||||
children: React.ReactNode;
|
||||
isUser: boolean;
|
||||
};
|
||||
|
||||
export function GroupsProviderContent({
|
||||
children,
|
||||
isUser,
|
||||
}: Readonly<ProviderContentProps>) {
|
||||
const {
|
||||
data: groups,
|
||||
mutate,
|
||||
isLoading,
|
||||
} = useFetchApi<Group[]>("/groups", false, true, !isUser);
|
||||
const groupRequest = useApiCall<Group>("/groups", true);
|
||||
const [dropdownOptions, setDropdownOptions] = useState<Group[]>([]);
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ const RoutesContext = React.createContext(
|
||||
toUpdate: Partial<Route>,
|
||||
onSuccess?: (route: Route) => void,
|
||||
message?: string,
|
||||
options?: { remove_access_control_groups?: boolean },
|
||||
) => void;
|
||||
},
|
||||
);
|
||||
@@ -34,7 +33,6 @@ export default function RoutesProvider({ children }: Readonly<Props>) {
|
||||
toUpdate: Partial<Route>,
|
||||
onSuccess?: (route: Route) => void,
|
||||
message?: string,
|
||||
options?: { remove_access_control_groups?: boolean },
|
||||
) => {
|
||||
const hasDomains = route.domains ? route.domains.length > 0 : false;
|
||||
|
||||
@@ -56,11 +54,10 @@ export default function RoutesProvider({ children }: Readonly<Props>) {
|
||||
metric: toUpdate.metric ?? route.metric ?? 9999,
|
||||
masquerade: toUpdate.masquerade ?? route.masquerade ?? true,
|
||||
groups: toUpdate.groups ?? route.groups ?? [],
|
||||
access_control_groups: options?.remove_access_control_groups
|
||||
? undefined
|
||||
: toUpdate.access_control_groups ??
|
||||
route.access_control_groups ??
|
||||
undefined,
|
||||
access_control_groups:
|
||||
toUpdate.access_control_groups ??
|
||||
route.access_control_groups ??
|
||||
undefined,
|
||||
},
|
||||
`/${route.id}`,
|
||||
)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import FullScreenLoading from "@components/ui/FullScreenLoading";
|
||||
import useFetchApi from "@utils/api";
|
||||
import { isNetBirdHosted } from "@utils/netbird";
|
||||
import React, { useMemo } from "react";
|
||||
import { Permission } from "@/interfaces/Permission";
|
||||
import { Role, User } from "@/interfaces/User";
|
||||
import { User } from "@/interfaces/User";
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
@@ -41,13 +40,8 @@ export const useUsers = () => React.useContext(UsersContext);
|
||||
|
||||
export const useLoggedInUser = () => {
|
||||
const { loggedInUser } = useUsers();
|
||||
const isOwner = loggedInUser ? loggedInUser?.role === Role.Owner : false;
|
||||
const isAdmin = loggedInUser ? loggedInUser?.role === Role.Admin : false;
|
||||
const isBillingAdmin = isNetBirdHosted()
|
||||
? loggedInUser
|
||||
? loggedInUser?.role === Role.BillingAdmin
|
||||
: false
|
||||
: false;
|
||||
const isOwner = loggedInUser ? loggedInUser?.role === "owner" : false;
|
||||
const isAdmin = loggedInUser ? loggedInUser?.role === "admin" : false;
|
||||
const isUser = !isOwner && !isAdmin;
|
||||
const isOwnerOrAdmin = isOwner || isAdmin;
|
||||
|
||||
@@ -62,7 +56,6 @@ export const useLoggedInUser = () => {
|
||||
isOwner,
|
||||
isAdmin,
|
||||
isUser,
|
||||
isBillingAdmin,
|
||||
isOwnerOrAdmin,
|
||||
permission,
|
||||
} as const;
|
||||
|
||||
@@ -5,6 +5,7 @@ const useSortedDropdownOptions = (
|
||||
dropdownOptions: Group[],
|
||||
values: Group[],
|
||||
isPopupOpen: boolean,
|
||||
hideAllGroupOption = false,
|
||||
): Group[] => {
|
||||
const sortOrderRef = useRef<Map<string, number>>(new Map());
|
||||
const prevValuesRef = useRef<Group[]>([]);
|
||||
@@ -25,12 +26,15 @@ const useSortedDropdownOptions = (
|
||||
// Sort the dropdown options based on the current sort order
|
||||
return useMemo(() => {
|
||||
const sortOrder = sortOrderRef.current;
|
||||
return [...dropdownOptions].sort((a, b) => {
|
||||
const indexA = sortOrder.get(a.name) ?? Infinity;
|
||||
const indexB = sortOrder.get(b.name) ?? Infinity;
|
||||
return indexA - indexB;
|
||||
});
|
||||
}, [dropdownOptions, sortOrderRef.current]);
|
||||
return [...dropdownOptions]
|
||||
.sort((a, b) => {
|
||||
const indexA = sortOrder.get(a.name) ?? Infinity;
|
||||
const indexB = sortOrder.get(b.name) ?? Infinity;
|
||||
if (a.name === "All") return -1; // Move "All" to the top
|
||||
return indexA - indexB;
|
||||
})
|
||||
.filter((group) => !hideAllGroupOption || group.name !== "All");
|
||||
}, [dropdownOptions, sortOrderRef.current, hideAllGroupOption]);
|
||||
};
|
||||
|
||||
export default useSortedDropdownOptions;
|
||||
@@ -35,5 +35,4 @@ export interface GroupedRoute {
|
||||
description?: string;
|
||||
description_search?: string;
|
||||
domain_search?: string;
|
||||
routes_search?: string;
|
||||
}
|
||||
@@ -18,5 +18,4 @@ export enum Role {
|
||||
User = "user",
|
||||
Admin = "admin",
|
||||
Owner = "owner",
|
||||
BillingAdmin = "billing_admin",
|
||||
}
|
||||
Loaded 20 of 43 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user