Merge pull request #4681 from Infisical/nav-restructure

improvement(frontend): Implement new navigation structure
This commit is contained in:
Scott Wilson
2025-10-20 05:08:44 -07:00
committed by GitHub
125 changed files with 2781 additions and 3045 deletions
@@ -76,11 +76,11 @@ export const AccessManagementPage = () => {
const hasNoAccess = tabSections.every((tab) => tab.isHidden);
return (
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
<div className="mx-auto flex flex-col justify-between bg-bunker-800 text-white">
<Helmet>
<title>{t("common.head-title", { title: t("settings.org.title") })}</title>
</Helmet>
<div className="mx-auto mb-6 w-full max-w-7xl">
<div className="mx-auto mb-6 w-full max-w-8xl">
<PageHeader
scope="org"
title="Access Control"
@@ -111,7 +111,7 @@ export const AccessManagementPage = () => {
isOpen={isUpgradePrivilegeSystemModalOpen}
onOpenChange={setIsUpgradePrivilegeSystemModalOpen}
/>
<Tabs value={selectedTab} onValueChange={updateSelectedTab}>
<Tabs orientation="vertical" value={selectedTab} onValueChange={updateSelectedTab}>
<TabList>
{tabSections
.filter((el) => !el.isHidden)
@@ -1,17 +1,5 @@
import { motion } from "framer-motion";
import { OrgGroupsSection } from "./components";
export const OrgGroupsTab = () => {
return (
<motion.div
key="panel-org-groups"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<OrgGroupsSection />
</motion.div>
);
return <OrgGroupsSection />;
};
@@ -1,17 +1,5 @@
import { motion } from "framer-motion";
import { IdentitySection } from "./components";
export const OrgIdentityTab = () => {
return (
<motion.div
key="panel-service-token"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<IdentitySection />
</motion.div>
);
return <IdentitySection />;
};
@@ -1,17 +1,5 @@
import { motion } from "framer-motion";
import { OrgMembersSection } from "./components";
export const OrgMembersTab = () => {
return (
<motion.div
key="panel-org-members"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<OrgMembersSection />
</motion.div>
);
return <OrgMembersSection />;
};
@@ -1,17 +1,5 @@
import { motion } from "framer-motion";
import { OrgRoleTable } from "./OrgRoleTable";
export const OrgRoleTabSection = () => {
return (
<motion.div
key="role-list"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<OrgRoleTable />
</motion.div>
);
return <OrgRoleTable />;
};
@@ -20,7 +20,7 @@ export const AppConnectionsPage = withPermission(
<meta property="og:image" content="/images/message.png" />
</Helmet>
<div className="flex w-full justify-center bg-bunker-800 text-white">
<div className="w-full max-w-7xl">
<div className="w-full max-w-8xl">
<PageHeader
scope="org"
className="w-full"
@@ -12,9 +12,8 @@ export const AuditLogsPage = () => {
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
</Helmet>
<div className="flex h-full w-full justify-center bg-bunker-800 text-white">
<div className="w-full max-w-7xl">
<div className="flex w-full justify-center bg-bunker-800 pb-6 text-white">
<div className="w-full max-w-8xl">
<PageHeader
scope="org"
title="Audit Logs"
@@ -18,7 +18,7 @@ export const BillingPage = () => {
<meta property="og:image" content="/images/message.png" />
</Helmet>
<div className="flex h-full w-full justify-center bg-bunker-800 text-white">
<div className="w-full max-w-7xl">
<div className="w-full max-w-8xl">
<PageHeader
scope="org"
title={t("billing.title")}
@@ -17,15 +17,15 @@ const tabs = [
export const BillingTabGroup = withPermission(
() => {
const tabsFiltered = isInfisicalCloud()
? tabs
: [{ name: "Infisical Self-Hosted", key: "tab-infisical-cloud" }];
if (!isInfisicalCloud()) {
return <BillingCloudTab />;
}
return (
<Tabs defaultValue={tabs[0].key}>
<Tabs orientation="vertical" defaultValue={tabs[0].key}>
<TabList>
{tabsFiltered.map((tab) => (
<Tab variant="org" value={tab.key}>
{tabs.map((tab) => (
<Tab variant="org" key={tab.key} value={tab.key}>
{tab.name}
</Tab>
))}
@@ -33,19 +33,15 @@ export const BillingTabGroup = withPermission(
<TabPanel value={tabs[0].key}>
<BillingCloudTab />
</TabPanel>
{isInfisicalCloud() && (
<>
<TabPanel value={tabs[1].key}>
<BillingSelfHostedTab />
</TabPanel>
<TabPanel value={tabs[2].key}>
<BillingReceiptsTab />
</TabPanel>
<TabPanel value={tabs[3].key}>
<BillingDetailsTab />
</TabPanel>
</>
)}
<TabPanel value={tabs[1].key}>
<BillingSelfHostedTab />
</TabPanel>
<TabPanel value={tabs[2].key}>
<BillingReceiptsTab />
</TabPanel>
<TabPanel value={tabs[3].key}>
<BillingDetailsTab />
</TabPanel>
</Tabs>
);
},
@@ -1,6 +1,8 @@
import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "@tanstack/react-router";
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { twMerge } from "tailwind-merge";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
@@ -80,10 +82,20 @@ const Page = () => {
if (isPending) return <Spinner size="sm" className="mt-2 ml-2" />;
return (
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
<div className="mx-auto flex flex-col justify-between bg-bunker-800 text-white">
{data && (
<div className="mx-auto mb-6 w-full max-w-7xl">
<PageHeader scope="org" title={data.group.name}>
<div className="mx-auto w-full max-w-8xl">
<Link
to="/organization/access-management"
search={{
selectedTab: TabSections.Groups
}}
className="mb-4 flex items-center gap-x-2 text-sm text-mineshaft-400"
>
<FontAwesomeIcon icon={faChevronLeft} />
Groups
</Link>
<PageHeader scope="org" description="Organization Group" title={data.group.name}>
<DropdownMenu>
<DropdownMenuTrigger asChild className="rounded-lg">
<div className="hover:text-primary-400 data-[state=open]:text-primary-400">
@@ -1,6 +1,8 @@
import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "@tanstack/react-router";
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications";
@@ -72,10 +74,20 @@ const Page = () => {
};
return (
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
<div className="mx-auto flex flex-col justify-between bg-bunker-800 text-white">
{data && (
<div className="mx-auto mb-6 w-full max-w-7xl">
<PageHeader scope="org" title={data.identity.name} />
<div className="mx-auto w-full max-w-8xl">
<Link
to="/organization/access-management"
search={{
selectedTab: OrgAccessControlTabSections.Identities
}}
className="mb-4 flex items-center gap-x-2 text-sm text-mineshaft-400"
>
<FontAwesomeIcon icon={faChevronLeft} />
Identities
</Link>
<PageHeader scope="org" description="Organization Identity" title={data.identity.name} />
<div className="flex">
<div className="mr-4 w-96">
<IdentityDetailsSection identityId={identityId} handlePopUpOpen={handlePopUpOpen} />
@@ -12,7 +12,7 @@ export const NetworkingPage = () => {
<meta property="og:image" content="/images/message.png" />
</Helmet>
<div className="flex w-full justify-center bg-bunker-800 text-white">
<div className="w-full max-w-7xl">
<div className="w-full max-w-8xl">
<PageHeader
scope="org"
title="Networking"
@@ -19,7 +19,7 @@ export const NetworkingTabGroup = () => {
const [selectedTab, setSelectedTab] = useState(search.selectedTab || tabs[0].key);
return (
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
<Tabs orientation="vertical" value={selectedTab} onValueChange={setSelectedTab}>
<TabList>
{tabs.map((tab) => (
<Tab variant="org" value={tab.key} key={tab.key}>
@@ -58,18 +58,16 @@ export const ProjectsPage = () => {
: true;
return (
<div className="mx-auto flex max-w-7xl flex-col justify-start bg-bunker-800">
<div className="mx-auto flex max-w-8xl flex-col justify-start bg-bunker-800">
<Helmet>
<title>{t("common.head-title", { title: t("settings.members.title") })}</title>
<link rel="icon" href="/infisical.ico" />
</Helmet>
<div className="mb-4 flex flex-col items-start justify-start">
<PageHeader
scope="org"
title="Overview"
description="Your team's complete security toolkit - organized and ready when you need them."
/>
</div>
<PageHeader
scope="org"
title="Overview"
description="Your team's complete security toolkit - organized and ready when you need them."
/>
{projectListView === ProjectListView.MyProjects ? (
<MyProjectView
onAddNewProject={() => handlePopUpOpen("addNewWs")}
@@ -1,8 +1,8 @@
import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
import { faCopy, faEllipsisV } from "@fortawesome/free-solid-svg-icons";
import { faChevronLeft, faCopy, faEllipsisV } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate, useParams } from "@tanstack/react-router";
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
@@ -77,20 +77,26 @@ export const Page = () => {
const isCustomRole = !["admin", "member", "no-access"].includes(data?.slug ?? "");
return (
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
<div className="mx-auto flex flex-col justify-between bg-bunker-800 text-white">
{data && (
<div className="mx-auto mb-6 w-full max-w-7xl">
<div className="mx-auto w-full max-w-8xl">
<Link
to="/organization/access-management"
search={{
selectedTab: OrgAccessControlTabSections.Roles
}}
className="mb-4 flex items-center gap-x-2 text-sm text-mineshaft-400"
>
<FontAwesomeIcon icon={faChevronLeft} />
Roles
</Link>
<PageHeader
scope="org"
title={
<div className="flex flex-col">
<div>
<span>{data.name}</span>
<p className="text-sm leading-3 font-normal text-mineshaft-400 normal-case">
{data.slug} {data.description && `- ${data.description}`}
</p>
</div>
</div>
title={data.name}
description={
<>
{data.slug} {data.description && `- ${data.description}`}
</>
}
>
{isCustomRole && (
@@ -20,7 +20,7 @@ export const SecretSharingPage = () => {
<meta name="og:description" content={String(t("approval.og-description"))} />
</Helmet>
<div className="h-full">
<div className="container mx-auto h-full w-full max-w-7xl bg-bunker-800 text-white">
<div className="mx-auto h-full w-full max-w-8xl bg-bunker-800 text-white">
<PageHeader
scope="org"
title="Secret Sharing"
@@ -1,7 +1,7 @@
import { Helmet } from "react-helmet";
import { useNavigate, useSearch } from "@tanstack/react-router";
import { Badge, Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { RequestSecretTab } from "./components/RequestSecret/RequestSecretTab";
@@ -34,14 +34,13 @@ export const ShareSecretSection = () => {
<meta property="og:image" content="/images/message.png" />
</Helmet>
<Tabs value={selectedTab} onValueChange={updateSelectedTab}>
<Tabs orientation="vertical" value={selectedTab} onValueChange={updateSelectedTab}>
<TabList>
<Tab value={SecretSharingPageTabs.ShareSecret}>Share Secrets</Tab>
<Tab value={SecretSharingPageTabs.RequestSecret}>
<Tab variant="org" value={SecretSharingPageTabs.ShareSecret}>
Share Secrets
</Tab>
<Tab variant="org" value={SecretSharingPageTabs.RequestSecret}>
Request Secrets
<Badge variant="primary" className="ml-1 cursor-pointer text-xs">
New
</Badge>
</Tab>
</TabList>
<TabPanel value={SecretSharingPageTabs.ShareSecret}>
@@ -20,7 +20,7 @@ export const SecretSharingSettingsPage = withPermission(
<title>{t("common.head-title", { title: "Secret Share Settings" })}</title>
</Helmet>
<div className="flex w-full justify-center bg-bunker-800 text-white">
<div className="w-full max-w-7xl">
<div className="w-full max-w-8xl">
<PageHeader scope="org" title="Secret Share Settings" />
<SecretSharingSettingsTabGroup />
</div>
@@ -14,8 +14,12 @@ export const SettingsPage = () => {
<title>{t("common.head-title", { title: t("settings.org.title") })}</title>
</Helmet>
<div className="flex w-full justify-center bg-bunker-800 text-white">
<div className="w-full max-w-7xl">
<PageHeader scope="org" title={t("settings.org.title")} />
<div className="w-full max-w-8xl">
<PageHeader
scope="org"
description="Configure organization-wide settings"
title={t("settings.org.title")}
/>
<OrgTabGroup />
</div>
</div>
@@ -55,7 +55,7 @@ export const OrgTabGroup = () => {
const [selectedTab, setSelectedTab] = useState(search.selectedTab || tabs[0].key);
return (
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
<Tabs orientation="vertical" value={selectedTab} onValueChange={setSelectedTab}>
<TabList>
{tabs.map((tab) => (
<Tab variant="org" value={tab.key} key={tab.key}>
@@ -1,6 +1,8 @@
import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "@tanstack/react-router";
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { twMerge } from "tailwind-merge";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
@@ -115,16 +117,30 @@ const Page = withPermission(
};
return (
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
<div className="mx-auto flex flex-col justify-between bg-bunker-800 text-white">
{membership && (
<div className="mx-auto mb-6 w-full max-w-7xl">
<div className="mx-auto w-full max-w-8xl">
<Link
to="/organization/access-management"
search={{
selectedTab: OrgAccessControlTabSections.Member
}}
className="mb-4 flex items-center gap-x-2 text-sm text-mineshaft-400"
>
<FontAwesomeIcon icon={faChevronLeft} />
Users
</Link>
<PageHeader
scope="org"
title={
membership.user.firstName || membership.user.lastName
? `${membership.user.firstName} ${membership.user.lastName ?? ""}`.trim()
: "-"
: (membership.user.username ??
membership.user.email ??
membership.inviteEmail ??
"Unknown User")
}
description="Organization User Membership"
>
<div>
{userId !== membership.user.id && (