mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 23:28:25 +00:00
Update UI for all projects/my projects
Updated the UI so that instead of having a dropdown to select + a heading on the project overview page, the heading becomes the drop down for my projects vs all projects
This commit is contained in:
+24
-28
@@ -2,21 +2,20 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { faExclamationCircle, faPlus } from "@fortawesome/free-solid-svg-icons";
|
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||||
import { OrgPermissionCan } from "@app/components/permissions";
|
|
||||||
import { NewProjectModal } from "@app/components/projects";
|
import { NewProjectModal } from "@app/components/projects";
|
||||||
import { Button, PageHeader } from "@app/components/v2";
|
import { PageHeader } from "@app/components/v2";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects, useSubscription } from "@app/context";
|
import { useSubscription } from "@app/context";
|
||||||
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
|
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
|
||||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||||
import { usePopUp } from "@app/hooks/usePopUp";
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
import { AllProjectView } from "./components/AllProjectView";
|
import { AllProjectView } from "./components/AllProjectView";
|
||||||
import { MyProjectView } from "./components/MyProjectView";
|
import { MyProjectView } from "./components/MyProjectView";
|
||||||
import { ProjectListView } from "./components/ProjectListToggle";
|
import { ProjectListToggle, ProjectListView } from "./components/ProjectListToggle";
|
||||||
|
|
||||||
const formatDescription = (type: ProjectType) => {
|
const formatDescription = (type: ProjectType) => {
|
||||||
if (type === ProjectType.SecretManager)
|
if (type === ProjectType.SecretManager)
|
||||||
@@ -80,33 +79,30 @@ export const ProductOverviewPage = ({ type }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="mb-4 flex flex-col items-start justify-start">
|
<div className="mb-4 flex flex-col items-start justify-start">
|
||||||
<PageHeader title="Projects" description={formatDescription(type)}>
|
<PageHeader
|
||||||
<OrgPermissionCan I={OrgPermissionActions.Create} an={OrgPermissionSubjects.Workspace}>
|
title={
|
||||||
{(isAllowed) => (
|
<div className="flex items-center gap-4">
|
||||||
<Button
|
<ProjectListToggle value={projectListView} onChange={setProjectListView} />
|
||||||
isDisabled={!isAllowed}
|
</div>
|
||||||
colorSchema="primary"
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
onClick={() => {
|
|
||||||
if (isAddingProjectsAllowed) {
|
|
||||||
handlePopUpOpen("addNewWs");
|
|
||||||
} else {
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}
|
}
|
||||||
}}
|
description={formatDescription(type)}
|
||||||
className="ml-2"
|
|
||||||
>
|
>
|
||||||
Add New Project
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</OrgPermissionCan>
|
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
</div>
|
</div>
|
||||||
{projectListView === ProjectListView.MyProjects && (
|
{projectListView === ProjectListView.MyProjects ? (
|
||||||
<MyProjectView type={type} onListViewToggle={setProjectListView} />
|
<MyProjectView
|
||||||
)}
|
type={type}
|
||||||
{projectListView === ProjectListView.AllProjects && (
|
onAddNewProject={() => handlePopUpOpen("addNewWs")}
|
||||||
<AllProjectView type={type} onListViewToggle={setProjectListView} />
|
onUpgradePlan={() => handlePopUpOpen("upgradePlan")}
|
||||||
|
isAddingProjectsAllowed={isAddingProjectsAllowed}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<AllProjectView
|
||||||
|
type={type}
|
||||||
|
onAddNewProject={() => handlePopUpOpen("addNewWs")}
|
||||||
|
onUpgradePlan={() => handlePopUpOpen("upgradePlan")}
|
||||||
|
isAddingProjectsAllowed={isAddingProjectsAllowed}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
<NewProjectModal
|
<NewProjectModal
|
||||||
isOpen={popUp.addNewWs.isOpen}
|
isOpen={popUp.addNewWs.isOpen}
|
||||||
|
|||||||
+29
-8
@@ -6,13 +6,15 @@ import {
|
|||||||
faCheck,
|
faCheck,
|
||||||
faFolderOpen,
|
faFolderOpen,
|
||||||
faList,
|
faList,
|
||||||
faMagnifyingGlass
|
faMagnifyingGlass,
|
||||||
|
faPlus
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -27,13 +29,14 @@ import {
|
|||||||
import { getProjectHomePage } from "@app/helpers/project";
|
import { getProjectHomePage } from "@app/helpers/project";
|
||||||
import { useDebounce, usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
import { useDebounce, usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
||||||
import { useRequestProjectAccess, useSearchProjects } from "@app/hooks/api";
|
import { useRequestProjectAccess, useSearchProjects } from "@app/hooks/api";
|
||||||
|
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
||||||
import { ProjectType, Workspace } from "@app/hooks/api/workspace/types";
|
import { ProjectType, Workspace } from "@app/hooks/api/workspace/types";
|
||||||
|
|
||||||
import { ProjectListToggle, ProjectListView } from "./ProjectListToggle";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
type: ProjectType;
|
type: ProjectType;
|
||||||
onListViewToggle: (value: ProjectListView) => void;
|
onAddNewProject: () => void;
|
||||||
|
onUpgradePlan: () => void;
|
||||||
|
isAddingProjectsAllowed: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type RequestAccessModalProps = {
|
type RequestAccessModalProps = {
|
||||||
@@ -83,7 +86,7 @@ const RequestAccessModal = ({ projectId, onPopUpToggle }: RequestAccessModalProp
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AllProjectView = ({ type, onListViewToggle }: Props) => {
|
export const AllProjectView = ({ type, onAddNewProject, onUpgradePlan, isAddingProjectsAllowed }: Props) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [searchFilter, setSearchFilter] = useState("");
|
const [searchFilter, setSearchFilter] = useState("");
|
||||||
const [debouncedSearch] = useDebounce(searchFilter);
|
const [debouncedSearch] = useDebounce(searchFilter);
|
||||||
@@ -119,11 +122,10 @@ export const AllProjectView = ({ type, onListViewToggle }: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex w-full flex-row">
|
<div className="flex w-full flex-row">
|
||||||
<ProjectListToggle value={ProjectListView.AllProjects} onChange={onListViewToggle} />
|
|
||||||
<div className="flex-grow" />
|
<div className="flex-grow" />
|
||||||
<Input
|
<Input
|
||||||
className="h-[2.3rem] bg-mineshaft-800 text-sm placeholder-mineshaft-50 duration-200 focus:bg-mineshaft-700/80"
|
className="h-[2.3rem] bg-mineshaft-800 text-sm placeholder-mineshaft-50 duration-200 focus:bg-mineshaft-700/80"
|
||||||
containerClassName="max-w-md"
|
containerClassName="w-full"
|
||||||
placeholder="Search by project name..."
|
placeholder="Search by project name..."
|
||||||
value={searchFilter}
|
value={searchFilter}
|
||||||
onChange={(e) => setSearchFilter(e.target.value)}
|
onChange={(e) => setSearchFilter(e.target.value)}
|
||||||
@@ -163,6 +165,25 @@ export const AllProjectView = ({ type, onListViewToggle }: Props) => {
|
|||||||
<FontAwesomeIcon icon={faList} />
|
<FontAwesomeIcon icon={faList} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
<OrgPermissionCan I={OrgPermissionActions.Create} an={OrgPermissionSubjects.Workspace}>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Button
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
colorSchema="primary"
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
onClick={() => {
|
||||||
|
if (isAddingProjectsAllowed) {
|
||||||
|
onAddNewProject();
|
||||||
|
} else {
|
||||||
|
onUpgradePlan();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="ml-2"
|
||||||
|
>
|
||||||
|
Add New Project
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 w-full rounded-md">
|
<div className="mt-4 w-full rounded-md">
|
||||||
{isProjectLoading &&
|
{isProjectLoading &&
|
||||||
@@ -266,7 +287,7 @@ export const AllProjectView = ({ type, onListViewToggle }: Props) => {
|
|||||||
>
|
>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title="Confirm Access Request"
|
title="Confirm Access Request"
|
||||||
subTitle={`Requesting access to project ${requestedWorkspaceDetails?.name}. You may include a note for the admins.`}
|
subTitle={`Requesting access to project ${requestedWorkspaceDetails?.name}. You may include an optional note for project admins to review your request.`}
|
||||||
>
|
>
|
||||||
<RequestAccessModal
|
<RequestAccessModal
|
||||||
onPopUpToggle={() => handlePopUpToggle("requestAccessConfirmation")}
|
onPopUpToggle={() => handlePopUpToggle("requestAccessConfirmation")}
|
||||||
|
|||||||
+28
-8
@@ -7,6 +7,7 @@ import {
|
|||||||
faBorderAll,
|
faBorderAll,
|
||||||
faList,
|
faList,
|
||||||
faMagnifyingGlass,
|
faMagnifyingGlass,
|
||||||
|
faPlus,
|
||||||
faSearch,
|
faSearch,
|
||||||
faStar as faSolidStar
|
faStar as faSolidStar
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
@@ -14,8 +15,9 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { IconButton, Input, Pagination, Skeleton, Tooltip } from "@app/components/v2";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
import { useOrganization } from "@app/context";
|
import { Button, IconButton, Input, Pagination, Skeleton, Tooltip } from "@app/components/v2";
|
||||||
|
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||||
import { getProjectHomePage } from "@app/helpers/project";
|
import { getProjectHomePage } from "@app/helpers/project";
|
||||||
import { usePagination, useResetPageHelper } from "@app/hooks";
|
import { usePagination, useResetPageHelper } from "@app/hooks";
|
||||||
import { useGetUserWorkspaces } from "@app/hooks/api";
|
import { useGetUserWorkspaces } from "@app/hooks/api";
|
||||||
@@ -24,11 +26,11 @@ import { useUpdateUserProjectFavorites } from "@app/hooks/api/users/mutation";
|
|||||||
import { useGetUserProjectFavorites } from "@app/hooks/api/users/queries";
|
import { useGetUserProjectFavorites } from "@app/hooks/api/users/queries";
|
||||||
import { ProjectType, Workspace } from "@app/hooks/api/workspace/types";
|
import { ProjectType, Workspace } from "@app/hooks/api/workspace/types";
|
||||||
|
|
||||||
import { ProjectListToggle, ProjectListView } from "./ProjectListToggle";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
type: ProjectType;
|
type: ProjectType;
|
||||||
onListViewToggle: (value: ProjectListView) => void;
|
onAddNewProject: () => void;
|
||||||
|
onUpgradePlan: () => void;
|
||||||
|
isAddingProjectsAllowed: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ProjectOrderBy {
|
enum ProjectOrderBy {
|
||||||
@@ -40,7 +42,7 @@ enum ProjectsViewMode {
|
|||||||
LIST = "list"
|
LIST = "list"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MyProjectView = ({ type, onListViewToggle }: Props) => {
|
export const MyProjectView = ({ type, onAddNewProject, onUpgradePlan, isAddingProjectsAllowed }: Props) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
|
|
||||||
@@ -318,11 +320,10 @@ export const MyProjectView = ({ type, onListViewToggle }: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex w-full flex-row">
|
<div className="flex w-full flex-row">
|
||||||
<ProjectListToggle value={ProjectListView.MyProjects} onChange={onListViewToggle} />
|
|
||||||
<div className="flex-grow" />
|
<div className="flex-grow" />
|
||||||
<Input
|
<Input
|
||||||
className="h-[2.3rem] bg-mineshaft-800 text-sm placeholder-mineshaft-50 duration-200 focus:bg-mineshaft-700/80"
|
className="h-[2.3rem] bg-mineshaft-800 text-sm placeholder-mineshaft-50 duration-200 focus:bg-mineshaft-700/80"
|
||||||
containerClassName="max-w-md"
|
containerClassName="w-full"
|
||||||
placeholder="Search by project name..."
|
placeholder="Search by project name..."
|
||||||
value={searchFilter}
|
value={searchFilter}
|
||||||
onChange={(e) => setSearchFilter(e.target.value)}
|
onChange={(e) => setSearchFilter(e.target.value)}
|
||||||
@@ -376,6 +377,25 @@ export const MyProjectView = ({ type, onListViewToggle }: Props) => {
|
|||||||
<FontAwesomeIcon icon={faList} />
|
<FontAwesomeIcon icon={faList} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
<OrgPermissionCan I={OrgPermissionActions.Create} an={OrgPermissionSubjects.Workspace}>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Button
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
colorSchema="primary"
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
onClick={() => {
|
||||||
|
if (isAddingProjectsAllowed) {
|
||||||
|
onAddNewProject();
|
||||||
|
} else {
|
||||||
|
onUpgradePlan();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="ml-2"
|
||||||
|
>
|
||||||
|
Add New Project
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
</div>
|
</div>
|
||||||
{projectsComponents}
|
{projectsComponents}
|
||||||
{!isProjectViewLoading && Boolean(filteredWorkspaces.length) && (
|
{!isProjectViewLoading && Boolean(filteredWorkspaces.length) && (
|
||||||
|
|||||||
+11
-1
@@ -1,4 +1,6 @@
|
|||||||
import { Select, SelectItem } from "@app/components/v2";
|
import { Select, SelectItem } from "@app/components/v2";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faChevronDown } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
export enum ProjectListView {
|
export enum ProjectListView {
|
||||||
MyProjects = "my-projects",
|
MyProjects = "my-projects",
|
||||||
@@ -11,10 +13,18 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ProjectListToggle = ({ value, onChange }: Props) => {
|
export const ProjectListToggle = ({ value, onChange }: Props) => {
|
||||||
|
const getDisplayText = (value: ProjectListView) => {
|
||||||
|
return value === ProjectListView.MyProjects ? "My Projects" : "All Projects";
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select value={value} onValueChange={onChange}>
|
<div className="flex items-center gap-2 relative group cursor-pointer">
|
||||||
|
<h1 className="text-3xl font-semibold group-hover:text-gray-500 transition-colors">{getDisplayText(value)}</h1>
|
||||||
|
<Select value={value} onValueChange={onChange} className="absolute left-0 top-0 w-full h-full opacity-0 cursor-pointer">
|
||||||
<SelectItem value={ProjectListView.MyProjects}>My Projects</SelectItem>
|
<SelectItem value={ProjectListView.MyProjects}>My Projects</SelectItem>
|
||||||
<SelectItem value={ProjectListView.AllProjects}>All Projects</SelectItem>
|
<SelectItem value={ProjectListView.AllProjects}>All Projects</SelectItem>
|
||||||
</Select>
|
</Select>
|
||||||
|
<FontAwesomeIcon icon={faChevronDown} className="group-hover:text-gray-500 transition-colors text-lg" />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user