mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 16:26:16 +00:00
misc: added favorites to app layout selection
This commit is contained in:
@@ -5,13 +5,14 @@
|
|||||||
/* eslint-disable no-var */
|
/* eslint-disable no-var */
|
||||||
/* eslint-disable func-names */
|
/* eslint-disable func-names */
|
||||||
|
|
||||||
import { useEffect, useMemo } from "react";
|
import { useCallback, useEffect, useMemo } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { faGithub, faSlack } from "@fortawesome/free-brands-svg-icons";
|
import { faGithub, faSlack } from "@fortawesome/free-brands-svg-icons";
|
||||||
|
import { faStar } from "@fortawesome/free-regular-svg-icons";
|
||||||
import {
|
import {
|
||||||
faAngleDown,
|
faAngleDown,
|
||||||
faArrowLeft,
|
faArrowLeft,
|
||||||
@@ -23,7 +24,8 @@ import {
|
|||||||
faInfo,
|
faInfo,
|
||||||
faMobile,
|
faMobile,
|
||||||
faPlus,
|
faPlus,
|
||||||
faQuestion
|
faQuestion,
|
||||||
|
faStar as faSolidStar
|
||||||
} 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 { yupResolver } from "@hookform/resolvers/yup";
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
@@ -72,6 +74,8 @@ import {
|
|||||||
useRegisterUserAction,
|
useRegisterUserAction,
|
||||||
useSelectOrganization
|
useSelectOrganization
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
|
import { useUpdateUserProjectFavorites } from "@app/hooks/api/users/mutation";
|
||||||
|
import { useGetUserProjectFavorites } from "@app/hooks/api/users/queries";
|
||||||
import { navigateUserToOrg } from "@app/views/Login/Login.utils";
|
import { navigateUserToOrg } from "@app/views/Login/Login.utils";
|
||||||
import { CreateOrgModal } from "@app/views/Org/components";
|
import { CreateOrgModal } from "@app/views/Org/components";
|
||||||
|
|
||||||
@@ -122,6 +126,11 @@ export const AppLayout = ({ children }: LayoutProps) => {
|
|||||||
const { workspaces, currentWorkspace } = useWorkspace();
|
const { workspaces, currentWorkspace } = useWorkspace();
|
||||||
const { orgs, currentOrg } = useOrganization();
|
const { orgs, currentOrg } = useOrganization();
|
||||||
|
|
||||||
|
const { data: projectFavorites } = useGetUserProjectFavorites(currentOrg?.id!);
|
||||||
|
const { mutateAsync: updateUserProjectFavorites } = useUpdateUserProjectFavorites();
|
||||||
|
const nonFavoriteWorkspaces = workspaces.filter((w) => !projectFavorites?.includes(w.id));
|
||||||
|
const favoriteWorkspaces = workspaces.filter((w) => projectFavorites?.includes(w.id));
|
||||||
|
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
const workspaceId = currentWorkspace?.id || "";
|
const workspaceId = currentWorkspace?.id || "";
|
||||||
@@ -271,6 +280,30 @@ export const AppLayout = ({ children }: LayoutProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addProjectToFavorites = useCallback(
|
||||||
|
(projectId: string) => {
|
||||||
|
if (currentOrg?.id) {
|
||||||
|
updateUserProjectFavorites({
|
||||||
|
orgId: currentOrg?.id,
|
||||||
|
projectFavorites: [...(projectFavorites || []), projectId]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentOrg, projectFavorites]
|
||||||
|
);
|
||||||
|
|
||||||
|
const removeProjectFromFavorites = useCallback(
|
||||||
|
(projectId: string) => {
|
||||||
|
if (currentOrg?.id) {
|
||||||
|
updateUserProjectFavorites({
|
||||||
|
orgId: currentOrg?.id,
|
||||||
|
projectFavorites: [...(projectFavorites || []).filter((entry) => entry !== projectId)]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentOrg, projectFavorites]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="dark hidden h-screen w-full flex-col overflow-x-hidden md:flex">
|
<div className="dark hidden h-screen w-full flex-col overflow-x-hidden md:flex">
|
||||||
@@ -451,19 +484,47 @@ export const AppLayout = ({ children }: LayoutProps) => {
|
|||||||
dropdownContainerClassName="text-bunker-200 bg-mineshaft-800 border border-mineshaft-600 z-50 max-h-96 border-gray-700"
|
dropdownContainerClassName="text-bunker-200 bg-mineshaft-800 border border-mineshaft-600 z-50 max-h-96 border-gray-700"
|
||||||
>
|
>
|
||||||
<div className="no-scrollbar::-webkit-scrollbar h-full no-scrollbar">
|
<div className="no-scrollbar::-webkit-scrollbar h-full no-scrollbar">
|
||||||
{workspaces
|
{[...favoriteWorkspaces, ...nonFavoriteWorkspaces]
|
||||||
.filter((ws) => ws.orgId === currentOrg?.id)
|
.filter((ws) => ws.orgId === currentOrg?.id)
|
||||||
.map(({ id, name }) => (
|
.map(({ id, name }) => (
|
||||||
<SelectItem
|
<div
|
||||||
key={`ws-layout-list-${id}`}
|
|
||||||
value={id}
|
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
currentWorkspace?.id === id && "bg-mineshaft-600",
|
"mb-1 grid grid-cols-7 rounded-md hover:bg-mineshaft-500",
|
||||||
"truncate"
|
id === currentWorkspace?.id && "bg-mineshaft-500"
|
||||||
)}
|
)}
|
||||||
|
key={id}
|
||||||
>
|
>
|
||||||
{name}
|
<div className="col-span-6">
|
||||||
</SelectItem>
|
<SelectItem
|
||||||
|
key={`ws-layout-list-${id}`}
|
||||||
|
value={id}
|
||||||
|
className="transition-none data-[highlighted]:bg-mineshaft-500"
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</SelectItem>
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 flex items-center">
|
||||||
|
{projectFavorites?.includes(id) ? (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faSolidStar}
|
||||||
|
className="text-sm text-mineshaft-300 hover:text-mineshaft-400"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
removeProjectFromFavorites(id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faStar}
|
||||||
|
className="text-sm text-mineshaft-400 hover:text-mineshaft-300"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
addProjectToFavorites(id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<hr className="mt-1 mb-1 h-px border-0 bg-gray-700" />
|
<hr className="mt-1 mb-1 h-px border-0 bg-gray-700" />
|
||||||
|
|||||||
@@ -619,7 +619,7 @@ const OrganizationPage = withPermission(
|
|||||||
{isFavorite ? (
|
{isFavorite ? (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faSolidStar}
|
icon={faSolidStar}
|
||||||
className="text-sm text-mineshaft-300"
|
className="text-sm text-mineshaft-300 hover:text-mineshaft-400"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
removeProjectFromFavorites(workspace.id);
|
removeProjectFromFavorites(workspace.id);
|
||||||
@@ -628,7 +628,7 @@ const OrganizationPage = withPermission(
|
|||||||
) : (
|
) : (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faStar}
|
icon={faStar}
|
||||||
className="text-sm text-mineshaft-400"
|
className="text-sm text-mineshaft-400 hover:text-mineshaft-300"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
addProjectToFavorites(workspace.id);
|
addProjectToFavorites(workspace.id);
|
||||||
@@ -651,6 +651,49 @@ const OrganizationPage = withPermission(
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const renderProjectListItem = (workspace: Workspace, isFavorite: boolean, index: number) => (
|
||||||
|
// eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
router.push(`/project/${workspace.id}/secrets/overview`);
|
||||||
|
localStorage.setItem("projectData.id", workspace.id);
|
||||||
|
}}
|
||||||
|
key={workspace.id}
|
||||||
|
className={`min-w-72 group grid h-14 cursor-pointer grid-cols-6 border-t border-l border-r border-mineshaft-600 bg-mineshaft-800 px-6 hover:bg-mineshaft-700 ${
|
||||||
|
index === 0 && "rounded-t-md"
|
||||||
|
} ${index === filteredWorkspaces.length - 1 && "rounded-b-md border-b"}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center sm:col-span-3 lg:col-span-4">
|
||||||
|
<FontAwesomeIcon icon={faFileShield} className="text-sm text-primary/70" />
|
||||||
|
<div className="ml-5 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">
|
||||||
|
<div className="text-center text-sm text-mineshaft-300">
|
||||||
|
{workspace.environments?.length || 0} environments
|
||||||
|
</div>
|
||||||
|
{isFavorite ? (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faSolidStar}
|
||||||
|
className="ml-6 text-sm text-mineshaft-300 hover:text-mineshaft-400"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
removeProjectFromFavorites(workspace.id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faStar}
|
||||||
|
className="ml-6 text-sm text-mineshaft-400 hover:text-mineshaft-300"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
addProjectToFavorites(workspace.id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
const projectsGridView = (
|
const projectsGridView = (
|
||||||
<>
|
<>
|
||||||
{favoriteWorkspaces.length > 0 && (
|
{favoriteWorkspaces.length > 0 && (
|
||||||
@@ -701,29 +744,13 @@ const OrganizationPage = withPermission(
|
|||||||
<Skeleton className="w-full bg-mineshaft-600" />
|
<Skeleton className="w-full bg-mineshaft-600" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{filteredWorkspaces.map((workspace, ind) => (
|
{[...favoriteWorkspaces, ...nonFavoriteWorkspaces].map((workspace, ind) =>
|
||||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events
|
renderProjectListItem(
|
||||||
<div
|
workspace,
|
||||||
onClick={() => {
|
favoriteWorkspaces.some((w) => w.id === workspace.id),
|
||||||
router.push(`/project/${workspace.id}/secrets/overview`);
|
ind
|
||||||
localStorage.setItem("projectData.id", workspace.id);
|
)
|
||||||
}}
|
)}
|
||||||
key={workspace.id}
|
|
||||||
className={`min-w-72 group grid h-14 cursor-pointer grid-cols-6 border-t border-l border-r border-mineshaft-600 bg-mineshaft-800 px-6 hover:bg-mineshaft-700 ${
|
|
||||||
ind === 0 && "rounded-t-md"
|
|
||||||
} ${ind === filteredWorkspaces.length - 1 && "rounded-b-md border-b"}`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center sm:col-span-3 lg:col-span-4">
|
|
||||||
<FontAwesomeIcon icon={faFileShield} className="text-sm text-primary/70" />
|
|
||||||
<div className="ml-5 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">
|
|
||||||
<div className="text-center text-sm text-mineshaft-300">
|
|
||||||
{workspace.environments?.length || 0} environments
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user