Merge pull request #3366 from Infisical/update-all-projcts-ui

Update UI for all projects/my projects
This commit is contained in:
Akhil Mohan
2025-04-07 15:29:30 +05:30
committed by GitHub
4 changed files with 117 additions and 51 deletions

View File

@@ -2,21 +2,20 @@
import { useState } from "react";
import { Helmet } from "react-helmet";
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 { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { OrgPermissionCan } from "@app/components/permissions";
import { NewProjectModal } from "@app/components/projects";
import { Button, PageHeader } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects, useSubscription } from "@app/context";
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 { ProjectListView } from "./components/ProjectListToggle";
import { ProjectListToggle, ProjectListView } from "./components/ProjectListToggle";
const formatDescription = (type: ProjectType) => {
if (type === ProjectType.SecretManager)
@@ -80,33 +79,29 @@ export const ProductOverviewPage = ({ type }: Props) => {
</div>
)}
<div className="mb-4 flex flex-col items-start justify-start">
<PageHeader title="Projects" description={formatDescription(type)}>
<OrgPermissionCan I={OrgPermissionActions.Create} an={OrgPermissionSubjects.Workspace}>
{(isAllowed) => (
<Button
isDisabled={!isAllowed}
colorSchema="primary"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => {
if (isAddingProjectsAllowed) {
handlePopUpOpen("addNewWs");
} else {
handlePopUpOpen("upgradePlan");
}
}}
className="ml-2"
>
Add New Project
</Button>
)}
</OrgPermissionCan>
</PageHeader>
<PageHeader
title={
<div className="flex items-center gap-4">
<ProjectListToggle value={projectListView} onChange={setProjectListView} />
</div>
}
description={formatDescription(type)}
/>
</div>
{projectListView === ProjectListView.MyProjects && (
<MyProjectView type={type} onListViewToggle={setProjectListView} />
)}
{projectListView === ProjectListView.AllProjects && (
<AllProjectView type={type} onListViewToggle={setProjectListView} />
{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}
/>
)}
<NewProjectModal
isOpen={popUp.addNewWs.isOpen}

View File

@@ -6,13 +6,15 @@ import {
faCheck,
faFolderOpen,
faList,
faMagnifyingGlass
faMagnifyingGlass,
faPlus
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate } from "@tanstack/react-router";
import { twMerge } from "tailwind-merge";
import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
import {
Button,
FormControl,
@@ -24,16 +26,17 @@ import {
Skeleton,
Tooltip
} from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { getProjectHomePage } from "@app/helpers/project";
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 { ProjectListToggle, ProjectListView } from "./ProjectListToggle";
type Props = {
type: ProjectType;
onListViewToggle: (value: ProjectListView) => void;
onAddNewProject: () => void;
onUpgradePlan: () => void;
isAddingProjectsAllowed: boolean;
};
type RequestAccessModalProps = {
@@ -83,7 +86,12 @@ const RequestAccessModal = ({ projectId, onPopUpToggle }: RequestAccessModalProp
);
};
export const AllProjectView = ({ type, onListViewToggle }: Props) => {
export const AllProjectView = ({
type,
onAddNewProject,
onUpgradePlan,
isAddingProjectsAllowed
}: Props) => {
const navigate = useNavigate();
const [searchFilter, setSearchFilter] = useState("");
const [debouncedSearch] = useDebounce(searchFilter);
@@ -119,11 +127,10 @@ export const AllProjectView = ({ type, onListViewToggle }: Props) => {
return (
<div>
<div className="flex w-full flex-row">
<ProjectListToggle value={ProjectListView.AllProjects} onChange={onListViewToggle} />
<div className="flex-grow" />
<Input
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..."
value={searchFilter}
onChange={(e) => setSearchFilter(e.target.value)}
@@ -163,6 +170,25 @@ export const AllProjectView = ({ type, onListViewToggle }: Props) => {
<FontAwesomeIcon icon={faList} />
</IconButton>
</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 className="mt-4 w-full rounded-md">
{isProjectLoading &&
@@ -266,7 +292,7 @@ export const AllProjectView = ({ type, onListViewToggle }: Props) => {
>
<ModalContent
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
onPopUpToggle={() => handlePopUpToggle("requestAccessConfirmation")}

View File

@@ -7,6 +7,7 @@ import {
faBorderAll,
faList,
faMagnifyingGlass,
faPlus,
faSearch,
faStar as faSolidStar
} from "@fortawesome/free-solid-svg-icons";
@@ -14,8 +15,9 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate } from "@tanstack/react-router";
import { createNotification } from "@app/components/notifications";
import { IconButton, Input, Pagination, Skeleton, Tooltip } from "@app/components/v2";
import { useOrganization } from "@app/context";
import { OrgPermissionCan } from "@app/components/permissions";
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 { usePagination, useResetPageHelper } from "@app/hooks";
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 { ProjectType, Workspace } from "@app/hooks/api/workspace/types";
import { ProjectListToggle, ProjectListView } from "./ProjectListToggle";
type Props = {
type: ProjectType;
onListViewToggle: (value: ProjectListView) => void;
onAddNewProject: () => void;
onUpgradePlan: () => void;
isAddingProjectsAllowed: boolean;
};
enum ProjectOrderBy {
@@ -40,7 +42,12 @@ enum ProjectsViewMode {
LIST = "list"
}
export const MyProjectView = ({ type, onListViewToggle }: Props) => {
export const MyProjectView = ({
type,
onAddNewProject,
onUpgradePlan,
isAddingProjectsAllowed
}: Props) => {
const navigate = useNavigate();
const { currentOrg } = useOrganization();
@@ -318,11 +325,10 @@ export const MyProjectView = ({ type, onListViewToggle }: Props) => {
return (
<div>
<div className="flex w-full flex-row">
<ProjectListToggle value={ProjectListView.MyProjects} onChange={onListViewToggle} />
<div className="flex-grow" />
<Input
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..."
value={searchFilter}
onChange={(e) => setSearchFilter(e.target.value)}
@@ -376,6 +382,25 @@ export const MyProjectView = ({ type, onListViewToggle }: Props) => {
<FontAwesomeIcon icon={faList} />
</IconButton>
</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>
{projectsComponents}
{!isProjectViewLoading && Boolean(filteredWorkspaces.length) && (

View File

@@ -1,3 +1,6 @@
import { faChevronDown } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Select, SelectItem } from "@app/components/v2";
export enum ProjectListView {
@@ -11,10 +14,27 @@ type Props = {
};
export const ProjectListToggle = ({ value, onChange }: Props) => {
const getDisplayText = (listView: ProjectListView) => {
return listView === ProjectListView.MyProjects ? "My Projects" : "All Projects";
};
return (
<Select value={value} onValueChange={onChange}>
<SelectItem value={ProjectListView.MyProjects}>My Projects</SelectItem>
<SelectItem value={ProjectListView.AllProjects}>All Projects</SelectItem>
</Select>
<div className="group relative flex cursor-pointer items-center gap-2">
<h1 className="text-3xl font-semibold transition-colors group-hover:text-gray-500">
{getDisplayText(value)}
</h1>
<Select
value={value}
onValueChange={onChange}
className="absolute left-0 top-0 h-full w-full cursor-pointer opacity-0"
>
<SelectItem value={ProjectListView.MyProjects}>My Projects</SelectItem>
<SelectItem value={ProjectListView.AllProjects}>All Projects</SelectItem>
</Select>
<FontAwesomeIcon
icon={faChevronDown}
className="text-lg transition-colors group-hover:text-gray-500"
/>
</div>
);
};