feat: re-arranged org project pages

This commit is contained in:
=
2025-07-02 01:16:47 +05:30
parent 0d97fc27c7
commit b8f7ffbf53
45 changed files with 128 additions and 556 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ export const Route = createFileRoute("/_authenticate/_inject-org-details/admin/_
beforeLoad: ({ context }) => {
if (!context.user.superAdmin) {
throw redirect({
to: "/organization/secret-manager/overview"
to: "/organization/projects"
});
}
}
@@ -4,7 +4,6 @@ import { useServerConfig } from "@app/context";
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
import { queryClient } from "@app/hooks/api/reactQuery";
import { userKeys } from "@app/hooks/api/users";
import { ProjectType } from "@app/hooks/api/workspace/types";
export const navigateUserToOrg = async (navigate: NavigateFn, organizationId?: string) => {
const userOrgs = await fetchOrganizations();
@@ -13,7 +12,7 @@ export const navigateUserToOrg = async (navigate: NavigateFn, organizationId?: s
if (organizationId) {
localStorage.setItem("orgData.id", organizationId);
navigate({ to: "/organization/secret-manager/overview" });
navigate({ to: "/organization/projects" });
return;
}
@@ -21,7 +20,7 @@ export const navigateUserToOrg = async (navigate: NavigateFn, organizationId?: s
// user is part of at least 1 non-auth enforced org
const userOrg = nonAuthEnforcedOrgs[0] && nonAuthEnforcedOrgs[0].id;
localStorage.setItem("orgData.id", userOrg);
navigate({ to: `/organization/${ProjectType.SecretManager}/overview` as const });
navigate({ to: "/organization/projects" });
} else {
// user is not part of any non-auth enforced orgs
localStorage.removeItem("orgData.id");
@@ -28,7 +28,6 @@ import {
import { MfaMethod } from "@app/hooks/api/auth/types";
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
import { isLoggedIn } from "@app/hooks/api/reactQuery";
import { ProjectType } from "@app/hooks/api/workspace/types";
// eslint-disable-next-line new-cap
const client = new jsrp.client();
@@ -204,7 +203,7 @@ export const SignupInvitePage = () => {
localStorage.setItem("orgData.id", orgId);
navigate({
to: `/organization/${ProjectType.SecretManager}/overview` as const
to: "/organization/projects"
});
};
@@ -18,7 +18,6 @@ import { useToggle } from "@app/hooks";
import { completeAccountSignup, useSelectOrganization } from "@app/hooks/api/auth/queries";
import { MfaMethod } from "@app/hooks/api/auth/types";
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
import { ProjectType } from "@app/hooks/api/workspace/types";
// eslint-disable-next-line new-cap
const client = new jsrp.client();
@@ -207,7 +206,7 @@ export const UserInfoSSOStep = ({
localStorage.setItem("orgData.id", orgId);
navigate({
to: `/organization/${ProjectType.SecretManager}/overview` as const
to: "/organization/projects"
});
} catch (error) {
setIsLoading(false);
@@ -64,7 +64,7 @@ export const DeleteProjectSection = () => {
});
navigate({
to: `/organization/${ProjectType.SecretManager}/overview` as const
to: "/organization/projects"
});
handlePopUpClose("deleteWorkspace");
} catch (err) {
@@ -112,7 +112,7 @@ export const Route = createFileRoute("/_restrict-login-signup")({
throw redirect({ to: "/login/select-organization" });
}
throw redirect({
to: `/organization/${ProjectType.SecretManager}/overview` as const
to: "/organization/projects"
});
},
component: AuthConsentWrapper
@@ -5,6 +5,7 @@ import { OrgPermissionCan } from "@app/components/permissions";
import { OrgPermissionBillingActions, OrgPermissionSubjects } from "@app/context";
import { BillingTabGroup } from "./components";
import { PageHeader } from "@app/components/v2";
export const BillingPage = () => {
const { t } = useTranslation();
@@ -18,10 +19,11 @@ export const BillingPage = () => {
</Helmet>
<div className="flex h-full w-full justify-center bg-bunker-800 text-white">
<div className="w-full max-w-7xl px-6">
<div className="my-6">
<p className="text-3xl font-semibold text-gray-200">{t("billing.title")}</p>
<div />
</div>
<PageHeader
title={t("billing.title")}
description="View your billing plan, next billing cycle."
/>
<OrgPermissionCan
passThrough={false}
I={OrgPermissionBillingActions.Read}
@@ -5,5 +5,14 @@ import { BillingPage } from "./BillingPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/billing"
)({
component: BillingPage
component: BillingPage,
beforeLoad: () => {
return {
breadcrumbs: [
{
label: "Billing"
}
]
};
}
});
@@ -1,7 +0,0 @@
import { ProjectType } from "@app/hooks/api/workspace/types";
import { ProductOverviewPage } from "../SecretManagerOverviewPage/SecretManagerOverviewPage";
export const CertManagerOverviewPage = () => (
<ProductOverviewPage type={ProjectType.CertificateManager} />
);
@@ -1,17 +0,0 @@
import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { CertManagerOverviewPage } from "./CertManagerOverviewPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/overview"
)({
component: CertManagerOverviewPage,
context: () => ({
breadcrumbs: [
{
label: "Cert Management",
link: linkOptions({ to: "/organization/cert-manager/overview" })
}
]
})
});
@@ -44,7 +44,7 @@ type Props = {
const Content = ({ identityId, handlePopUpToggle }: Omit<Props, "popUp">) => {
const { currentOrg } = useOrganization();
const { data: workspaces = [] } = useGetUserWorkspaces({ type: "all" });
const { data: workspaces = [] } = useGetUserWorkspaces();
const { mutateAsync: addIdentityToWorkspace } = useAddIdentityToWorkspace();
const {
@@ -1,5 +0,0 @@
import { ProjectType } from "@app/hooks/api/workspace/types";
import { ProductOverviewPage } from "../SecretManagerOverviewPage/SecretManagerOverviewPage";
export const KmsOverviewPage = () => <ProductOverviewPage type={ProjectType.KMS} />;
@@ -1,17 +0,0 @@
import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { KmsOverviewPage } from "./KmsOverviewPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/kms/overview"
)({
component: KmsOverviewPage,
context: () => ({
breadcrumbs: [
{
label: "KMS",
link: linkOptions({ to: "/organization/kms/overview" })
}
]
})
});
@@ -10,31 +10,25 @@ import { NewProjectModal } from "@app/components/projects";
import { PageHeader } from "@app/components/v2";
import { useSubscription } from "@app/context";
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { usePopUp } from "@app/hooks/usePopUp";
import { AllProjectView } from "./components/AllProjectView";
import { MyProjectView } from "./components/MyProjectView";
import { ProjectListToggle, ProjectListView } from "./components/ProjectListToggle";
const formatDescription = (type: ProjectType) => {
if (type === ProjectType.SecretManager)
return "Securely store, manage, and rotate various application secrets, such as database credentials, API keys, etc.";
if (type === ProjectType.CertificateManager)
return "Manage your PKI infrastructure and issue digital certificates for services, applications, and devices.";
if (type === ProjectType.KMS)
return "Centralize the management of keys for cryptographic operations, such as encryption and decryption.";
if (type === ProjectType.SecretScanning)
return "Connect and monitor data sources to prevent secret leaks.";
return "Infisical SSH lets you issue SSH credentials to users for short-lived, secure SSH access to infrastructure.";
};
// const formatDescription = (type: ProjectType) => {
// if (type === ProjectType.SecretManager)
// return "Securely store, manage, and rotate various application secrets, such as database credentials, API keys, etc.";
// if (type === ProjectType.CertificateManager)
// return "Manage your PKI infrastructure and issue digital certificates for services, applications, and devices.";
// if (type === ProjectType.KMS)
// return "Centralize the management of keys for cryptographic operations, such as encryption and decryption.";
// if (type === ProjectType.SecretScanning)
// return "Connect and monitor data sources to prevent secret leaks.";
// return "Infisical SSH lets you issue SSH credentials to users for short-lived, secure SSH access to infrastructure.";
// };
type Props = {
type: ProjectType;
};
// #TODO: Update all the workspaceIds
export const ProductOverviewPage = ({ type }: Props) => {
export const ProjectsPage = () => {
const { t } = useTranslation();
const [projectListView, setProjectListView] = useState(ProjectListView.MyProjects);
@@ -87,19 +81,17 @@ export const ProductOverviewPage = ({ type }: Props) => {
<ProjectListToggle value={projectListView} onChange={setProjectListView} />
</div>
}
description={formatDescription(type)}
description="Your team's complete security toolkit - organized and ready when you need them."
/>
</div>
{projectListView === ProjectListView.MyProjects ? (
<MyProjectView
type={type}
onAddNewProject={() => handlePopUpOpen("addNewWs")}
onUpgradePlan={() => handlePopUpOpen("upgradePlan")}
isAddingProjectsAllowed={isAddingProjectsAllowed}
/>
) : (
<AllProjectView
type={type}
onAddNewProject={() => handlePopUpOpen("addNewWs")}
onUpgradePlan={() => handlePopUpOpen("upgradePlan")}
isAddingProjectsAllowed={isAddingProjectsAllowed}
@@ -108,7 +100,6 @@ export const ProductOverviewPage = ({ type }: Props) => {
<NewProjectModal
isOpen={popUp.addNewWs.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("addNewWs", isOpen)}
projectType={type}
/>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
@@ -118,7 +109,3 @@ export const ProductOverviewPage = ({ type }: Props) => {
</div>
);
};
export const SecretManagerOverviewPage = () => (
<ProductOverviewPage type={ProjectType.SecretManager} />
);
@@ -35,10 +35,9 @@ import {
} from "@app/helpers/userTablePreferences";
import { useDebounce, usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
import { useRequestProjectAccess, useSearchProjects } from "@app/hooks/api";
import { ProjectType, Workspace } from "@app/hooks/api/workspace/types";
import { Workspace } from "@app/hooks/api/workspace/types";
type Props = {
type: ProjectType;
onAddNewProject: () => void;
onUpgradePlan: () => void;
isAddingProjectsAllowed: boolean;
@@ -92,7 +91,6 @@ const RequestAccessModal = ({ projectId, onPopUpToggle }: RequestAccessModalProp
};
export const AllProjectView = ({
type,
onAddNewProject,
onUpgradePlan,
isAddingProjectsAllowed
@@ -125,7 +123,6 @@ export const AllProjectView = ({
const { data: searchedProjects, isPending: isProjectLoading } = useSearchProjects({
limit,
offset,
type,
name: debouncedSearch || undefined,
orderDirection
});
@@ -29,10 +29,9 @@ import { useGetUserWorkspaces } from "@app/hooks/api";
import { OrderByDirection } from "@app/hooks/api/generic/types";
import { useUpdateUserProjectFavorites } from "@app/hooks/api/users/mutation";
import { useGetUserProjectFavorites } from "@app/hooks/api/users/queries";
import { ProjectType, Workspace } from "@app/hooks/api/workspace/types";
import { Workspace } from "@app/hooks/api/workspace/types";
type Props = {
type: ProjectType;
onAddNewProject: () => void;
onUpgradePlan: () => void;
isAddingProjectsAllowed: boolean;
@@ -48,7 +47,6 @@ enum ProjectsViewMode {
}
export const MyProjectView = ({
type,
onAddNewProject,
onUpgradePlan,
isAddingProjectsAllowed
@@ -56,9 +54,7 @@ export const MyProjectView = ({
const navigate = useNavigate();
const { currentOrg } = useOrganization();
const { data: workspaces = [], isPending: isWorkspaceLoading } = useGetUserWorkspaces({
type
});
const { data: workspaces = [], isPending: isWorkspaceLoading } = useGetUserWorkspaces();
const {
setPage,
perPage,
@@ -202,11 +198,6 @@ export const MyProjectView = ({
</div>
<div className="flex w-full flex-row items-end justify-between place-self-end">
{type === ProjectType.SecretManager && (
<div className="mt-0 text-xs text-mineshaft-400">
{workspace.environments?.length || 0} environments
</div>
)}
<button type="button">
<div className="group ml-auto w-max cursor-pointer rounded-full border border-mineshaft-600 bg-mineshaft-900 px-4 py-2 text-sm text-mineshaft-300 transition-all hover:border-primary-500/80 hover:bg-primary-800/20 hover:text-mineshaft-200">
Explore{" "}
@@ -239,11 +230,6 @@ export const MyProjectView = ({
<div className="truncate text-sm text-mineshaft-100">{workspace.name}</div>
</div>
<div className="flex items-center justify-end sm:col-span-3 lg:col-span-2">
{type === ProjectType.SecretManager && (
<div className="text-center text-sm text-mineshaft-300">
{workspace.environments?.length || 0} environments
</div>
)}
{isFavorite ? (
<FontAwesomeIcon
icon={faSolidStar}
@@ -0,0 +1,16 @@
import { createFileRoute } from "@tanstack/react-router";
import { ProjectsPage } from "./ProjectsPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/projects"
)({
component: ProjectsPage,
context: () => ({
breadcrumbs: [
{
label: "Projects"
}
]
})
});
@@ -1,16 +0,0 @@
import { createFileRoute } from "@tanstack/react-router";
import { SecretManagerOverviewPage } from "./SecretManagerOverviewPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/overview"
)({
component: SecretManagerOverviewPage,
context: () => ({
breadcrumbs: [
{
label: "Secret Management"
}
]
})
});
@@ -1,7 +0,0 @@
import { ProjectType } from "@app/hooks/api/workspace/types";
import { ProductOverviewPage } from "../SecretManagerOverviewPage/SecretManagerOverviewPage";
export const SecretScanningOverviewPage = () => (
<ProductOverviewPage type={ProjectType.SecretScanning} />
);
@@ -1,17 +0,0 @@
import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { SecretScanningOverviewPage } from "./SecretScanningOverviewPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/secret-scanning/overview"
)({
component: SecretScanningOverviewPage,
context: () => ({
breadcrumbs: [
{
label: "Secret Scanning",
link: linkOptions({ to: "/organization/secret-scanning/overview" })
}
]
})
});
@@ -1,5 +0,0 @@
import { ProjectType } from "@app/hooks/api/workspace/types";
import { ProductOverviewPage } from "../SecretManagerOverviewPage/SecretManagerOverviewPage";
export const SshOverviewPage = () => <ProductOverviewPage type={ProjectType.SSH} />;
@@ -1,17 +0,0 @@
import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { SshOverviewPage } from "./SshOverviewPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview"
)({
component: SshOverviewPage,
context: () => ({
breadcrumbs: [
{
label: "SSH",
link: linkOptions({ to: "/organization/ssh/overview" })
}
]
})
});
@@ -12,7 +12,7 @@ import { useProjectPermission, useWorkspace } from "@app/context";
import { usePopUp } from "@app/hooks";
import { useGetWorkspaceById, useMigrateProjectToV3, workspaceKeys } from "@app/hooks/api";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { ProjectType, ProjectVersion } from "@app/hooks/api/workspace/types";
import { ProjectVersion } from "@app/hooks/api/workspace/types";
enum ProjectUpgradeStatus {
InProgress = "IN_PROGRESS",
@@ -54,7 +54,7 @@ export const SecretV2MigrationSection = () => {
createNotification({ type: "success", text: "Project upgrade completed successfully" });
migrateProjectToV3.reset();
queryClient.invalidateQueries({
queryKey: workspaceKeys.getAllUserWorkspace(ProjectType.SecretManager)
queryKey: workspaceKeys.getAllUserWorkspace()
});
}
}, [isProjectUpgraded, Boolean(migrateProjectToV3.data)]);