mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: seperated layouts for each product line
This commit is contained in:
62
frontend/src/layouts/KmsLayout/KmsLayout.tsx
Normal file
62
frontend/src/layouts/KmsLayout/KmsLayout.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
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 KmsLayout = () => {
|
||||
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 pb-2 pt-3 text-lg text-white">
|
||||
KMS
|
||||
</div>
|
||||
<div className="mt-2 flex-grow">
|
||||
<Menu>
|
||||
<Link
|
||||
to="/projects/$projectId/kms/overview"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}>Overview</MenuItem>}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/$projectId/kms/kmip"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}>KMIP</MenuItem>}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/$projectId/cert-manager/settings"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}>Settings</MenuItem>}
|
||||
</Link>
|
||||
</Menu>
|
||||
</div>
|
||||
</nav>
|
||||
</motion.div>
|
||||
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 dark:[color-scheme:dark]">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
1
frontend/src/layouts/KmsLayout/index.tsx
Normal file
1
frontend/src/layouts/KmsLayout/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { KmsLayout } from "./KmsLayout";
|
||||
102
frontend/src/layouts/PkiManagerLayout/PkiManagerLayout.tsx
Normal file
102
frontend/src/layouts/PkiManagerLayout/PkiManagerLayout.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faMobile } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
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 PkiManagerLayout = () => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { t } = useTranslation();
|
||||
|
||||
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 pb-2 pt-3 text-lg text-white">
|
||||
PKI Manager
|
||||
</div>
|
||||
<div className="mt-2 flex-grow">
|
||||
<Menu>
|
||||
<Link
|
||||
to="/projects/$projectId/cert-manager/subscribers"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}>Subscribers</MenuItem>}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/$projectId/cert-manager/certificate-templates"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}> Certificate Templates</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/$projectId/cert-manager/certificates"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}> Certificates</MenuItem>}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/$projectId/cert-manager/certificate-authorities"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}> Certificates Authorities</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/$projectId/cert-manager/alerting"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}>Alerting</MenuItem>}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/$projectId/cert-manager/settings"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}>Settings</MenuItem>}
|
||||
</Link>
|
||||
</Menu>
|
||||
</div>
|
||||
</nav>
|
||||
</motion.div>
|
||||
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 dark:[color-scheme:dark]">
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
1
frontend/src/layouts/PkiManagerLayout/index.tsx
Normal file
1
frontend/src/layouts/PkiManagerLayout/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { PkiManagerLayout } from "./PkiManagerLayout";
|
||||
@@ -0,0 +1,137 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faMobile } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link, Outlet } from "@tanstack/react-router";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
import { Badge, Menu, MenuItem } from "@app/components/v2";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import {
|
||||
useGetAccessRequestsCount,
|
||||
useGetSecretApprovalRequestCount,
|
||||
useGetSecretRotations
|
||||
} from "@app/hooks/api";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
// 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 ProjectGeneralLayout = () => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
const projectSlug = currentWorkspace?.slug || "";
|
||||
|
||||
const { data: secretApprovalReqCount } = useGetSecretApprovalRequestCount({
|
||||
workspaceId
|
||||
});
|
||||
const { data: accessApprovalRequestCount } = useGetAccessRequestsCount({
|
||||
projectSlug
|
||||
});
|
||||
|
||||
// we only show the secret rotations v1 tab if they have existing rotations
|
||||
const { data: secretRotations } = useGetSecretRotations({
|
||||
workspaceId,
|
||||
options: {
|
||||
refetchOnMount: false
|
||||
}
|
||||
});
|
||||
|
||||
const pendingRequestsCount =
|
||||
(secretApprovalReqCount?.open || 0) + (accessApprovalRequestCount?.pendingCount || 0);
|
||||
|
||||
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 pb-2 pt-3 text-lg text-white">
|
||||
Project General
|
||||
</div>
|
||||
<div className="mt-2 flex-grow">
|
||||
<Menu>
|
||||
<Link
|
||||
to={`/projects/$projectId/${ProjectType.SecretManager}/overview` as const}
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}>{t("nav.menu.secrets")}</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to={`/projects/$projectId/${ProjectType.SecretManager}/integrations` as const}
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}>{t("nav.menu.integrations")}</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
{Boolean(secretRotations?.length) && (
|
||||
<Link
|
||||
to={`/${ProjectType.SecretManager}/$projectId/secret-rotation` as const}
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}>Secret Rotation</MenuItem>}
|
||||
</Link>
|
||||
)}
|
||||
<Link
|
||||
to={`/projects/$projectId/${ProjectType.SecretManager}/approval` as const}
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}>
|
||||
Approvals
|
||||
{Boolean(
|
||||
secretApprovalReqCount?.open || accessApprovalRequestCount?.pendingCount
|
||||
) && (
|
||||
<Badge variant="primary" className="ml-1.5">
|
||||
{pendingRequestsCount}
|
||||
</Badge>
|
||||
)}
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to={`/${currentWorkspace.type}/$projectId/settings` as const}
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}>{t("nav.menu.project-settings")}</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
</Menu>
|
||||
</div>
|
||||
</nav>
|
||||
</motion.div>
|
||||
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 dark:[color-scheme:dark]">
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
1
frontend/src/layouts/ProjectGeneralLayout/index.tsx
Normal file
1
frontend/src/layouts/ProjectGeneralLayout/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { ProjectGeneralLayout } from "./ProjectGeneralLayout";
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faDotCircle, faHome, faMobile, faWindowMaximize } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link, Outlet } from "@tanstack/react-router";
|
||||
import { Link, Outlet, useLocation } from "@tanstack/react-router";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
import {
|
||||
@@ -20,6 +20,8 @@ import { useProjectPermission, useWorkspace } from "@app/context";
|
||||
import { AssumePrivilegeModeBanner } from "./components/AssumePrivilegeModeBanner";
|
||||
import { useLocalStorageState } from "@app/hooks";
|
||||
import { ShouldWrap } from "@app/components/utilities/ShouldWrapComponent";
|
||||
import { getCurrentProductFromUrl } from "@app/helpers/project";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
enum SidebarStyle {
|
||||
Expanded = "expanded",
|
||||
@@ -31,6 +33,7 @@ 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 { currentWorkspace } = useWorkspace();
|
||||
const [sidebarStyle, setSidebarStyle] = useLocalStorageState(
|
||||
"project-sidebar-style",
|
||||
@@ -45,6 +48,13 @@ export const ProjectLayout = () => {
|
||||
const maxSidebarWidth =
|
||||
sidebarStyle === SidebarStyle.Collapsed ? MIN_SIDEBAR_SIZE : MAX_SIDEBAR_SIZE;
|
||||
|
||||
const currentProductType = getCurrentProductFromUrl(location.pathname);
|
||||
const isSecretManager = currentProductType === ProjectType.SecretManager;
|
||||
const isPki = currentProductType === ProjectType.CertificateManager;
|
||||
const isKms = currentProductType === ProjectType.KMS;
|
||||
const isSsh = currentProductType === ProjectType.SSH;
|
||||
const isSecretScanning = currentProductType === ProjectType.SecretScanning;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@@ -76,23 +86,18 @@ export const ProjectLayout = () => {
|
||||
to="/projects/$projectId/secret-manager/overview"
|
||||
params={{ projectId: currentWorkspace.id }}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem
|
||||
className="relative flex items-center gap-2 overflow-hidden"
|
||||
isSelected={isActive}
|
||||
leftIcon={
|
||||
<Lottie
|
||||
className="inline-block h-6 w-6 shrink-0"
|
||||
icon="sliding-carousel"
|
||||
/>
|
||||
}
|
||||
>
|
||||
{isActive && (
|
||||
<div className="absolute left-0 top-0 h-full w-0.5 bg-primary" />
|
||||
)}
|
||||
Secret Manager
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
className="relative flex items-center gap-2 overflow-hidden"
|
||||
isSelected={isSecretManager}
|
||||
leftIcon={
|
||||
<Lottie className="inline-block h-6 w-6 shrink-0" icon="sliding-carousel" />
|
||||
}
|
||||
>
|
||||
{isSecretManager && (
|
||||
<div className="absolute left-0 top-0 h-full w-0.5 bg-primary" />
|
||||
)}
|
||||
Secret Manager
|
||||
</MenuItem>
|
||||
</Link>
|
||||
</ShouldWrap>
|
||||
<ShouldWrap
|
||||
@@ -105,18 +110,14 @@ export const ProjectLayout = () => {
|
||||
to="/projects/$projectId/cert-manager/subscribers"
|
||||
params={{ projectId: currentWorkspace.id }}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem
|
||||
className="relative flex items-center gap-2 overflow-hidden"
|
||||
isSelected={isActive}
|
||||
leftIcon={<Lottie className="inline-block h-6 w-6 shrink-0" icon="note" />}
|
||||
>
|
||||
{isActive && (
|
||||
<div className="absolute left-0 top-0 h-full w-0.5 bg-primary" />
|
||||
)}
|
||||
PKI Manager
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
className="relative flex items-center gap-2 overflow-hidden"
|
||||
isSelected={isPki}
|
||||
leftIcon={<Lottie className="inline-block h-6 w-6 shrink-0" icon="note" />}
|
||||
>
|
||||
{isPki && <div className="absolute left-0 top-0 h-full w-0.5 bg-primary" />}
|
||||
PKI Manager
|
||||
</MenuItem>
|
||||
</Link>
|
||||
</ShouldWrap>
|
||||
<ShouldWrap
|
||||
@@ -129,20 +130,14 @@ export const ProjectLayout = () => {
|
||||
to="/projects/$projectId/kms/overview"
|
||||
params={{ projectId: currentWorkspace.id }}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem
|
||||
className="relative flex items-center gap-2 overflow-hidden"
|
||||
isSelected={isActive}
|
||||
leftIcon={
|
||||
<Lottie className="inline-block h-6 w-6 shrink-0" icon="unlock" />
|
||||
}
|
||||
>
|
||||
{isActive && (
|
||||
<div className="absolute left-0 top-0 h-full w-0.5 bg-primary" />
|
||||
)}
|
||||
KMS
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
className="relative flex items-center gap-2 overflow-hidden"
|
||||
isSelected={isKms}
|
||||
leftIcon={<Lottie className="inline-block h-6 w-6 shrink-0" icon="unlock" />}
|
||||
>
|
||||
{isKms && <div className="absolute left-0 top-0 h-full w-0.5 bg-primary" />}
|
||||
KMS
|
||||
</MenuItem>
|
||||
</Link>
|
||||
</ShouldWrap>
|
||||
<ShouldWrap
|
||||
@@ -155,20 +150,16 @@ export const ProjectLayout = () => {
|
||||
to="/projects/$projectId/ssh/overview"
|
||||
params={{ projectId: currentWorkspace.id }}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem
|
||||
className="relative flex items-center gap-2 overflow-hidden"
|
||||
isSelected={isActive}
|
||||
leftIcon={
|
||||
<Lottie className="inline-block h-6 w-6 shrink-0" icon="verified" />
|
||||
}
|
||||
>
|
||||
{isActive && (
|
||||
<div className="absolute left-0 top-0 h-full w-0.5 bg-primary" />
|
||||
)}
|
||||
SSH
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
className="relative flex items-center gap-2 overflow-hidden"
|
||||
isSelected={isSsh}
|
||||
leftIcon={
|
||||
<Lottie className="inline-block h-6 w-6 shrink-0" icon="verified" />
|
||||
}
|
||||
>
|
||||
{isSsh && <div className="absolute left-0 top-0 h-full w-0.5 bg-primary" />}
|
||||
SSH
|
||||
</MenuItem>
|
||||
</Link>
|
||||
</ShouldWrap>
|
||||
<ShouldWrap
|
||||
@@ -178,23 +169,21 @@ export const ProjectLayout = () => {
|
||||
position="right"
|
||||
>
|
||||
<Link
|
||||
to="/projects/$projectId/secret-scanning/findings"
|
||||
to="/projects/$projectId/secret-scanning/data-sources"
|
||||
params={{ projectId: currentWorkspace.id }}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem
|
||||
className="relative flex items-center gap-2 overflow-hidden"
|
||||
isSelected={isActive}
|
||||
leftIcon={
|
||||
<Lottie className="inline-block h-6 w-6 shrink-0" icon="secret-scan" />
|
||||
}
|
||||
>
|
||||
{isActive && (
|
||||
<div className="absolute left-0 top-0 h-full w-0.5 bg-primary" />
|
||||
)}
|
||||
Secret Scanning
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
className="relative flex items-center gap-2 overflow-hidden"
|
||||
isSelected={isSecretScanning}
|
||||
leftIcon={
|
||||
<Lottie className="inline-block h-6 w-6 shrink-0" icon="secret-scan" />
|
||||
}
|
||||
>
|
||||
{isSecretScanning && (
|
||||
<div className="absolute left-0 top-0 h-full w-0.5 bg-primary" />
|
||||
)}
|
||||
Secret Scanning
|
||||
</MenuItem>
|
||||
</Link>
|
||||
</ShouldWrap>
|
||||
</Menu>
|
||||
|
||||
@@ -11,10 +11,7 @@ import {
|
||||
useGetSecretApprovalRequestCount,
|
||||
useGetSecretRotations
|
||||
} from "@app/hooks/api";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
// 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 SecretManagerLayout = () => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
@@ -59,7 +56,7 @@ export const SecretManagerLayout = () => {
|
||||
<div className="mt-2 flex-grow">
|
||||
<Menu>
|
||||
<Link
|
||||
to={`/projects/$projectId/${ProjectType.SecretManager}/overview` as const}
|
||||
to="/projects/$projectId/secret-manager/overview"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
@@ -69,7 +66,7 @@ export const SecretManagerLayout = () => {
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to={`/projects/$projectId/${ProjectType.SecretManager}/integrations` as const}
|
||||
to="/projects/$projectId/secret-manager/integrations"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
@@ -80,7 +77,7 @@ export const SecretManagerLayout = () => {
|
||||
</Link>
|
||||
{Boolean(secretRotations?.length) && (
|
||||
<Link
|
||||
to={`/${ProjectType.SecretManager}/$projectId/secret-rotation` as const}
|
||||
to="/projects/$projectId/secret-manager/secret-rotation"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
@@ -89,7 +86,7 @@ export const SecretManagerLayout = () => {
|
||||
</Link>
|
||||
)}
|
||||
<Link
|
||||
to={`/projects/$projectId/${ProjectType.SecretManager}/approval` as const}
|
||||
to="/projects/$projectId/secret-manager/approval"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
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 SecretScanningLayout = () => {
|
||||
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 pb-2 pt-3 text-lg text-white">
|
||||
Secret Scanning
|
||||
</div>
|
||||
<div className="mt-2 flex-grow">
|
||||
<Menu>
|
||||
<Link
|
||||
to="/projects/$projectId/secret-scanning/data-sources"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}> Data Sources</MenuItem>}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/$projectId/secret-scanning/findings"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}>Findings</MenuItem>}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/$projectId/cert-manager/settings"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}>Settings</MenuItem>}
|
||||
</Link>
|
||||
</Menu>
|
||||
</div>
|
||||
</nav>
|
||||
</motion.div>
|
||||
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 dark:[color-scheme:dark]">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
1
frontend/src/layouts/SecretScanningLayout/index.tsx
Normal file
1
frontend/src/layouts/SecretScanningLayout/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { SecretScanningLayout } from "./SecretScanningLayout";
|
||||
100
frontend/src/layouts/SshLayout/SshLayout.tsx
Normal file
100
frontend/src/layouts/SshLayout/SshLayout.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import { Link, Outlet } from "@tanstack/react-router";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
import { Menu, MenuItem } from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
|
||||
export const SshLayout = () => {
|
||||
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 pb-2 pt-3 text-lg text-white">
|
||||
SSH
|
||||
</div>
|
||||
<div className="mt-2 flex-grow">
|
||||
<Menu>
|
||||
<Link
|
||||
to="/projects/$projectId/ssh/overview"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}>Hosts</MenuItem>}
|
||||
</Link>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Read}
|
||||
a={ProjectPermissionSub.SshCertificateAuthorities}
|
||||
>
|
||||
{(isAllowed) =>
|
||||
isAllowed && (
|
||||
<Link
|
||||
to="/projects/$projectId/ssh/cas"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}> Certificate Templates</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
</ProjectPermissionCan>
|
||||
<Link
|
||||
to="/projects/$projectId/cert-manager/certificates"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}> Certificates</MenuItem>}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/$projectId/cert-manager/certificate-authorities"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}> Certificates Authorities</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/$projectId/cert-manager/alerting"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}>Alerting</MenuItem>}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/$projectId/cert-manager/settings"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => <MenuItem isSelected={isActive}>Settings</MenuItem>}
|
||||
</Link>
|
||||
</Menu>
|
||||
</div>
|
||||
</nav>
|
||||
</motion.div>
|
||||
<div className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 p-4 dark:[color-scheme:dark]">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
1
frontend/src/layouts/SshLayout/index.tsx
Normal file
1
frontend/src/layouts/SshLayout/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { SshLayout } from "./SshLayout";
|
||||
@@ -1,45 +1,14 @@
|
||||
import { createFileRoute, linkOptions } from '@tanstack/react-router'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { workspaceKeys } from '@app/hooks/api'
|
||||
import {
|
||||
fetchUserProjectPermissions,
|
||||
roleQueryKeys,
|
||||
} from '@app/hooks/api/roles/queries'
|
||||
import { fetchWorkspaceById } from '@app/hooks/api/workspace/queries'
|
||||
import { ProjectLayout } from '@app/layouts/ProjectLayout'
|
||||
import { PkiManagerLayout } from "@app/layouts/PkiManagerLayout";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/cert-manager/_cert-manager-layout',
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/cert-manager/_cert-manager-layout"
|
||||
)({
|
||||
component: ProjectLayout,
|
||||
beforeLoad: async ({ params, context }) => {
|
||||
const project = await context.queryClient.ensureQueryData({
|
||||
queryKey: workspaceKeys.getWorkspaceById(params.projectId),
|
||||
queryFn: () => fetchWorkspaceById(params.projectId),
|
||||
})
|
||||
|
||||
await context.queryClient.ensureQueryData({
|
||||
queryKey: roleQueryKeys.getUserProjectPermissions({
|
||||
workspaceId: params.projectId,
|
||||
}),
|
||||
queryFn: () =>
|
||||
fetchUserProjectPermissions({ workspaceId: params.projectId }),
|
||||
})
|
||||
|
||||
component: PkiManagerLayout,
|
||||
beforeLoad: async () => {
|
||||
return {
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'Cert Managers',
|
||||
link: linkOptions({ to: '/organization/cert-manager/overview' }),
|
||||
},
|
||||
{
|
||||
label: project.name,
|
||||
link: linkOptions({
|
||||
to: '/cert-manager/$projectId/subscribers',
|
||||
params: { projectId: project.id },
|
||||
}),
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
})
|
||||
breadcrumbs: []
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,45 +1,14 @@
|
||||
import { createFileRoute, linkOptions } from '@tanstack/react-router'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { workspaceKeys } from '@app/hooks/api'
|
||||
import {
|
||||
fetchUserProjectPermissions,
|
||||
roleQueryKeys,
|
||||
} from '@app/hooks/api/roles/queries'
|
||||
import { fetchWorkspaceById } from '@app/hooks/api/workspace/queries'
|
||||
import { ProjectLayout } from '@app/layouts/ProjectLayout'
|
||||
import { KmsLayout } from "@app/layouts/KmsLayout";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/kms/_kms-layout',
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/kms/_kms-layout"
|
||||
)({
|
||||
component: ProjectLayout,
|
||||
beforeLoad: async ({ params, context }) => {
|
||||
const project = await context.queryClient.ensureQueryData({
|
||||
queryKey: workspaceKeys.getWorkspaceById(params.projectId),
|
||||
queryFn: () => fetchWorkspaceById(params.projectId),
|
||||
})
|
||||
|
||||
await context.queryClient.ensureQueryData({
|
||||
queryKey: roleQueryKeys.getUserProjectPermissions({
|
||||
workspaceId: params.projectId,
|
||||
}),
|
||||
queryFn: () =>
|
||||
fetchUserProjectPermissions({ workspaceId: params.projectId }),
|
||||
})
|
||||
|
||||
component: KmsLayout,
|
||||
beforeLoad: async () => {
|
||||
return {
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'KMS',
|
||||
link: linkOptions({ to: '/organization/kms/overview' }),
|
||||
},
|
||||
{
|
||||
label: project.name,
|
||||
link: linkOptions({
|
||||
to: '/kms/$projectId/overview',
|
||||
params: { projectId: project.id },
|
||||
}),
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
})
|
||||
breadcrumbs: []
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
3
frontend/src/pages/project/SettingsPage/SettingsPage.tsx
Normal file
3
frontend/src/pages/project/SettingsPage/SettingsPage.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export const SettingsPage = () => {
|
||||
return <div>Settings</div>;
|
||||
};
|
||||
24
frontend/src/pages/project/SettingsPage/route.tsx
Normal file
24
frontend/src/pages/project/SettingsPage/route.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { SettingsPage } from "./SettingsPage";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/ssh/_ssh-layout/access-management"
|
||||
)({
|
||||
component: SettingsPage,
|
||||
beforeLoad: ({ context, params }) => {
|
||||
return {
|
||||
breadcrumbs: [
|
||||
...context.breadcrumbs
|
||||
// {
|
||||
// label: "Access Control",
|
||||
// link: linkOptions({
|
||||
// to: "/ssh/$projectId/access-management",
|
||||
// params: {
|
||||
// projectId: params.projectId
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -1,45 +1,14 @@
|
||||
import { createFileRoute, linkOptions } from '@tanstack/react-router'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { workspaceKeys } from '@app/hooks/api'
|
||||
import {
|
||||
fetchUserProjectPermissions,
|
||||
roleQueryKeys,
|
||||
} from '@app/hooks/api/roles/queries'
|
||||
import { fetchWorkspaceById } from '@app/hooks/api/workspace/queries'
|
||||
import { ProjectLayout } from '@app/layouts/ProjectLayout'
|
||||
import { SecretScanningLayout } from "@app/layouts/SecretScanningLayout";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/secret-scanning/_secret-scanning-layout',
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/secret-scanning/_secret-scanning-layout"
|
||||
)({
|
||||
component: ProjectLayout,
|
||||
beforeLoad: async ({ params, context }) => {
|
||||
const project = await context.queryClient.ensureQueryData({
|
||||
queryKey: workspaceKeys.getWorkspaceById(params.projectId),
|
||||
queryFn: () => fetchWorkspaceById(params.projectId),
|
||||
})
|
||||
|
||||
await context.queryClient.ensureQueryData({
|
||||
queryKey: roleQueryKeys.getUserProjectPermissions({
|
||||
workspaceId: params.projectId,
|
||||
}),
|
||||
queryFn: () =>
|
||||
fetchUserProjectPermissions({ workspaceId: params.projectId }),
|
||||
})
|
||||
|
||||
component: SecretScanningLayout,
|
||||
beforeLoad: async () => {
|
||||
return {
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'Secret Scanning',
|
||||
link: linkOptions({ to: '/organization/secret-scanning/overview' }),
|
||||
},
|
||||
{
|
||||
label: project.name,
|
||||
link: linkOptions({
|
||||
to: '/secret-scanning/$projectId/data-sources',
|
||||
params: { projectId: project.id },
|
||||
}),
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
})
|
||||
breadcrumbs: []
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,45 +1,14 @@
|
||||
import { createFileRoute, linkOptions } from '@tanstack/react-router'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { workspaceKeys } from '@app/hooks/api'
|
||||
import {
|
||||
fetchUserProjectPermissions,
|
||||
roleQueryKeys,
|
||||
} from '@app/hooks/api/roles/queries'
|
||||
import { fetchWorkspaceById } from '@app/hooks/api/workspace/queries'
|
||||
import { ProjectLayout } from '@app/layouts/ProjectLayout'
|
||||
import { SshLayout } from "@app/layouts/SshLayout";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/ssh/_ssh-layout',
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/$projectId/_project-layout/ssh/_ssh-layout"
|
||||
)({
|
||||
component: ProjectLayout,
|
||||
beforeLoad: async ({ params, context }) => {
|
||||
const project = await context.queryClient.ensureQueryData({
|
||||
queryKey: workspaceKeys.getWorkspaceById(params.projectId),
|
||||
queryFn: () => fetchWorkspaceById(params.projectId),
|
||||
})
|
||||
|
||||
await context.queryClient.ensureQueryData({
|
||||
queryKey: roleQueryKeys.getUserProjectPermissions({
|
||||
workspaceId: params.projectId,
|
||||
}),
|
||||
queryFn: () =>
|
||||
fetchUserProjectPermissions({ workspaceId: params.projectId }),
|
||||
})
|
||||
|
||||
component: SshLayout,
|
||||
beforeLoad: async () => {
|
||||
return {
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: 'SSH',
|
||||
link: linkOptions({ to: '/organization/ssh/overview' }),
|
||||
},
|
||||
{
|
||||
label: project.name,
|
||||
link: linkOptions({
|
||||
to: '/ssh/$projectId/overview',
|
||||
params: { projectId: project.id },
|
||||
}),
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
})
|
||||
breadcrumbs: []
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user