mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #3860 from Infisical/secret-sync-selection-improvements
improvement(secret-sync/app-connection): Add search/pagination to secret sync and app connection selection modals
This commit is contained in:
@@ -58,6 +58,7 @@ export const CreateSecretSyncModal = ({ onOpenChange, selectSync = null, ...prop
|
|||||||
}
|
}
|
||||||
onPointerDownOutside={(e) => e.preventDefault()}
|
onPointerDownOutside={(e) => e.preventDefault()}
|
||||||
className="max-w-2xl"
|
className="max-w-2xl"
|
||||||
|
bodyClassName="overflow-visible"
|
||||||
subTitle={selectedSync ? undefined : "Select a third-party service to sync secrets to."}
|
subTitle={selectedSync ? undefined : "Select a third-party service to sync secrets to."}
|
||||||
>
|
>
|
||||||
<Content
|
<Content
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { faWrench } from "@fortawesome/free-solid-svg-icons";
|
import { useMemo } from "react";
|
||||||
|
import { faInfoCircle, faMagnifyingGlass, faSearch } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { Spinner, Tooltip } from "@app/components/v2";
|
import { EmptyState, Input, Pagination, Spinner, Tooltip } from "@app/components/v2";
|
||||||
import { useSubscription } from "@app/context";
|
import { useSubscription } from "@app/context";
|
||||||
import { SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
|
import { SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
||||||
import { SecretSync, useSecretSyncOptions } from "@app/hooks/api/secretSyncs";
|
import { SecretSync, useSecretSyncOptions } from "@app/hooks/api/secretSyncs";
|
||||||
|
|
||||||
import { UpgradePlanModal } from "../license/UpgradePlanModal";
|
import { UpgradePlanModal } from "../license/UpgradePlanModal";
|
||||||
@@ -19,6 +20,26 @@ export const SecretSyncSelect = ({ onSelect }: Props) => {
|
|||||||
|
|
||||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
||||||
|
|
||||||
|
const { search, setSearch, setPage, page, perPage, setPerPage, offset } = usePagination("", {
|
||||||
|
initPerPage: 16
|
||||||
|
});
|
||||||
|
|
||||||
|
const filteredOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
secretSyncOptions?.filter(
|
||||||
|
({ name, destination }) =>
|
||||||
|
name?.toLowerCase().includes(search.trim().toLowerCase()) ||
|
||||||
|
destination.toLowerCase().includes(search.toLowerCase())
|
||||||
|
) ?? [],
|
||||||
|
[secretSyncOptions, search]
|
||||||
|
);
|
||||||
|
|
||||||
|
useResetPageHelper({
|
||||||
|
totalCount: filteredOptions.length,
|
||||||
|
offset,
|
||||||
|
setPage
|
||||||
|
});
|
||||||
|
|
||||||
if (isPending) {
|
if (isPending) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col items-center justify-center py-2.5">
|
<div className="flex h-full flex-col items-center justify-center py-2.5">
|
||||||
@@ -29,75 +50,103 @@ export const SecretSyncSelect = ({ onSelect }: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-4 gap-2">
|
<div className="flex flex-col gap-4">
|
||||||
{secretSyncOptions?.map(({ destination, enterprise }) => {
|
<Input
|
||||||
const { image, name } = SECRET_SYNC_MAP[destination];
|
value={search}
|
||||||
return (
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
<button
|
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
|
||||||
type="button"
|
placeholder="Search options..."
|
||||||
onClick={() =>
|
className="bg-mineshaft-800 placeholder:text-mineshaft-400"
|
||||||
enterprise && !subscription.enterpriseSecretSyncs
|
/>
|
||||||
? handlePopUpOpen("upgradePlan")
|
<div className="grid h-[29.5rem] grid-cols-4 content-start gap-2">
|
||||||
: onSelect(destination)
|
{filteredOptions.slice(offset, perPage * page)?.map(({ destination, enterprise }) => {
|
||||||
}
|
const { image, name } = SECRET_SYNC_MAP[destination];
|
||||||
className="group relative flex h-28 cursor-pointer flex-col items-center justify-center overflow-hidden rounded-md border border-mineshaft-600 bg-mineshaft-700 p-4 duration-200 hover:bg-mineshaft-600"
|
return (
|
||||||
>
|
<button
|
||||||
<img
|
type="button"
|
||||||
src={`/images/integrations/${image}`}
|
onClick={() =>
|
||||||
height={40}
|
enterprise && !subscription.enterpriseSecretSyncs
|
||||||
width={40}
|
? handlePopUpOpen("upgradePlan")
|
||||||
className="mt-auto"
|
: onSelect(destination)
|
||||||
alt={`${name} logo`}
|
}
|
||||||
/>
|
className="group relative flex h-28 cursor-pointer flex-col items-center justify-center overflow-hidden rounded-md border border-mineshaft-600 bg-mineshaft-700 p-4 duration-200 hover:bg-mineshaft-600"
|
||||||
<div className="mt-auto max-w-xs text-center text-xs font-medium text-gray-300 duration-200 group-hover:text-gray-200">
|
>
|
||||||
{name}
|
<img
|
||||||
</div>
|
src={`/images/integrations/${image}`}
|
||||||
</button>
|
height={40}
|
||||||
);
|
width={40}
|
||||||
})}
|
className="mt-auto"
|
||||||
|
alt={`${name} logo`}
|
||||||
|
/>
|
||||||
|
<div className="mt-auto max-w-xs text-center text-xs font-medium text-gray-300 duration-200 group-hover:text-gray-200">
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{!filteredOptions?.length && (
|
||||||
|
<EmptyState
|
||||||
|
className="col-span-full mt-40"
|
||||||
|
title="No Secret Syncs match search"
|
||||||
|
icon={faSearch}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{Boolean(filteredOptions.length) && (
|
||||||
|
<Pagination
|
||||||
|
startAdornment={
|
||||||
|
<Tooltip
|
||||||
|
side="bottom"
|
||||||
|
className="max-w-sm py-4"
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
<p className="mb-2">Infisical is constantly adding support for more services.</p>
|
||||||
|
<p>
|
||||||
|
{`If you don't see the third-party
|
||||||
|
service you're looking for,`}{" "}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
className="underline hover:text-mineshaft-300"
|
||||||
|
href="https://infisical.com/slack"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
let us know on Slack
|
||||||
|
</a>{" "}
|
||||||
|
or{" "}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
className="underline hover:text-mineshaft-300"
|
||||||
|
href="https://github.com/Infisical/infisical/discussions"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
make a request on GitHub
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="-ml-3 flex items-center gap-1.5 text-mineshaft-400">
|
||||||
|
<span className="text-xs">
|
||||||
|
Don't see the third-party service you're looking for?
|
||||||
|
</span>
|
||||||
|
<FontAwesomeIcon size="xs" icon={faInfoCircle} />
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
count={filteredOptions.length}
|
||||||
|
page={page}
|
||||||
|
perPage={perPage}
|
||||||
|
onChangePage={setPage}
|
||||||
|
onChangePerPage={setPerPage}
|
||||||
|
perPageList={[16]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<UpgradePlanModal
|
<UpgradePlanModal
|
||||||
isOpen={popUp.upgradePlan.isOpen}
|
isOpen={popUp.upgradePlan.isOpen}
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||||
text="You can use every Secret Sync if you switch to Infisical's Enterprise plan."
|
text="You can use every Secret Sync if you switch to Infisical's Enterprise plan."
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
|
||||||
side="bottom"
|
|
||||||
className="max-w-sm py-4"
|
|
||||||
content={
|
|
||||||
<>
|
|
||||||
<p className="mb-2">Infisical is constantly adding support for more services.</p>
|
|
||||||
<p>
|
|
||||||
{`If you don't see the third-party
|
|
||||||
service you're looking for,`}{" "}
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
className="underline hover:text-mineshaft-300"
|
|
||||||
href="https://infisical.com/slack"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
let us know on Slack
|
|
||||||
</a>{" "}
|
|
||||||
or{" "}
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
className="underline hover:text-mineshaft-300"
|
|
||||||
href="https://github.com/Infisical/infisical/discussions"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
make a request on GitHub
|
|
||||||
</a>
|
|
||||||
.
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="group relative flex h-28 flex-col items-center justify-center rounded-md border border-dashed border-mineshaft-600 bg-mineshaft-800 p-4 hover:bg-mineshaft-900/50">
|
|
||||||
<FontAwesomeIcon className="mt-auto text-3xl" icon={faWrench} />
|
|
||||||
<div className="mt-auto max-w-xs text-center text-xs font-medium text-gray-300 duration-200 group-hover:text-gray-200">
|
|
||||||
Coming Soon
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { faWrench } from "@fortawesome/free-solid-svg-icons";
|
import { useMemo } from "react";
|
||||||
|
import { faInfoCircle, faMagnifyingGlass, faSearch } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||||
import { Spinner, Tooltip } from "@app/components/v2";
|
import { EmptyState, Input, Pagination, Spinner, Tooltip } from "@app/components/v2";
|
||||||
import { useSubscription } from "@app/context";
|
import { useSubscription } from "@app/context";
|
||||||
import { APP_CONNECTION_MAP } from "@app/helpers/appConnections";
|
import { APP_CONNECTION_MAP } from "@app/helpers/appConnections";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
||||||
import { useAppConnectionOptions } from "@app/hooks/api/appConnections";
|
import { useAppConnectionOptions } from "@app/hooks/api/appConnections";
|
||||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||||
|
|
||||||
@@ -19,6 +20,26 @@ export const AppConnectionsSelect = ({ onSelect }: Props) => {
|
|||||||
|
|
||||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
||||||
|
|
||||||
|
const { search, setSearch, setPage, page, perPage, setPerPage, offset } = usePagination("", {
|
||||||
|
initPerPage: 16
|
||||||
|
});
|
||||||
|
|
||||||
|
const filteredOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
appConnectionOptions?.filter(
|
||||||
|
({ name, app }) =>
|
||||||
|
name?.toLowerCase().includes(search.trim().toLowerCase()) ||
|
||||||
|
app.toLowerCase().includes(search.toLowerCase())
|
||||||
|
) ?? [],
|
||||||
|
[appConnectionOptions, search]
|
||||||
|
);
|
||||||
|
|
||||||
|
useResetPageHelper({
|
||||||
|
totalCount: filteredOptions.length,
|
||||||
|
offset,
|
||||||
|
setPage
|
||||||
|
});
|
||||||
|
|
||||||
if (isPending) {
|
if (isPending) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col items-center justify-center py-2.5">
|
<div className="flex h-full flex-col items-center justify-center py-2.5">
|
||||||
@@ -29,86 +50,120 @@ export const AppConnectionsSelect = ({ onSelect }: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-4 gap-2">
|
<div className="flex flex-col gap-4">
|
||||||
{appConnectionOptions?.map((option) => {
|
<Input
|
||||||
const { image, name, size = 50, enterprise = false, icon } = APP_CONNECTION_MAP[option.app];
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
|
||||||
|
placeholder="Search options..."
|
||||||
|
className="bg-mineshaft-800 placeholder:text-mineshaft-400"
|
||||||
|
/>
|
||||||
|
<div className="grid h-[29.5rem] grid-cols-4 content-start gap-2">
|
||||||
|
{filteredOptions.slice(offset, perPage * page)?.map((option) => {
|
||||||
|
const {
|
||||||
|
image,
|
||||||
|
name,
|
||||||
|
size = 50,
|
||||||
|
enterprise = false,
|
||||||
|
icon
|
||||||
|
} = APP_CONNECTION_MAP[option.app];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
enterprise && !subscription.enterpriseAppConnections
|
enterprise && !subscription.enterpriseAppConnections
|
||||||
? handlePopUpOpen("upgradePlan")
|
? handlePopUpOpen("upgradePlan")
|
||||||
: onSelect(option.app)
|
: onSelect(option.app)
|
||||||
}
|
}
|
||||||
className="group relative flex h-28 cursor-pointer flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-700 p-4 duration-200 hover:bg-mineshaft-600"
|
className="group relative flex h-28 cursor-pointer flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-700 p-4 duration-200 hover:bg-mineshaft-600"
|
||||||
>
|
>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<img
|
<img
|
||||||
src={`/images/integrations/${image}`}
|
src={`/images/integrations/${image}`}
|
||||||
style={{
|
style={{
|
||||||
width: `${size}px`
|
width: `${size}px`
|
||||||
}}
|
}}
|
||||||
className="mt-auto"
|
className="mt-auto"
|
||||||
alt={`${name} logo`}
|
alt={`${name} logo`}
|
||||||
/>
|
|
||||||
{icon && (
|
|
||||||
<FontAwesomeIcon
|
|
||||||
className="absolute -bottom-1.5 -right-1.5 text-primary-700"
|
|
||||||
size="xl"
|
|
||||||
icon={icon}
|
|
||||||
/>
|
/>
|
||||||
)}
|
{icon && (
|
||||||
</div>
|
<FontAwesomeIcon
|
||||||
<div className="mt-auto max-w-xs text-center text-xs font-medium text-gray-300 duration-200 group-hover:text-gray-200">
|
className="absolute -bottom-1.5 -right-1.5 text-primary-700"
|
||||||
{name}
|
size="xl"
|
||||||
</div>
|
icon={icon}
|
||||||
</button>
|
/>
|
||||||
);
|
)}
|
||||||
})}
|
</div>
|
||||||
|
<div className="mt-auto max-w-xs text-center text-xs font-medium text-gray-300 duration-200 group-hover:text-gray-200">
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{!filteredOptions?.length && (
|
||||||
|
<EmptyState
|
||||||
|
className="col-span-full mt-40"
|
||||||
|
title="No App Connections match search"
|
||||||
|
icon={faSearch}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{Boolean(filteredOptions.length) && (
|
||||||
|
<Pagination
|
||||||
|
startAdornment={
|
||||||
|
<Tooltip
|
||||||
|
side="bottom"
|
||||||
|
className="max-w-sm py-4"
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
<p className="mb-2">Infisical is constantly adding support for more services.</p>
|
||||||
|
<p>
|
||||||
|
{`If you don't see the third-party
|
||||||
|
service you're looking for,`}{" "}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
className="underline hover:text-mineshaft-300"
|
||||||
|
href="https://infisical.com/slack"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
let us know on Slack
|
||||||
|
</a>{" "}
|
||||||
|
or{" "}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
className="underline hover:text-mineshaft-300"
|
||||||
|
href="https://github.com/Infisical/infisical/discussions"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
make a request on GitHub
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="-ml-3 flex items-center gap-1.5 text-mineshaft-400">
|
||||||
|
<span className="text-xs">
|
||||||
|
Don't see the third-party service you're looking for?
|
||||||
|
</span>
|
||||||
|
<FontAwesomeIcon size="xs" icon={faInfoCircle} />
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
count={filteredOptions.length}
|
||||||
|
page={page}
|
||||||
|
perPage={perPage}
|
||||||
|
onChangePage={setPage}
|
||||||
|
onChangePerPage={setPerPage}
|
||||||
|
perPageList={[16]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<UpgradePlanModal
|
<UpgradePlanModal
|
||||||
isOpen={popUp.upgradePlan.isOpen}
|
isOpen={popUp.upgradePlan.isOpen}
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||||
text="You can use every App Connection if you switch to Infisical's Enterprise plan."
|
text="You can use every App Connection if you switch to Infisical's Enterprise plan."
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
|
||||||
side="bottom"
|
|
||||||
className="max-w-sm py-4"
|
|
||||||
content={
|
|
||||||
<>
|
|
||||||
<p className="mb-2">Infisical is constantly adding support for more connections.</p>
|
|
||||||
<p>
|
|
||||||
{`If you don't see the third-party
|
|
||||||
app you're looking for,`}{" "}
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
className="underline hover:text-mineshaft-300"
|
|
||||||
href="https://infisical.com/slack"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
let us know on Slack
|
|
||||||
</a>{" "}
|
|
||||||
or{" "}
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
className="underline hover:text-mineshaft-300"
|
|
||||||
href="https://github.com/Infisical/infisical/discussions"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
make a request on GitHub
|
|
||||||
</a>
|
|
||||||
.
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="group relative flex h-28 flex-col items-center justify-center rounded-md border border-dashed border-mineshaft-600 bg-mineshaft-800 p-4">
|
|
||||||
<FontAwesomeIcon className="mt-auto text-xl" icon={faWrench} />
|
|
||||||
<div className="mt-auto max-w-xs text-center text-sm font-medium text-gray-300 duration-200 group-hover:text-gray-200">
|
|
||||||
Coming Soon
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user