mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 23:26:42 +00:00
feat: added search to project nav
This commit is contained in:
@@ -305,7 +305,6 @@ export const projectServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO(pta: check this again)
|
|
||||||
await bootstrapSshProject({
|
await bootstrapSshProject({
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
sshCertificateAuthorityDAL,
|
sshCertificateAuthorityDAL,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
faCubes,
|
faCubes,
|
||||||
faEnvelope,
|
faEnvelope,
|
||||||
faInfo,
|
faInfo,
|
||||||
|
faInfoCircle,
|
||||||
faSignOut,
|
faSignOut,
|
||||||
faSlash,
|
faSlash,
|
||||||
faSort,
|
faSort,
|
||||||
@@ -19,6 +20,7 @@ import { useQueryClient } from "@tanstack/react-query";
|
|||||||
import { Link, useNavigate, useRouter, useRouterState } from "@tanstack/react-router";
|
import { Link, useNavigate, useRouter, useRouterState } from "@tanstack/react-router";
|
||||||
|
|
||||||
import { Mfa } from "@app/components/auth/Mfa";
|
import { Mfa } from "@app/components/auth/Mfa";
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
import SecurityClient from "@app/components/utilities/SecurityClient";
|
||||||
import {
|
import {
|
||||||
BreadcrumbContainer,
|
BreadcrumbContainer,
|
||||||
@@ -30,7 +32,8 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
Modal,
|
Modal,
|
||||||
ModalContent,
|
ModalContent,
|
||||||
TBreadcrumbFormat
|
TBreadcrumbFormat,
|
||||||
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { envConfig } from "@app/config/env";
|
import { envConfig } from "@app/config/env";
|
||||||
import { useOrganization, useSubscription, useUser } from "@app/context";
|
import { useOrganization, useSubscription, useUser } from "@app/context";
|
||||||
@@ -39,6 +42,7 @@ import { useToggle } from "@app/hooks";
|
|||||||
import { useGetOrganizations, useLogoutUser, workspaceKeys } from "@app/hooks/api";
|
import { useGetOrganizations, useLogoutUser, workspaceKeys } from "@app/hooks/api";
|
||||||
import { authKeys, selectOrganization } from "@app/hooks/api/auth/queries";
|
import { authKeys, selectOrganization } from "@app/hooks/api/auth/queries";
|
||||||
import { MfaMethod } from "@app/hooks/api/auth/types";
|
import { MfaMethod } from "@app/hooks/api/auth/types";
|
||||||
|
import { getAuthToken } from "@app/hooks/api/reactQuery";
|
||||||
import { SubscriptionPlan } from "@app/hooks/api/types";
|
import { SubscriptionPlan } from "@app/hooks/api/types";
|
||||||
import { AuthMethod } from "@app/hooks/api/users/types";
|
import { AuthMethod } from "@app/hooks/api/users/types";
|
||||||
import { navigateUserToOrg } from "@app/pages/auth/LoginPage/Login.utils";
|
import { navigateUserToOrg } from "@app/pages/auth/LoginPage/Login.utils";
|
||||||
@@ -130,6 +134,19 @@ export const Navbar = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCopyToken = async () => {
|
||||||
|
try {
|
||||||
|
await window.navigator.clipboard.writeText(getAuthToken());
|
||||||
|
createNotification({
|
||||||
|
type: "success",
|
||||||
|
text: "Copied current login session token to clipboard"
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
createNotification({ type: "error", text: "Failed to copy user token to clipboard" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (shouldShowMfa) {
|
if (shouldShowMfa) {
|
||||||
return (
|
return (
|
||||||
<div className="flex max-h-screen min-h-screen flex-col items-center justify-center gap-2 overflow-y-auto bg-gradient-to-tr from-mineshaft-600 via-mineshaft-800 to-bunker-700">
|
<div className="flex max-h-screen min-h-screen flex-col items-center justify-center gap-2 overflow-y-auto bg-gradient-to-tr from-mineshaft-600 via-mineshaft-800 to-bunker-700">
|
||||||
@@ -365,6 +382,16 @@ export const Navbar = () => {
|
|||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</a>
|
</a>
|
||||||
<div className="mt-1 h-1 border-t border-mineshaft-600" />
|
<div className="mt-1 h-1 border-t border-mineshaft-600" />
|
||||||
|
<DropdownMenuItem onClick={handleCopyToken}>
|
||||||
|
Copy Token
|
||||||
|
<Tooltip
|
||||||
|
content="This token is linked to your current login session and can only access resources within the organization you're currently logged into."
|
||||||
|
className="max-w-3xl"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faInfoCircle} className="mb-[0.06rem] pl-1.5 text-xs" />
|
||||||
|
</Tooltip>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<div className="mt-1 h-1 border-t border-mineshaft-600" />
|
||||||
<DropdownMenuItem onClick={logOutUser} icon={<FontAwesomeIcon icon={faSignOut} />}>
|
<DropdownMenuItem onClick={logOutUser} icon={<FontAwesomeIcon icon={faSignOut} />}>
|
||||||
Log Out
|
Log Out
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { faStar } from "@fortawesome/free-regular-svg-icons";
|
import { faStar } from "@fortawesome/free-regular-svg-icons";
|
||||||
import {
|
import {
|
||||||
faCheck,
|
faCheck,
|
||||||
faCube,
|
faCube,
|
||||||
|
faMagnifyingGlass,
|
||||||
faPlus,
|
faPlus,
|
||||||
faSort,
|
faSort,
|
||||||
faStar as faSolidStar
|
faStar as faSolidStar
|
||||||
@@ -19,7 +20,8 @@ import {
|
|||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
IconButton
|
IconButton,
|
||||||
|
Input
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
OrgPermissionActions,
|
OrgPermissionActions,
|
||||||
@@ -37,6 +39,7 @@ import { ProjectType, Workspace } from "@app/hooks/api/workspace/types";
|
|||||||
|
|
||||||
// TODO(pta): add search to project select
|
// TODO(pta): add search to project select
|
||||||
export const ProjectSelect = () => {
|
export const ProjectSelect = () => {
|
||||||
|
const [searchProject, setSearchProject] = useState("");
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const { data: workspaces = [] } = useGetUserWorkspaces();
|
const { data: workspaces = [] } = useGetUserWorkspaces();
|
||||||
@@ -131,46 +134,62 @@ export const ProjectSelect = () => {
|
|||||||
style={{ minWidth: "220px" }}
|
style={{ minWidth: "220px" }}
|
||||||
>
|
>
|
||||||
<div className="px-2 py-1 text-xs capitalize text-mineshaft-400">Projects</div>
|
<div className="px-2 py-1 text-xs capitalize text-mineshaft-400">Projects</div>
|
||||||
{projects?.map((workspace) => {
|
<div className="mb-1 border-b border-b-mineshaft-600 py-1 pb-1">
|
||||||
return (
|
<Input
|
||||||
<DropdownMenuItem
|
value={searchProject}
|
||||||
key={workspace.id}
|
onChange={(evt) => setSearchProject(evt.target.value || "")}
|
||||||
onClick={async () => {
|
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
|
||||||
// todo(akhi): this is not using react query because react query in overview is throwing error when envs are not exact same count
|
size="xs"
|
||||||
// to reproduce change this back to router.push and switch between two projects with different env count
|
variant="plain"
|
||||||
// look into this on dashboard revamp
|
placeholder="Search projects"
|
||||||
const url = linkOptions({
|
/>
|
||||||
to: getProjectHomePage(workspace.defaultType),
|
</div>
|
||||||
params: {
|
<div className="thin-scrollbar max-h-80 overflow-auto">
|
||||||
projectId: workspace.id
|
{projects
|
||||||
}
|
?.filter((el) => el.name?.toLowerCase().includes(searchProject.toLowerCase()))
|
||||||
});
|
?.map((workspace) => {
|
||||||
window.location.assign(url.to.replaceAll("$projectId", workspace.id));
|
return (
|
||||||
}}
|
<DropdownMenuItem
|
||||||
icon={
|
key={workspace.id}
|
||||||
currentWorkspace?.id === workspace.id && (
|
onClick={async () => {
|
||||||
<FontAwesomeIcon icon={faCheck} className="mr-3 text-primary" />
|
// todo(akhi): this is not using react query because react query in overview is throwing error when envs are not exact same count
|
||||||
)
|
// to reproduce change this back to router.push and switch between two projects with different env count
|
||||||
}
|
// look into this on dashboard revamp
|
||||||
>
|
const url = linkOptions({
|
||||||
<div className="flex items-center">
|
to: getProjectHomePage(workspace.defaultType),
|
||||||
<div className="flex max-w-[150px] flex-grow items-center justify-between truncate">
|
params: {
|
||||||
{workspace.name}
|
projectId: workspace.id
|
||||||
</div>
|
}
|
||||||
<FontAwesomeIcon
|
});
|
||||||
icon={workspace.isFavorite ? faSolidStar : faStar}
|
window.location.assign(url.to.replaceAll("$projectId", workspace.id));
|
||||||
className="text-sm text-yellow-600 hover:text-mineshaft-400"
|
|
||||||
onClick={async (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
await (
|
|
||||||
workspace.isFavorite ? removeProjectFromFavorites : addProjectToFavorites
|
|
||||||
)(workspace.id);
|
|
||||||
}}
|
}}
|
||||||
/>
|
icon={
|
||||||
</div>
|
currentWorkspace?.id === workspace.id && (
|
||||||
</DropdownMenuItem>
|
<FontAwesomeIcon icon={faCheck} className="mr-3 text-primary" />
|
||||||
);
|
)
|
||||||
})}
|
}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="flex max-w-[150px] flex-grow items-center justify-between truncate">
|
||||||
|
{workspace.name}
|
||||||
|
</div>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={workspace.isFavorite ? faSolidStar : faStar}
|
||||||
|
className="text-sm text-yellow-600 hover:text-mineshaft-400"
|
||||||
|
onClick={async (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
await (
|
||||||
|
workspace.isFavorite
|
||||||
|
? removeProjectFromFavorites
|
||||||
|
: addProjectToFavorites
|
||||||
|
)(workspace.id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
<div className="mt-1 h-1 border-t border-mineshaft-600" />
|
<div className="mt-1 h-1 border-t border-mineshaft-600" />
|
||||||
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Workspace}>
|
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Workspace}>
|
||||||
{(isAllowed) => (
|
{(isAllowed) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user