Merge pull request #4888 from Infisical/ENG-4154

improvement: add invite members link to nav bar to improve discovery
This commit is contained in:
Scott Wilson
2025-11-18 09:25:33 -08:00
committed by GitHub
2 changed files with 85 additions and 16 deletions
@@ -16,15 +16,19 @@ import {
faSignOut, faSignOut,
faToolbox, faToolbox,
faUser, faUser,
faUserCog,
faUserPlus,
faUsers faUsers
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Link, useLocation, useNavigate, useRouter, useRouterState } from "@tanstack/react-router"; import { Link, useLocation, useNavigate, useRouter, useRouterState } from "@tanstack/react-router";
import { UserPlusIcon } from "lucide-react";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { Mfa } from "@app/components/auth/Mfa"; import { Mfa } from "@app/components/auth/Mfa";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
import SecurityClient from "@app/components/utilities/SecurityClient"; import SecurityClient from "@app/components/utilities/SecurityClient";
import { import {
BreadcrumbContainer, BreadcrumbContainer,
@@ -44,7 +48,13 @@ import {
} from "@app/components/v2"; } from "@app/components/v2";
import { Badge, InstanceIcon, OrgIcon, SubOrgIcon } from "@app/components/v3"; import { Badge, InstanceIcon, OrgIcon, SubOrgIcon } from "@app/components/v3";
import { envConfig } from "@app/config/env"; import { envConfig } from "@app/config/env";
import { useOrganization, useSubscription, useUser } from "@app/context"; import {
OrgPermissionActions,
OrgPermissionSubjects,
useOrganization,
useSubscription,
useUser
} from "@app/context";
import { isInfisicalCloud } from "@app/helpers/platform"; import { isInfisicalCloud } from "@app/helpers/platform";
import { useToggle } from "@app/hooks"; import { useToggle } from "@app/hooks";
import { import {
@@ -567,15 +577,36 @@ export const Navbar = () => {
</Button> </Button>
</Tooltip> </Tooltip>
)} )}
{user.superAdmin && !location.pathname.startsWith("/admin") && ( {/* eslint-disable-next-line no-nested-ternary */}
<Link {!location.pathname.startsWith("/admin") ? (
className="mr-2 flex h-[34px] items-center rounded-md border border-mineshaft-500 px-2.5 py-1.5 text-sm whitespace-nowrap text-mineshaft-200 hover:bg-mineshaft-600" user.superAdmin ? (
to="/admin" <Link
> className="mr-2 flex h-[34px] items-center rounded-md border border-mineshaft-500 px-2.5 py-1.5 text-sm whitespace-nowrap text-mineshaft-200 hover:bg-mineshaft-600"
<InstanceIcon className="inline-block size-3.5" /> to="/admin"
<span className="ml-2 hidden md:inline-block">Server Console</span> >
</Link> <InstanceIcon className="inline-block size-3.5" />
)} <span className="ml-2 hidden md:inline-block">Server Console</span>
</Link>
) : (
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Member}>
{(isAllowed) =>
isAllowed ? (
<Link
className="mr-2 flex h-[34px] items-center rounded-md border border-mineshaft-500 px-2.5 py-1.5 text-sm whitespace-nowrap text-mineshaft-200 hover:bg-mineshaft-600"
to="/organization/access-management"
search={{
selectedTab: "members",
action: "invite-members"
}}
>
<UserPlusIcon className="inline-block size-3.5" />
<span className="ml-2 hidden md:inline-block">Invite Members</span>
</Link>
) : null
}
</OrgPermissionCan>
)
) : null}
<DropdownMenu modal={false}> <DropdownMenu modal={false}>
<DropdownMenuTrigger> <DropdownMenuTrigger>
<div className="rounded-l-md border border-r-0 border-mineshaft-500 px-2.5 py-1 hover:bg-mineshaft-600"> <div className="rounded-l-md border border-r-0 border-mineshaft-500 px-2.5 py-1 hover:bg-mineshaft-600">
@@ -657,8 +688,27 @@ export const Navbar = () => {
</div> </div>
</div> </div>
<Link to="/personal-settings"> <Link to="/personal-settings">
<DropdownMenuItem>Personal Settings</DropdownMenuItem> <DropdownMenuItem icon={<FontAwesomeIcon icon={faUserCog} />}>
Personal Settings
</DropdownMenuItem>
</Link> </Link>
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Member}>
{(isAllowed) =>
isAllowed ? (
<Link
to="/organization/access-management"
search={{
selectedTab: "members",
action: "invite-members"
}}
>
<DropdownMenuItem icon={<FontAwesomeIcon icon={faUserPlus} />}>
Invite Members
</DropdownMenuItem>
</Link>
) : null
}
</OrgPermissionCan>
<a <a
href="https://infisical.com/docs/documentation/getting-started/introduction" href="https://infisical.com/docs/documentation/getting-started/introduction"
target="_blank" target="_blank"
@@ -1,7 +1,8 @@
import { useState } from "react"; import { useEffect, useState } from "react";
import { faPlus, faTrash } from "@fortawesome/free-solid-svg-icons"; import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { BanIcon } from "lucide-react"; import { useNavigate, useSearch } from "@tanstack/react-router";
import { BanIcon, UserPlusIcon } from "lucide-react";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal"; import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
@@ -16,6 +17,7 @@ import {
Tooltip Tooltip
} from "@app/components/v2"; } from "@app/components/v2";
import { Badge, DocumentationLinkBadge } from "@app/components/v3"; import { Badge, DocumentationLinkBadge } from "@app/components/v3";
import { ROUTE_PATHS } from "@app/const/routes";
import { import {
OrgPermissionActions, OrgPermissionActions,
OrgPermissionSubjects, OrgPermissionSubjects,
@@ -35,6 +37,7 @@ import { OrgMembersTable } from "./OrgMembersTable";
export const OrgMembersSection = () => { export const OrgMembersSection = () => {
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { currentOrg, isSubOrganization } = useOrganization(); const { currentOrg, isSubOrganization } = useOrganization();
const navigate = useNavigate();
const orgId = currentOrg?.id ?? ""; const orgId = currentOrg?.id ?? "";
const { user } = useUser(); const { user } = useUser();
const userId = user?.id || ""; const userId = user?.id || "";
@@ -55,6 +58,22 @@ export const OrgMembersSection = () => {
const [selectedMemberIds, setSelectedMemberIds] = useState<string[]>([]); const [selectedMemberIds, setSelectedMemberIds] = useState<string[]>([]);
const urlAction = useSearch({
from: ROUTE_PATHS.Organization.AccessControlPage.id,
select: (el) => el.action,
structuralSharing: true
});
useEffect(() => {
if (urlAction === "invite-members") {
handlePopUpOpen("addMember");
navigate({
to: ".",
search: ({ action, ...search }) => search
});
}
}, [urlAction]);
const { mutateAsync: deleteMutateAsync } = useDeleteOrgMembership(); const { mutateAsync: deleteMutateAsync } = useDeleteOrgMembership();
const { mutateAsync: deleteBatchMutateAsync } = useDeleteOrgMembershipBatch(); const { mutateAsync: deleteBatchMutateAsync } = useDeleteOrgMembershipBatch();
const { mutateAsync: updateOrgMembership } = useUpdateOrgMembership(); const { mutateAsync: updateOrgMembership } = useUpdateOrgMembership();
@@ -194,13 +213,13 @@ export const OrgMembersSection = () => {
<Button <Button
colorSchema="secondary" colorSchema="secondary"
type="submit" type="submit"
leftIcon={<FontAwesomeIcon icon={faPlus} />} leftIcon={<UserPlusIcon size={16} />}
onClick={() => onClick={() =>
isSubOrganization ? handlePopUpOpen("addMemberToSubOrg") : handleAddMemberModal() isSubOrganization ? handlePopUpOpen("addMemberToSubOrg") : handleAddMemberModal()
} }
isDisabled={!isAllowed} isDisabled={!isAllowed}
> >
Add Member {isSubOrganization ? "Add Members" : "Invite Members"}
</Button> </Button>
)} )}
</OrgPermissionCan> </OrgPermissionCan>