feat: added search to project nav

This commit is contained in:
=
2025-07-02 01:16:50 +05:30
parent 01c5f516f8
commit 9f3236b47d
3 changed files with 88 additions and 43 deletions
@@ -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) => (