mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 13:27:46 +00:00
feat: brought back assume privilege banner
This commit is contained in:
@@ -42,7 +42,8 @@ export const initProjectHelper = async ({ projectName }: { projectName: string }
|
|||||||
const {
|
const {
|
||||||
data: { project }
|
data: { project }
|
||||||
} = await createWorkspace({
|
} = await createWorkspace({
|
||||||
projectName
|
projectName,
|
||||||
|
type: ProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -4,10 +4,13 @@ import { Link, Outlet } from "@tanstack/react-router";
|
|||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
import { Lottie, Menu, MenuGroup, MenuItem } from "@app/components/v2";
|
import { Lottie, Menu, MenuGroup, MenuItem } from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useProjectPermission, useWorkspace } from "@app/context";
|
||||||
|
|
||||||
|
import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePrivilegeModeBanner";
|
||||||
|
|
||||||
export const KmsLayout = () => {
|
export const KmsLayout = () => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { assumedPrivilegeDetails } = useProjectPermission();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
|
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
|
||||||
@@ -120,6 +123,7 @@ export const KmsLayout = () => {
|
|||||||
</nav>
|
</nav>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 pt-8">
|
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 pt-8">
|
||||||
|
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,10 +15,13 @@ import { Link, Outlet } from "@tanstack/react-router";
|
|||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
import { Lottie, Menu, MenuGroup, MenuItem } from "@app/components/v2";
|
import { Lottie, Menu, MenuGroup, MenuItem } from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useProjectPermission, useWorkspace } from "@app/context";
|
||||||
|
|
||||||
|
import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePrivilegeModeBanner";
|
||||||
|
|
||||||
export const PkiManagerLayout = () => {
|
export const PkiManagerLayout = () => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { assumedPrivilegeDetails } = useProjectPermission();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -184,6 +187,7 @@ export const PkiManagerLayout = () => {
|
|||||||
</nav>
|
</nav>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 pt-8">
|
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 pt-8">
|
||||||
|
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
import { Link, Outlet } from "@tanstack/react-router";
|
|
||||||
import { motion } from "framer-motion";
|
|
||||||
|
|
||||||
import { Menu, MenuItem } from "@app/components/v2";
|
|
||||||
import { useWorkspace } from "@app/context";
|
|
||||||
|
|
||||||
export const ProjectGeneralLayout = () => {
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
|
|
||||||
<div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
|
|
||||||
<motion.div
|
|
||||||
key="menu-project-items"
|
|
||||||
initial={{ x: -150 }}
|
|
||||||
animate={{ x: 0 }}
|
|
||||||
exit={{ x: -150 }}
|
|
||||||
transition={{ duration: 0.2 }}
|
|
||||||
className="dark w-full border-r border-mineshaft-600 bg-gradient-to-tr from-mineshaft-700 via-mineshaft-800 to-mineshaft-900 md:w-60"
|
|
||||||
>
|
|
||||||
<nav className="items-between flex h-full flex-col overflow-y-auto dark:[color-scheme:dark]">
|
|
||||||
<div className="border-b border-mineshaft-600 px-4 py-3.5 text-lg text-white">
|
|
||||||
Project Overview
|
|
||||||
</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<Menu>
|
|
||||||
<Link
|
|
||||||
to="/projects/$projectId/access-management"
|
|
||||||
params={{
|
|
||||||
projectId: currentWorkspace.id
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ isActive }) => <MenuItem isSelected={isActive}>Access Control</MenuItem>}
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/projects/$projectId/settings"
|
|
||||||
params={{
|
|
||||||
projectId: currentWorkspace.id
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ isActive }) => <MenuItem isSelected={isActive}>Project Settings</MenuItem>}
|
|
||||||
</Link>
|
|
||||||
</Menu>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</motion.div>
|
|
||||||
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 pt-8">
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { ProjectGeneralLayout } from "./ProjectGeneralLayout";
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { faDotCircle, faMobile, faWindowMaximize } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import { Link, Outlet, useLocation } from "@tanstack/react-router";
|
|
||||||
import { motion } from "framer-motion";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
|
|
||||||
import { ShouldWrap } from "@app/components/utilities/ShouldWrapComponent";
|
|
||||||
import {
|
|
||||||
Divider,
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
Lottie,
|
|
||||||
Menu,
|
|
||||||
MenuItem,
|
|
||||||
Tooltip
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { useProjectPermission, useWorkspace } from "@app/context";
|
|
||||||
import { getCurrentProductFromUrl } from "@app/helpers/project";
|
|
||||||
import { useLocalStorageState } from "@app/hooks";
|
|
||||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
|
||||||
|
|
||||||
import { AssumePrivilegeModeBanner } from "./components/AssumePrivilegeModeBanner";
|
|
||||||
|
|
||||||
enum SidebarStyle {
|
|
||||||
Expanded = "expanded",
|
|
||||||
Collapsed = "collapsed",
|
|
||||||
ExpandOnHover = "expand-on-hover"
|
|
||||||
}
|
|
||||||
const MIN_SIDEBAR_SIZE = "55px";
|
|
||||||
const MAX_SIDEBAR_SIZE = "220px";
|
|
||||||
// This is a generic layout shared by all types of projects.
|
|
||||||
// If the product layout differs significantly, create a new layout as needed.
|
|
||||||
export const ProjectLayout = () => {
|
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { assumedPrivilegeDetails } = useProjectPermission();
|
|
||||||
|
|
||||||
const currentProductType = getCurrentProductFromUrl(location.pathname);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
className="dark relative hidden w-full flex-col overflow-x-hidden md:flex"
|
|
||||||
style={{
|
|
||||||
height: "calc(100vh - 3rem)",
|
|
||||||
paddingLeft: !(sidebarStyle === SidebarStyle.Expanded) ? MIN_SIDEBAR_SIZE : "0px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex flex-grow flex-col overflow-y-auto overflow-x-hidden md:flex-row">
|
|
||||||
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800">
|
|
||||||
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="z-[200] flex h-screen w-screen flex-col items-center justify-center bg-bunker-800 md:hidden">
|
|
||||||
<FontAwesomeIcon icon={faMobile} className="mb-8 text-7xl text-gray-300" />
|
|
||||||
<p className="max-w-sm px-6 text-center text-lg text-gray-200">
|
|
||||||
{` ${t("common.no-mobile")} `}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
+2
-6
@@ -3,10 +3,9 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
|
|
||||||
import { Button } from "@app/components/v2";
|
import { Button } from "@app/components/v2";
|
||||||
import { useProjectPermission, useWorkspace } from "@app/context";
|
import { useProjectPermission, useWorkspace } from "@app/context";
|
||||||
import { getCurrentProductFromUrl, getProjectHomePage } from "@app/helpers/project";
|
import { getProjectHomePage } from "@app/helpers/project";
|
||||||
import { useRemoveAssumeProjectPrivilege } from "@app/hooks/api";
|
import { useRemoveAssumeProjectPrivilege } from "@app/hooks/api";
|
||||||
import { ActorType } from "@app/hooks/api/auditLogs/enums";
|
import { ActorType } from "@app/hooks/api/auditLogs/enums";
|
||||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
|
||||||
|
|
||||||
export const AssumePrivilegeModeBanner = () => {
|
export const AssumePrivilegeModeBanner = () => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
@@ -37,10 +36,7 @@ export const AssumePrivilegeModeBanner = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
const url = getProjectHomePage(
|
const url = getProjectHomePage(currentWorkspace.type);
|
||||||
getCurrentProductFromUrl(window.location.href) || ProjectType.SecretManager
|
|
||||||
);
|
|
||||||
|
|
||||||
window.location.href = url.replace("$projectId", currentWorkspace.id);
|
window.location.href = url.replace("$projectId", currentWorkspace.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
import { ComponentPropsWithRef, ElementType, useRef } from "react";
|
|
||||||
import { DotLottie, DotLottieReact } from "@lottiefiles/dotlottie-react";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
|
|
||||||
import { MenuItemProps } from "@app/components/v2";
|
|
||||||
|
|
||||||
export const MenuIconButton = <T extends ElementType = "button">({
|
|
||||||
children,
|
|
||||||
icon,
|
|
||||||
className,
|
|
||||||
isDisabled,
|
|
||||||
isSelected,
|
|
||||||
as: Item = "div",
|
|
||||||
description,
|
|
||||||
// wrapping in forward ref with generic component causes the loss of ts definitions on props
|
|
||||||
inputRef,
|
|
||||||
lottieIconMode = "forward",
|
|
||||||
...props
|
|
||||||
}: MenuItemProps<T> &
|
|
||||||
ComponentPropsWithRef<T> & { lottieIconMode?: "reverse" | "forward" }): JSX.Element => {
|
|
||||||
const iconRef = useRef<DotLottie | null>(null);
|
|
||||||
return (
|
|
||||||
<div className="px-1">
|
|
||||||
<Item
|
|
||||||
type="button"
|
|
||||||
role="menuitem"
|
|
||||||
className={twMerge(
|
|
||||||
"group relative my-1 flex w-full cursor-pointer flex-col items-center justify-center rounded p-2 font-inter text-sm text-bunker-100 transition-all duration-150 hover:bg-mineshaft-700",
|
|
||||||
isSelected && "bg-bunker-8g0 hover:bg-mineshaft-500",
|
|
||||||
isDisabled && "cursor-not-allowed hover:bg-transparent",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
onMouseEnter={() => iconRef.current?.play()}
|
|
||||||
onMouseLeave={() => iconRef.current?.stop()}
|
|
||||||
ref={inputRef}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`${isSelected ? "opacity-100" : "opacity-0"} absolute left-0 h-full w-0.5 rounded-bl rounded-tl bg-primary transition-all duration-150`}
|
|
||||||
/>
|
|
||||||
{icon && (
|
|
||||||
<div className="my-auto mb-2 h-6 w-6">
|
|
||||||
<DotLottieReact
|
|
||||||
dotLottieRefCallback={(el) => {
|
|
||||||
iconRef.current = el;
|
|
||||||
}}
|
|
||||||
src={`/lotties/${icon}.json`}
|
|
||||||
loop
|
|
||||||
className="h-full w-full"
|
|
||||||
mode={lottieIconMode}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div
|
|
||||||
className="flex-grow justify-center break-words text-center"
|
|
||||||
style={{ fontSize: "10px" }}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</Item>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// export const MenuIconButton = <T extends ElementType = "button">({
|
|
||||||
// children,
|
|
||||||
// icon,
|
|
||||||
// className,
|
|
||||||
// isDisabled,
|
|
||||||
// isSelected,
|
|
||||||
// as: Item = "div",
|
|
||||||
// description,
|
|
||||||
// // wrapping in forward ref with generic component causes the loss of ts definitions on props
|
|
||||||
// inputRef,
|
|
||||||
// lottieIconMode = "forward",
|
|
||||||
// ...props
|
|
||||||
// }: MenuItemProps<T> &
|
|
||||||
// ComponentPropsWithRef<T> & { lottieIconMode?: "reverse" | "forward" }): JSX.Element => {
|
|
||||||
// const iconRef = useRef<DotLottie | null>(null);
|
|
||||||
// return (
|
|
||||||
// <div className="px-1">
|
|
||||||
// <Item
|
|
||||||
// type="button"
|
|
||||||
// role="menuitem"
|
|
||||||
// className={twMerge(
|
|
||||||
// "group relative my-1 flex w-full cursor-pointer flex-col items-center justify-center rounded p-2 font-inter text-sm text-bunker-100 transition-all duration-150 hover:bg-mineshaft-700",
|
|
||||||
// isSelected && "bg-mineshaft-600 hover:bg-mineshaft-700",
|
|
||||||
// isDisabled && "cursor-not-allowed hover:bg-transparent",
|
|
||||||
// className
|
|
||||||
// )}
|
|
||||||
// onMouseEnter={() => iconRef.current?.play()}
|
|
||||||
// onMouseLeave={() => iconRef.current?.stop()}
|
|
||||||
// ref={inputRef}
|
|
||||||
// {...props}
|
|
||||||
// >
|
|
||||||
// <div
|
|
||||||
// className={`${"opacity-0"} absolute left-0 h-full w-1 bg-primary transition-all duration-150`}
|
|
||||||
// />
|
|
||||||
// {icon && (
|
|
||||||
// <div className="my-auto mb-2 h-6 w-6">
|
|
||||||
// <DotLottieReact
|
|
||||||
// dotLottieRefCallback={(el) => {
|
|
||||||
// iconRef.current = el;
|
|
||||||
// }}
|
|
||||||
// src={`/lotties/${icon}.json`}
|
|
||||||
// loop
|
|
||||||
// className="h-full w-full"
|
|
||||||
// mode={lottieIconMode}
|
|
||||||
// />
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// <div
|
|
||||||
// className="flex-grow justify-center break-words text-center"
|
|
||||||
// style={{ fontSize: "10px" }}
|
|
||||||
// >
|
|
||||||
// {children}
|
|
||||||
// </div>
|
|
||||||
// </Item>
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import { ComponentPropsWithRef, ElementType, useRef } from "react";
|
|
||||||
import { DotLottie, DotLottieReact } from "@lottiefiles/dotlottie-react";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
|
|
||||||
import { MenuItemProps } from "@app/components/v2";
|
|
||||||
|
|
||||||
export const MenuIconButton = <T extends ElementType = "button">({
|
|
||||||
children,
|
|
||||||
icon,
|
|
||||||
className,
|
|
||||||
isDisabled,
|
|
||||||
isSelected,
|
|
||||||
as: Item = "div",
|
|
||||||
description,
|
|
||||||
// wrapping in forward ref with generic component causes the loss of ts definitions on props
|
|
||||||
inputRef,
|
|
||||||
lottieIconMode = "forward",
|
|
||||||
...props
|
|
||||||
}: MenuItemProps<T> &
|
|
||||||
ComponentPropsWithRef<T> & { lottieIconMode?: "reverse" | "forward" }): JSX.Element => {
|
|
||||||
const iconRef = useRef<DotLottie | null>(null);
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Item
|
|
||||||
type="button"
|
|
||||||
role="menuitem"
|
|
||||||
className={twMerge(
|
|
||||||
"group relative flex w-full cursor-pointer flex-col items-center justify-center p-2 font-inter text-sm text-bunker-100 transition-all duration-150 hover:bg-mineshaft-700",
|
|
||||||
isSelected && "rounded-none bg-bunker-800 hover:bg-mineshaft-600",
|
|
||||||
isDisabled && "cursor-not-allowed hover:bg-transparent",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
onMouseEnter={() => iconRef.current?.play()}
|
|
||||||
onMouseLeave={() => iconRef.current?.stop()}
|
|
||||||
ref={inputRef}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`${
|
|
||||||
isSelected ? "opacity-100" : "opacity-0"
|
|
||||||
} absolute left-0 h-full w-0.5 bg-primary transition-all duration-150`}
|
|
||||||
/>
|
|
||||||
{icon && (
|
|
||||||
<div className="my-auto mb-2 h-6 w-6">
|
|
||||||
<DotLottieReact
|
|
||||||
dotLottieRefCallback={(el) => {
|
|
||||||
iconRef.current = el;
|
|
||||||
}}
|
|
||||||
src={`/lotties/${icon}.json`}
|
|
||||||
loop
|
|
||||||
className="h-full w-full"
|
|
||||||
mode={lottieIconMode}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div
|
|
||||||
className="flex-grow justify-center break-words text-center"
|
|
||||||
style={{ fontSize: "10px" }}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</Item>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { MenuIconButton } from "./MenuIconButton";
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
import {
|
|
||||||
faAngleDown,
|
|
||||||
faArrowLeft,
|
|
||||||
faArrowUpRightFromSquare,
|
|
||||||
faCheck
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import { Link, useNavigate } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { useOrganization, useUser } from "@app/context";
|
|
||||||
import { useGetOrganizations, useLogoutUser } from "@app/hooks/api";
|
|
||||||
import { AuthMethod } from "@app/hooks/api/users/types";
|
|
||||||
|
|
||||||
type Prop = {
|
|
||||||
onChangeOrg: (orgId: string) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SidebarHeader = ({ onChangeOrg }: Prop) => {
|
|
||||||
const { currentOrg } = useOrganization();
|
|
||||||
const { user } = useUser();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const { data: orgs } = useGetOrganizations();
|
|
||||||
|
|
||||||
const logout = useLogoutUser();
|
|
||||||
const logOutUser = async () => {
|
|
||||||
try {
|
|
||||||
console.log("Logging out...");
|
|
||||||
await logout.mutateAsync();
|
|
||||||
navigate({ to: "/login" });
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex h-12 cursor-default items-center px-3 pt-6">
|
|
||||||
<Link to="/organization/projects">
|
|
||||||
<div className="pl-1 pr-2 text-mineshaft-400 duration-200 hover:text-mineshaft-100">
|
|
||||||
<FontAwesomeIcon icon={faArrowLeft} />
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild className="max-w-[160px] data-[state=open]:bg-mineshaft-600">
|
|
||||||
<div className="mr-auto flex items-center rounded-md py-1.5 pl-1.5 pr-2 hover:bg-mineshaft-600">
|
|
||||||
<div className="flex h-5 w-5 min-w-[20px] items-center justify-center rounded-md bg-primary text-sm">
|
|
||||||
{currentOrg?.name.charAt(0)}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="overflow-hidden truncate text-ellipsis pl-2 text-sm text-mineshaft-100"
|
|
||||||
style={{ maxWidth: "140px" }}
|
|
||||||
>
|
|
||||||
{currentOrg?.name}
|
|
||||||
</div>
|
|
||||||
<FontAwesomeIcon icon={faAngleDown} className="pl-1 pt-1 text-xs text-mineshaft-300" />
|
|
||||||
</div>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="start" className="p-1">
|
|
||||||
<div className="px-2 py-1 text-xs text-mineshaft-400">{user?.username}</div>
|
|
||||||
{orgs?.map((org) => {
|
|
||||||
return (
|
|
||||||
<DropdownMenuItem key={org.id}>
|
|
||||||
<Button
|
|
||||||
onClick={async () => {
|
|
||||||
if (currentOrg?.id === org.id) return;
|
|
||||||
|
|
||||||
if (org.authEnforced) {
|
|
||||||
// org has an org-level auth method enabled (e.g. SAML)
|
|
||||||
// -> logout + redirect to SAML SSO
|
|
||||||
|
|
||||||
await logout.mutateAsync();
|
|
||||||
if (org.orgAuthMethod === AuthMethod.OIDC) {
|
|
||||||
window.open(`/api/v1/sso/oidc/login?orgSlug=${org.slug}`);
|
|
||||||
} else {
|
|
||||||
window.open(`/api/v1/sso/redirect/saml2/organizations/${org.slug}`);
|
|
||||||
}
|
|
||||||
window.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
onChangeOrg(org?.id);
|
|
||||||
}}
|
|
||||||
variant="plain"
|
|
||||||
colorSchema="secondary"
|
|
||||||
size="xs"
|
|
||||||
className="flex w-full items-center justify-start p-0 font-normal"
|
|
||||||
leftIcon={
|
|
||||||
currentOrg?.id === org.id && (
|
|
||||||
<FontAwesomeIcon icon={faCheck} className="mr-3 text-primary" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="flex w-full max-w-[150px] items-center justify-between truncate">
|
|
||||||
{org.name}
|
|
||||||
</div>
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
<div className="mt-1 h-1 border-t border-mineshaft-600" />
|
|
||||||
<button type="button" onClick={logOutUser} className="w-full">
|
|
||||||
<DropdownMenuItem>Log Out</DropdownMenuItem>
|
|
||||||
</button>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger
|
|
||||||
asChild
|
|
||||||
className="p-1 hover:bg-primary-400 hover:text-black data-[state=open]:bg-primary-400 data-[state=open]:text-black"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="child flex items-center justify-center rounded-full bg-mineshaft pr-1 text-mineshaft-300 hover:bg-mineshaft-500"
|
|
||||||
style={{ fontSize: "11px", width: "26px", height: "26px" }}
|
|
||||||
>
|
|
||||||
{user?.firstName?.charAt(0)}
|
|
||||||
{user?.lastName && user?.lastName?.charAt(0)}
|
|
||||||
</div>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="start" className="p-1">
|
|
||||||
<div className="px-2 py-1 text-xs text-mineshaft-400">{user?.username}</div>
|
|
||||||
<Link to="/personal-settings">
|
|
||||||
<DropdownMenuItem>Personal Settings</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
<a
|
|
||||||
href="https://infisical.com/docs/documentation/getting-started/introduction"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="mt-3 w-full text-sm font-normal leading-[1.2rem] text-mineshaft-300 hover:text-mineshaft-100"
|
|
||||||
>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
Documentation
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={faArrowUpRightFromSquare}
|
|
||||||
className="mb-[0.06rem] pl-1.5 text-xxs"
|
|
||||||
/>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://infisical.com/slack"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="mt-3 w-full text-sm font-normal leading-[1.2rem] text-mineshaft-300 hover:text-mineshaft-100"
|
|
||||||
>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
Join Slack Community
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={faArrowUpRightFromSquare}
|
|
||||||
className="mb-[0.06rem] pl-1.5 text-xxs"
|
|
||||||
/>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</a>
|
|
||||||
{user?.superAdmin && (
|
|
||||||
<Link to="/admin">
|
|
||||||
<DropdownMenuItem className="mt-1 border-t border-mineshaft-600">
|
|
||||||
Server Admin Console
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
<Link to="/organization/admin">
|
|
||||||
<DropdownMenuItem className="mt-1 border-t border-mineshaft-600">
|
|
||||||
Organization Admin Console
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
<div className="mt-1 h-1 border-t border-mineshaft-600" />
|
|
||||||
<button type="button" onClick={logOutUser} className="w-full">
|
|
||||||
<DropdownMenuItem>Log Out</DropdownMenuItem>
|
|
||||||
</button>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { SidebarHeader } from "./SidebarHeader";
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { ProjectLayout } from "./ProjectLayout";
|
|
||||||
@@ -14,15 +14,18 @@ import { Link, Outlet } from "@tanstack/react-router";
|
|||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
import { Badge, Lottie, Menu, MenuGroup, MenuItem } from "@app/components/v2";
|
import { Badge, Lottie, Menu, MenuGroup, MenuItem } from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useProjectPermission, useWorkspace } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useGetAccessRequestsCount,
|
useGetAccessRequestsCount,
|
||||||
useGetSecretApprovalRequestCount,
|
useGetSecretApprovalRequestCount,
|
||||||
useGetSecretRotations
|
useGetSecretRotations
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
|
|
||||||
|
import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePrivilegeModeBanner";
|
||||||
|
|
||||||
export const SecretManagerLayout = () => {
|
export const SecretManagerLayout = () => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { assumedPrivilegeDetails } = useProjectPermission();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const workspaceId = currentWorkspace?.id || "";
|
const workspaceId = currentWorkspace?.id || "";
|
||||||
@@ -202,6 +205,7 @@ export const SecretManagerLayout = () => {
|
|||||||
</nav>
|
</nav>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 pt-8">
|
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 pt-8">
|
||||||
|
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,10 +10,13 @@ import { Link, Outlet } from "@tanstack/react-router";
|
|||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
import { Lottie, Menu, MenuGroup, MenuItem } from "@app/components/v2";
|
import { Lottie, Menu, MenuGroup, MenuItem } from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useProjectPermission, useWorkspace } from "@app/context";
|
||||||
|
|
||||||
|
import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePrivilegeModeBanner";
|
||||||
|
|
||||||
export const SecretScanningLayout = () => {
|
export const SecretScanningLayout = () => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { assumedPrivilegeDetails } = useProjectPermission();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
|
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
|
||||||
@@ -126,6 +129,7 @@ export const SecretScanningLayout = () => {
|
|||||||
</nav>
|
</nav>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 pt-8">
|
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 pt-8">
|
||||||
|
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,10 +5,18 @@ import { motion } from "framer-motion";
|
|||||||
|
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import { Lottie, Menu, MenuGroup, MenuItem } from "@app/components/v2";
|
import { Lottie, Menu, MenuGroup, MenuItem } from "@app/components/v2";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
useProjectPermission,
|
||||||
|
useWorkspace
|
||||||
|
} from "@app/context";
|
||||||
|
|
||||||
|
import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePrivilegeModeBanner";
|
||||||
|
|
||||||
export const SshLayout = () => {
|
export const SshLayout = () => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { assumedPrivilegeDetails } = useProjectPermission();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
|
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
|
||||||
@@ -131,6 +139,7 @@ export const SshLayout = () => {
|
|||||||
</nav>
|
</nav>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 pt-8">
|
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 pt-8">
|
||||||
|
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
import { createFileRoute, stripSearchParams } from "@tanstack/react-router";
|
|
||||||
import { zodValidator } from "@tanstack/zod-adapter";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { ProjectAccessControlTabs } from "@app/types/project";
|
|
||||||
|
|
||||||
import { AccessControlPage } from "./AccessControlPage";
|
|
||||||
|
|
||||||
const AccessControlPageQuerySchema = z.object({
|
|
||||||
selectedTab: z.nativeEnum(ProjectAccessControlTabs).catch(ProjectAccessControlTabs.Member),
|
|
||||||
requesterEmail: z.string().catch("")
|
|
||||||
});
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/_project-general-layout/access-management"
|
|
||||||
)({
|
|
||||||
component: AccessControlPage,
|
|
||||||
validateSearch: zodValidator(AccessControlPageQuerySchema),
|
|
||||||
search: {
|
|
||||||
middlewares: [stripSearchParams({ requesterEmail: "" })]
|
|
||||||
},
|
|
||||||
beforeLoad: ({ context }) => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
...context.breadcrumbs,
|
|
||||||
{
|
|
||||||
label: "Access Control"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
+2
-5
@@ -24,7 +24,7 @@ import {
|
|||||||
Tr
|
Tr
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
import { getCurrentProductFromUrl, getProjectHomePage } from "@app/helpers/project";
|
import { getProjectHomePage } from "@app/helpers/project";
|
||||||
import {
|
import {
|
||||||
getUserTablePreference,
|
getUserTablePreference,
|
||||||
PreferenceKey,
|
PreferenceKey,
|
||||||
@@ -36,7 +36,6 @@ import { ActorType } from "@app/hooks/api/auditLogs/enums";
|
|||||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||||
import { useListProjectGroupUsers } from "@app/hooks/api/groups/queries";
|
import { useListProjectGroupUsers } from "@app/hooks/api/groups/queries";
|
||||||
import { EFilterReturnedUsers, TGroupMembership } from "@app/hooks/api/groups/types";
|
import { EFilterReturnedUsers, TGroupMembership } from "@app/hooks/api/groups/types";
|
||||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
|
||||||
|
|
||||||
import { GroupMembershipRow } from "./GroupMembershipRow";
|
import { GroupMembershipRow } from "./GroupMembershipRow";
|
||||||
|
|
||||||
@@ -137,9 +136,7 @@ export const GroupMembersTable = ({ groupMembership }: Props) => {
|
|||||||
text: "User privilege assumption has started"
|
text: "User privilege assumption has started"
|
||||||
});
|
});
|
||||||
|
|
||||||
const url = getProjectHomePage(
|
const url = getProjectHomePage(currentWorkspace.type);
|
||||||
getCurrentProductFromUrl(window.location.href) || ProjectType.SecretManager
|
|
||||||
);
|
|
||||||
window.location.href = url.replace("$projectId", currentWorkspace.id);
|
window.location.href = url.replace("$projectId", currentWorkspace.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/cert-management/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/kms/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/secret-management/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/secret-scanning/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/ssh/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { ProjectAccessControlTabs } from "@app/types/project";
|
|
||||||
|
|
||||||
import { GroupDetailsByIDPage } from "./GroupDetailsByIDPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/_project-general-layout/groups/$groupId"
|
|
||||||
)({
|
|
||||||
component: GroupDetailsByIDPage,
|
|
||||||
beforeLoad: ({ context, params }) => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
...context.breadcrumbs,
|
|
||||||
{
|
|
||||||
label: "Access Control",
|
|
||||||
link: linkOptions({
|
|
||||||
to: "/projects/$projectId/access-management",
|
|
||||||
params: {
|
|
||||||
projectId: params.projectId
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
selectedTab: ProjectAccessControlTabs.Groups
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Group"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/cert-management/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/kms/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/secret-management/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/secret-scanning/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/ssh/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { ProjectAccessControlTabs } from "@app/types/project";
|
|
||||||
|
|
||||||
import { IdentityDetailsByIDPage } from "./IdentityDetailsByIDPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/_project-general-layout/identities/$identityId"
|
|
||||||
)({
|
|
||||||
component: IdentityDetailsByIDPage,
|
|
||||||
beforeLoad: ({ context, params }) => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
...context.breadcrumbs,
|
|
||||||
{
|
|
||||||
label: "Access Control",
|
|
||||||
link: linkOptions({
|
|
||||||
to: "/projects/$projectId/access-management",
|
|
||||||
params: {
|
|
||||||
projectId: params.projectId
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
selectedTab: ProjectAccessControlTabs.Identities
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Machine Identity"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/cert-management/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/kms/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/secret-management/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/secret-scanning/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/ssh/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { ProjectAccessControlTabs } from "@app/types/project";
|
|
||||||
|
|
||||||
import { MemberDetailsByIDPage } from "./MemberDetailsByIDPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/_project-general-layout/members/$membershipId"
|
|
||||||
)({
|
|
||||||
component: MemberDetailsByIDPage,
|
|
||||||
beforeLoad: ({ context, params }) => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
...context.breadcrumbs,
|
|
||||||
{
|
|
||||||
label: "Access Control",
|
|
||||||
link: linkOptions({
|
|
||||||
to: "/projects/$projectId/access-management",
|
|
||||||
params: {
|
|
||||||
projectId: params.projectId
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
selectedTab: ProjectAccessControlTabs.Member
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "User"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/cert-management/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/kms/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/secret-management/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/secret-scanning/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const Route = createFileRoute(
|
|||||||
{
|
{
|
||||||
label: "Access Control",
|
label: "Access Control",
|
||||||
link: linkOptions({
|
link: linkOptions({
|
||||||
to: "/projects/$projectId/access-management",
|
to: "/projects/ssh/$projectId/access-management",
|
||||||
params: {
|
params: {
|
||||||
projectId: params.projectId
|
projectId: params.projectId
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { ProjectAccessControlTabs } from "@app/types/project";
|
|
||||||
|
|
||||||
import { RoleDetailsBySlugPage } from "./RoleDetailsBySlugPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/_project-general-layout/roles/$roleSlug"
|
|
||||||
)({
|
|
||||||
component: RoleDetailsBySlugPage,
|
|
||||||
beforeLoad: ({ context, params }) => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
...context.breadcrumbs,
|
|
||||||
{
|
|
||||||
label: "Access Control",
|
|
||||||
link: linkOptions({
|
|
||||||
to: "/projects/$projectId/access-management",
|
|
||||||
params: {
|
|
||||||
projectId: params.projectId
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
selectedTab: ProjectAccessControlTabs.Roles
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Roles"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import { Helmet } from "react-helmet";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import { PageHeader, Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
|
|
||||||
|
|
||||||
import { ProjectGeneralTab } from "./components/ProjectGeneralTab";
|
|
||||||
|
|
||||||
const tabs = [{ name: "General", key: "tab-project-general", Component: ProjectGeneralTab }];
|
|
||||||
|
|
||||||
export const SettingsPage = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex h-full w-full justify-center bg-bunker-800 text-white">
|
|
||||||
<Helmet>
|
|
||||||
<title>{t("common.head-title", { title: t("settings.project.title") })}</title>
|
|
||||||
</Helmet>
|
|
||||||
<div className="w-full max-w-7xl">
|
|
||||||
<PageHeader
|
|
||||||
title={t("settings.project.title")}
|
|
||||||
description="Configure your project details and project-specific settings"
|
|
||||||
/>
|
|
||||||
<Tabs defaultValue={tabs[0].key}>
|
|
||||||
<TabList>
|
|
||||||
{tabs.map((tab) => (
|
|
||||||
<Tab value={tab.key} key={tab.key}>
|
|
||||||
{tab.name}
|
|
||||||
</Tab>
|
|
||||||
))}
|
|
||||||
</TabList>
|
|
||||||
{tabs.map(({ key, Component }) => (
|
|
||||||
<TabPanel value={key} key={key}>
|
|
||||||
<Component />
|
|
||||||
</TabPanel>
|
|
||||||
))}
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { SettingsPage } from "./SettingsPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/_project-general-layout/settings"
|
|
||||||
)({
|
|
||||||
component: SettingsPage,
|
|
||||||
beforeLoad: ({ context }) => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
...context.breadcrumbs,
|
|
||||||
{
|
|
||||||
label: "Settings"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { SettingsPage } from "./SettingsPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/_project-general-layout/settings"
|
|
||||||
)({
|
|
||||||
component: SettingsPage,
|
|
||||||
beforeLoad: ({ context }) => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
...context.breadcrumbs,
|
|
||||||
{
|
|
||||||
label: "Settings"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { SettingsPage } from "./SettingsPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/_project-general-layout/settings"
|
|
||||||
)({
|
|
||||||
component: SettingsPage,
|
|
||||||
beforeLoad: ({ context }) => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
...context.breadcrumbs,
|
|
||||||
{
|
|
||||||
label: "Settings"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { SettingsPage } from "./SettingsPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/_project-general-layout/settings"
|
|
||||||
)({
|
|
||||||
component: SettingsPage,
|
|
||||||
beforeLoad: ({ context }) => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
...context.breadcrumbs,
|
|
||||||
{
|
|
||||||
label: "Settings"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { SettingsPage } from "./SettingsPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/_project-layout/projects/ssh/$projectId/_ssh-layout/settings"
|
|
||||||
)({
|
|
||||||
component: SettingsPage,
|
|
||||||
beforeLoad: ({ context }) => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
...context.breadcrumbs,
|
|
||||||
{
|
|
||||||
label: "Settings"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { SettingsPage } from "./SettingsPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/_project-general-layout/settings"
|
|
||||||
)({
|
|
||||||
component: SettingsPage,
|
|
||||||
beforeLoad: ({ context }) => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
...context.breadcrumbs,
|
|
||||||
{
|
|
||||||
label: "Settings"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { ProjectGeneralLayout } from "@app/layouts/ProjectGeneralLayout";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/_project-general-layout"
|
|
||||||
)({
|
|
||||||
component: ProjectGeneralLayout
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { BreadcrumbTypes } from "@app/components/v2";
|
|
||||||
import { ProjectLayout } from "@app/layouts/ProjectLayout";
|
|
||||||
import { ProjectSelect } from "@app/layouts/ProjectLayout/components/ProjectSelect";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/_org-layout/_project-layout"
|
|
||||||
)({
|
|
||||||
component: ProjectLayout,
|
|
||||||
beforeLoad: async () => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
{
|
|
||||||
type: BreadcrumbTypes.Component,
|
|
||||||
component: ProjectSelect
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user