mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 18:27:36 +00:00
Merge pull request #1548 from rhythmbhiwani/overview-page-enchanced
Overview Page Bug Fixes and Enhancement
This commit is contained in:
@@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { subject } from "@casl/ability";
|
import { subject } from "@casl/ability";
|
||||||
import { faCheckCircle } from "@fortawesome/free-regular-svg-icons";
|
import { faCheckCircle, faCircle } from "@fortawesome/free-regular-svg-icons";
|
||||||
import {
|
import {
|
||||||
faAngleDown,
|
faAngleDown,
|
||||||
faArrowDown,
|
faArrowDown,
|
||||||
@@ -18,7 +18,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
|
|
||||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
import NavHeader from "@app/components/navigation/NavHeader";
|
import NavHeader from "@app/components/navigation/NavHeader";
|
||||||
import { PermissionDeniedBanner, ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -108,7 +108,11 @@ export const SecretOverviewPage = () => {
|
|||||||
}, [isWorkspaceLoading, workspaceId, router.isReady]);
|
}, [isWorkspaceLoading, workspaceId, router.isReady]);
|
||||||
|
|
||||||
const userAvailableEnvs = currentWorkspace?.environments || [];
|
const userAvailableEnvs = currentWorkspace?.environments || [];
|
||||||
const [visibleEnvs, setVisisbleEnvs] = useState(userAvailableEnvs);
|
const [visibleEnvs, setVisibleEnvs] = useState(userAvailableEnvs);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setVisibleEnvs(userAvailableEnvs);
|
||||||
|
}, [userAvailableEnvs]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: secrets,
|
data: secrets,
|
||||||
@@ -208,9 +212,9 @@ export const SecretOverviewPage = () => {
|
|||||||
|
|
||||||
const handleEnvSelect = (envId: string) => {
|
const handleEnvSelect = (envId: string) => {
|
||||||
if (visibleEnvs.map((env) => env.id).includes(envId)) {
|
if (visibleEnvs.map((env) => env.id).includes(envId)) {
|
||||||
setVisisbleEnvs(visibleEnvs.filter((env) => env.id !== envId));
|
setVisibleEnvs(visibleEnvs.filter((env) => env.id !== envId));
|
||||||
} else {
|
} else {
|
||||||
setVisisbleEnvs(visibleEnvs.concat(userAvailableEnvs.filter((env) => env.id === envId)));
|
setVisibleEnvs(visibleEnvs.concat(userAvailableEnvs.filter((env) => env.id === envId)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -390,41 +394,44 @@ export const SecretOverviewPage = () => {
|
|||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<FolderBreadCrumbs secretPath={secretPath} onResetSearch={handleResetSearch} />
|
<FolderBreadCrumbs secretPath={secretPath} onResetSearch={handleResetSearch} />
|
||||||
<div className="flex flex-row items-center justify-center space-x-2">
|
<div className="flex flex-row items-center justify-center space-x-2">
|
||||||
<DropdownMenu>
|
{userAvailableEnvs.length > 0 && (
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenu>
|
||||||
<IconButton
|
<DropdownMenuTrigger asChild>
|
||||||
ariaLabel="Environments"
|
<IconButton
|
||||||
variant="plain"
|
ariaLabel="Environments"
|
||||||
size="sm"
|
variant="plain"
|
||||||
className="mr-2 flex w-11 items-center justify-center overflow-hidden border border-mineshaft-600 bg-mineshaft-800 p-0 hover:border-primary/60 hover:bg-primary/10"
|
size="sm"
|
||||||
>
|
className="flex h-10 w-11 items-center justify-center overflow-hidden border border-mineshaft-600 bg-mineshaft-800 p-0 hover:border-primary/60 hover:bg-primary/10"
|
||||||
<Tooltip content="Choose visible environments" className="mb-2">
|
>
|
||||||
<FontAwesomeIcon icon={faList} />
|
<Tooltip content="Choose visible environments" className="mb-2">
|
||||||
</Tooltip>
|
<FontAwesomeIcon icon={faList} />
|
||||||
</IconButton>
|
</Tooltip>
|
||||||
</DropdownMenuTrigger>
|
</IconButton>
|
||||||
<DropdownMenuContent align="end">
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuLabel>Choose visible environments</DropdownMenuLabel>
|
<DropdownMenuContent align="end">
|
||||||
{userAvailableEnvs.map((avaiableEnv) => {
|
<DropdownMenuLabel>Choose visible environments</DropdownMenuLabel>
|
||||||
const { id: envId, name } = avaiableEnv;
|
{userAvailableEnvs.map((availableEnv) => {
|
||||||
|
const { id: envId, name } = availableEnv;
|
||||||
|
|
||||||
const isEnvSelected = visibleEnvs.map((env) => env.id).includes(envId);
|
const isEnvSelected = visibleEnvs.map((env) => env.id).includes(envId);
|
||||||
return (
|
return (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => handleEnvSelect(envId)}
|
onClick={() => handleEnvSelect(envId)}
|
||||||
key={envId}
|
key={envId}
|
||||||
icon={
|
icon={
|
||||||
isEnvSelected && (
|
isEnvSelected ? (
|
||||||
<FontAwesomeIcon className="text-primary" icon={faCheckCircle} />
|
<FontAwesomeIcon className="text-primary" icon={faCheckCircle} />
|
||||||
)
|
) : (
|
||||||
}
|
<FontAwesomeIcon className="text-mineshaft-400" icon={faCircle} />
|
||||||
iconPos="left"
|
)
|
||||||
>
|
}
|
||||||
<div className="flex items-center">{name}</div>
|
iconPos="left"
|
||||||
</DropdownMenuItem>
|
>
|
||||||
);
|
<div className="flex items-center">{name}</div>
|
||||||
})}
|
</DropdownMenuItem>
|
||||||
{/* <DropdownMenuItem className="px-1.5" asChild>
|
);
|
||||||
|
})}
|
||||||
|
{/* <DropdownMenuItem className="px-1.5" asChild>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
@@ -436,8 +443,9 @@ export const SecretOverviewPage = () => {
|
|||||||
Create an environment
|
Create an environment
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuItem> */}
|
</DropdownMenuItem> */}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
)}
|
||||||
<div className="w-80">
|
<div className="w-80">
|
||||||
<Input
|
<Input
|
||||||
className="h-[2.3rem] bg-mineshaft-800 placeholder-mineshaft-50 duration-200 focus:bg-mineshaft-700/80"
|
className="h-[2.3rem] bg-mineshaft-800 placeholder-mineshaft-50 duration-200 focus:bg-mineshaft-700/80"
|
||||||
@@ -447,62 +455,64 @@ export const SecretOverviewPage = () => {
|
|||||||
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
|
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
{userAvailableEnvs.length > 0 && (
|
||||||
<ProjectPermissionCan
|
<div>
|
||||||
I={ProjectPermissionActions.Create}
|
<ProjectPermissionCan
|
||||||
a={subject(ProjectPermissionSub.Secrets, { secretPath })}
|
I={ProjectPermissionActions.Create}
|
||||||
>
|
a={subject(ProjectPermissionSub.Secrets, { secretPath })}
|
||||||
{(isAllowed) => (
|
>
|
||||||
<Button
|
{(isAllowed) => (
|
||||||
variant="outline_bg"
|
<Button
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
variant="outline_bg"
|
||||||
onClick={() => handlePopUpOpen("addSecretsInAllEnvs")}
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
className="h-10 rounded-r-none"
|
onClick={() => handlePopUpOpen("addSecretsInAllEnvs")}
|
||||||
isDisabled={!isAllowed}
|
className="h-10 rounded-r-none"
|
||||||
>
|
isDisabled={!isAllowed}
|
||||||
Add Secret
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
<DropdownMenu
|
|
||||||
open={popUp.misc.isOpen}
|
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("misc", isOpen)}
|
|
||||||
>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<IconButton
|
|
||||||
ariaLabel="add-folder-or-import"
|
|
||||||
variant="outline_bg"
|
|
||||||
className="rounded-l-none bg-mineshaft-600 p-3"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faAngleDown} />
|
|
||||||
</IconButton>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end">
|
|
||||||
<div className="flex flex-col space-y-1 p-1.5">
|
|
||||||
<ProjectPermissionCan
|
|
||||||
I={ProjectPermissionActions.Create}
|
|
||||||
a={subject(ProjectPermissionSub.Secrets, { secretPath })}
|
|
||||||
>
|
>
|
||||||
{(isAllowed) => (
|
Add Secret
|
||||||
<Button
|
</Button>
|
||||||
leftIcon={<FontAwesomeIcon icon={faFolderPlus} />}
|
)}
|
||||||
onClick={() => {
|
</ProjectPermissionCan>
|
||||||
handlePopUpOpen("addFolder");
|
<DropdownMenu
|
||||||
handlePopUpClose("misc");
|
open={popUp.misc.isOpen}
|
||||||
}}
|
onOpenChange={(isOpen) => handlePopUpToggle("misc", isOpen)}
|
||||||
isDisabled={!isAllowed}
|
>
|
||||||
variant="outline_bg"
|
<DropdownMenuTrigger asChild>
|
||||||
className="h-10"
|
<IconButton
|
||||||
isFullWidth
|
ariaLabel="add-folder-or-import"
|
||||||
>
|
variant="outline_bg"
|
||||||
Add Folder
|
className="rounded-l-none bg-mineshaft-600 p-3"
|
||||||
</Button>
|
>
|
||||||
)}
|
<FontAwesomeIcon icon={faAngleDown} />
|
||||||
</ProjectPermissionCan>
|
</IconButton>
|
||||||
</div>
|
</DropdownMenuTrigger>
|
||||||
</DropdownMenuContent>
|
<DropdownMenuContent align="end">
|
||||||
</DropdownMenu>
|
<div className="flex flex-col space-y-1 p-1.5">
|
||||||
</div>
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Create}
|
||||||
|
a={subject(ProjectPermissionSub.Secrets, { secretPath })}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Button
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faFolderPlus} />}
|
||||||
|
onClick={() => {
|
||||||
|
handlePopUpOpen("addFolder");
|
||||||
|
handlePopUpClose("misc");
|
||||||
|
}}
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
variant="outline_bg"
|
||||||
|
className="h-10"
|
||||||
|
isFullWidth
|
||||||
|
>
|
||||||
|
Add Folder
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -565,14 +575,25 @@ export const SecretOverviewPage = () => {
|
|||||||
className="bg-mineshaft-700"
|
className="bg-mineshaft-700"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isTableEmpty && !isTableLoading && (
|
{userAvailableEnvs.length > 0 && visibleEnvs.length === 0 && (
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td colSpan={visibleEnvs.length + 1}>
|
<Td colSpan={visibleEnvs.length + 1}>
|
||||||
<EmptyState title="Let's add some secrets" icon={faFolderBlank} iconSize="3x">
|
<EmptyState title="You have no visible environments" iconSize="3x" />
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
)}
|
||||||
|
{userAvailableEnvs.length === 0 && (
|
||||||
|
<Tr>
|
||||||
|
<Td colSpan={visibleEnvs.length + 1}>
|
||||||
|
<EmptyState
|
||||||
|
title="You have no environments, start by adding some"
|
||||||
|
iconSize="3x"
|
||||||
|
>
|
||||||
<Link
|
<Link
|
||||||
href={{
|
href={{
|
||||||
pathname: "/project/[id]/secrets/[env]",
|
pathname: "/project/[id]/settings",
|
||||||
query: { id: workspaceId, env: visibleEnvs?.[0]?.slug }
|
query: { id: workspaceId },
|
||||||
|
hash: "environments"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
@@ -581,13 +602,38 @@ export const SecretOverviewPage = () => {
|
|||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
size="md"
|
size="md"
|
||||||
>
|
>
|
||||||
Go to {visibleEnvs?.[0]?.name}
|
Add environments
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</EmptyState>
|
</EmptyState>
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
)}
|
)}
|
||||||
|
{isTableEmpty && !isTableLoading && visibleEnvs.length > 0 && (
|
||||||
|
<Tr>
|
||||||
|
<Td colSpan={visibleEnvs.length + 1}>
|
||||||
|
<EmptyState
|
||||||
|
title={
|
||||||
|
searchFilter
|
||||||
|
? "No secret found for your search, add one now"
|
||||||
|
: "Let's add some secrets"
|
||||||
|
}
|
||||||
|
icon={faFolderBlank}
|
||||||
|
iconSize="3x"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className="mt-4"
|
||||||
|
variant="outline_bg"
|
||||||
|
colorSchema="primary"
|
||||||
|
size="md"
|
||||||
|
onClick={() => handlePopUpOpen("addSecretsInAllEnvs")}
|
||||||
|
>
|
||||||
|
Add Secrets
|
||||||
|
</Button>
|
||||||
|
</EmptyState>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
)}
|
||||||
{!isTableLoading &&
|
{!isTableLoading &&
|
||||||
filteredFolderNames.map((folderName, index) => (
|
filteredFolderNames.map((folderName, index) => (
|
||||||
<SecretOverviewFolderRow
|
<SecretOverviewFolderRow
|
||||||
@@ -599,22 +645,19 @@ export const SecretOverviewPage = () => {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{!isTableLoading &&
|
{!isTableLoading &&
|
||||||
(visibleEnvs?.length > 0 ? (
|
visibleEnvs?.length > 0 &&
|
||||||
filteredSecretNames.map((key, index) => (
|
filteredSecretNames.map((key, index) => (
|
||||||
<SecretOverviewTableRow
|
<SecretOverviewTableRow
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
onSecretCreate={handleSecretCreate}
|
onSecretCreate={handleSecretCreate}
|
||||||
onSecretDelete={handleSecretDelete}
|
onSecretDelete={handleSecretDelete}
|
||||||
onSecretUpdate={handleSecretUpdate}
|
onSecretUpdate={handleSecretUpdate}
|
||||||
key={`overview-${key}-${index + 1}`}
|
key={`overview-${key}-${index + 1}`}
|
||||||
environments={visibleEnvs}
|
environments={visibleEnvs}
|
||||||
secretKey={key}
|
secretKey={key}
|
||||||
getSecretByKey={getSecretByKey}
|
getSecretByKey={getSecretByKey}
|
||||||
expandableColWidth={expandableTableWidth}
|
expandableColWidth={expandableTableWidth}
|
||||||
/>
|
/>
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<PermissionDeniedBanner />
|
|
||||||
))}
|
))}
|
||||||
</TBody>
|
</TBody>
|
||||||
<TFoot>
|
<TFoot>
|
||||||
|
|||||||
+17
-7
@@ -171,7 +171,7 @@ export const CreateSecretForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormLabel label="Environments" className="mb-2" />
|
<FormLabel label="Environments" className="mb-2" />
|
||||||
<div className="thin-scrollbar grid max-h-64 grid-cols-3 gap-4 overflow-auto ">
|
<div className="thin-scrollbar grid max-h-64 grid-cols-3 gap-4 overflow-auto py-2">
|
||||||
{environments.map((env) => {
|
{environments.map((env) => {
|
||||||
return (
|
return (
|
||||||
<Controller
|
<Controller
|
||||||
@@ -183,13 +183,23 @@ export const CreateSecretForm = ({
|
|||||||
isChecked={field.value}
|
isChecked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
id={`secret-input-${env.slug}`}
|
id={`secret-input-${env.slug}`}
|
||||||
|
className="!justify-start"
|
||||||
>
|
>
|
||||||
{env.name}
|
<span className="flex w-full flex-row items-center justify-start whitespace-pre-wrap">
|
||||||
{getSecretByKey(env.slug, newSecretKey) && (
|
<span title={env.name} className="truncate">
|
||||||
<Tooltip content="Secret exists. Will be overwritten">
|
{env.name}
|
||||||
<FontAwesomeIcon icon={faWarning} className="ml-1 text-yellow-400" />
|
</span>
|
||||||
</Tooltip>
|
<span>
|
||||||
)}
|
{getSecretByKey(env.slug, newSecretKey) && (
|
||||||
|
<Tooltip
|
||||||
|
className="max-w-[150px]"
|
||||||
|
content="Secret already exists, and it will be overwritten"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faWarning} className="ml-1 text-yellow-400" />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+7
-2
@@ -148,8 +148,13 @@ export const SecretOverviewTableRow = ({
|
|||||||
key={`secret-expanded-${slug}-${secretKey}`}
|
key={`secret-expanded-${slug}-${secretKey}`}
|
||||||
className="hover:bg-mineshaft-700"
|
className="hover:bg-mineshaft-700"
|
||||||
>
|
>
|
||||||
<td className="flex" style={{ padding: "0.25rem 1rem" }}>
|
<td
|
||||||
<div className="flex h-8 items-center">{name}</div>
|
className="flex h-full items-center"
|
||||||
|
style={{ padding: "0.25rem 1rem" }}
|
||||||
|
>
|
||||||
|
<div title={name} className="flex h-8 w-[8rem] items-center ">
|
||||||
|
<span className="truncate">{name}</span>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="col-span-2 h-8 w-full">
|
<td className="col-span-2 h-8 w-full">
|
||||||
<SecretEditRow
|
<SecretEditRow
|
||||||
|
|||||||
+4
-1
@@ -63,7 +63,10 @@ export const EnvironmentSection = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div
|
||||||
|
id="environments"
|
||||||
|
className="mb-6 scroll-m-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
|
||||||
|
>
|
||||||
<div className="mb-8 flex justify-between">
|
<div className="mb-8 flex justify-between">
|
||||||
<p className="text-xl font-semibold text-mineshaft-100">Environments</p>
|
<p className="text-xl font-semibold text-mineshaft-100">Environments</p>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user