From 3a87b30140dfe7375ac4f5edfd306de4bcb0cc8d Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Tue, 10 Sep 2024 08:35:21 +0200 Subject: [PATCH 1/5] chore: change delay for debouncedSearchQuery function --- components/filters/CustomSearchInput.tsx | 2 +- hooks/useDebounce.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/filters/CustomSearchInput.tsx b/components/filters/CustomSearchInput.tsx index b3a827abb7..c6ded1a175 100644 --- a/components/filters/CustomSearchInput.tsx +++ b/components/filters/CustomSearchInput.tsx @@ -11,7 +11,7 @@ export const CustomSearchInput: React.FC = () => { const [searchQuery, setSearchQuery] = useState(() => { return searchParams.get("filter[search]") || ""; }); - const debouncedSearchQuery = useDebounce(searchQuery, 300); + const debouncedSearchQuery = useDebounce(searchQuery, 500); const applySearch = useCallback( (query: string) => { diff --git a/hooks/useDebounce.ts b/hooks/useDebounce.ts index f0d8d7c3bf..100322b91c 100644 --- a/hooks/useDebounce.ts +++ b/hooks/useDebounce.ts @@ -4,7 +4,7 @@ export function useDebounce(value: T, delay?: number): T { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { - const timer = setTimeout(() => setDebouncedValue(value), delay ?? 900); + const timer = setTimeout(() => setDebouncedValue(value), delay ?? 500); return () => { clearTimeout(timer); From 457c845af84ef7b235e8ddf8760e2a5f49ae3fc8 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Tue, 10 Sep 2024 11:28:36 +0200 Subject: [PATCH 2/5] chore: WIP --- components/filters/CustomSearchInput.tsx | 35 ++++++++++++++++-------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/components/filters/CustomSearchInput.tsx b/components/filters/CustomSearchInput.tsx index c6ded1a175..513ff739a9 100644 --- a/components/filters/CustomSearchInput.tsx +++ b/components/filters/CustomSearchInput.tsx @@ -8,20 +8,24 @@ import { useDebounce } from "../../hooks/useDebounce"; export const CustomSearchInput: React.FC = () => { const router = useRouter(); const searchParams = useSearchParams(); - const [searchQuery, setSearchQuery] = useState(() => { - return searchParams.get("filter[search]") || ""; - }); + // const [searchQuery, setSearchQuery] = useState(() => { + // return searchParams.get("filter[search]") || ""; + // }); + const [searchQuery, setSearchQuery] = useState(""); + const debouncedSearchQuery = useDebounce(searchQuery, 500); + console.log("debouncedSearchQuery", debouncedSearchQuery); const applySearch = useCallback( (query: string) => { const params = new URLSearchParams(searchParams.toString()); if (query) { params.set("filter[search]", query); + setSearchQuery(query); } else { params.delete("filter[search]"); } - router.replace(`?${params.toString()}`, { scroll: false }); + router.push(`?${params.toString()}`, { scroll: false }); }, [router, searchParams], ); @@ -32,8 +36,18 @@ export const CustomSearchInput: React.FC = () => { }; useEffect(() => { - applySearch(debouncedSearchQuery); - }, [debouncedSearchQuery, applySearch]); + const searchFromUrl = searchParams.get("filter[search]") || ""; + setSearchQuery(searchFromUrl); + }, [searchParams]); + + // useEffect(() => { + // const timer = setTimeout(() => { + // applySearch(debouncedSearchQuery); + // console.log("hi from useEffect"); + // }, 2000); + + // return () => clearTimeout(timer); + // }, [debouncedSearchQuery, applySearch, searchParams]); return ( { label="Search" labelPlacement="inside" value={searchQuery} - onChange={(e) => setSearchQuery(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter") { - applySearch(searchQuery); - } + onChange={(e) => { + const value = e.target.value; + setSearchQuery(value); + applySearch(value); }} endContent={ searchQuery && ( From 5163bcb72c1e051e8f2f55fb6ee7509fa95fcd0a Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Tue, 10 Sep 2024 14:13:08 +0200 Subject: [PATCH 3/5] chore: add new component for provider ID --- components/filters/CustomSearchInput.tsx | 6 +- components/icons/Icons.tsx | 111 ++++++++++++++++++ components/providers/ProviderInfo.tsx | 39 ++++-- components/providers/SnippetIdProvider.tsx | 28 +++++ components/providers/index.ts | 1 + .../providers/table/ColumnsProvider.tsx | 19 ++- 6 files changed, 186 insertions(+), 18 deletions(-) create mode 100644 components/providers/SnippetIdProvider.tsx diff --git a/components/filters/CustomSearchInput.tsx b/components/filters/CustomSearchInput.tsx index 513ff739a9..f5f4508a5a 100644 --- a/components/filters/CustomSearchInput.tsx +++ b/components/filters/CustomSearchInput.tsx @@ -3,7 +3,7 @@ import { XCircle } from "lucide-react"; import { useRouter, useSearchParams } from "next/navigation"; import React, { useCallback, useEffect, useState } from "react"; -import { useDebounce } from "../../hooks/useDebounce"; +// import { useDebounce } from "../../hooks/useDebounce"; export const CustomSearchInput: React.FC = () => { const router = useRouter(); @@ -13,8 +13,8 @@ export const CustomSearchInput: React.FC = () => { // }); const [searchQuery, setSearchQuery] = useState(""); - const debouncedSearchQuery = useDebounce(searchQuery, 500); - console.log("debouncedSearchQuery", debouncedSearchQuery); + // const debouncedSearchQuery = useDebounce(searchQuery, 500); + // console.log("debouncedSearchQuery", debouncedSearchQuery); const applySearch = useCallback( (query: string) => { diff --git a/components/icons/Icons.tsx b/components/icons/Icons.tsx index 6024849b2d..f665b49628 100644 --- a/components/icons/Icons.tsx +++ b/components/icons/Icons.tsx @@ -464,6 +464,117 @@ export const NotificationIcon: React.FC = ({ ); +export const IdIcon: React.FC = ({ + size = 24, + width, + height, + ...props +}) => ( + + + +); + +export const DoneIcon: React.FC = ({ + size, + height, + width, + ...props +}) => { + return ( + + + + ); +}; + +export const CopyIcon: React.FC = ({ + size, + height, + width, + ...props +}) => { + return ( + + + + ); +}; + +export const FlowIcon: React.FC = ({ + size, + height, + width, + ...props +}) => { + return ( + + + + ); +}; + +export const ConnectionIcon: React.FC = ({ + size, + height, + width, + ...props +}) => { + return ( + + + + ); +}; + export const SuccessIcon: React.FC = ({ size = 24, width, diff --git a/components/providers/ProviderInfo.tsx b/components/providers/ProviderInfo.tsx index 8730e6cfcd..39d33a62fb 100644 --- a/components/providers/ProviderInfo.tsx +++ b/components/providers/ProviderInfo.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { WifiIcon, WifiOffIcon, WifiPendingIcon } from "../icons"; +import { ConnectionIcon } from "../icons"; import { AWSProviderBadge, AzureProviderBadge, @@ -12,24 +12,35 @@ interface ProviderInfoProps { connected: boolean | null; provider: "aws" | "azure" | "gcp" | "kubernetes"; providerAlias: string; - providerId: string; } export const ProviderInfo: React.FC = ({ connected, provider, providerAlias, - providerId, }) => { const getIcon = () => { switch (connected) { case true: - return ; + return ( +
+ +
+ ); case false: - return ; + return ( +
+ +
+ ); case null: + return ( +
+ +
+ ); default: - return ; + return ; } }; @@ -49,13 +60,15 @@ export const ProviderInfo: React.FC = ({ }; return ( -
-
-
{getIcon()}
-
{getProviderLogo()}
-
- {providerAlias} - {providerId} +
+
+
+
{getProviderLogo()}
+
{getIcon()}
+
+ {providerAlias} + {providerAlias} +
diff --git a/components/providers/SnippetIdProvider.tsx b/components/providers/SnippetIdProvider.tsx new file mode 100644 index 0000000000..775e72eaf7 --- /dev/null +++ b/components/providers/SnippetIdProvider.tsx @@ -0,0 +1,28 @@ +import { Snippet } from "@nextui-org/react"; +import React from "react"; + +import { CopyIcon, DoneIcon, IdIcon } from "../icons"; + +interface SnippetIdProviderProps { + providerId: string; +} +export const SnippetIdProvider: React.FC = ({ + providerId, +}) => { + return ( + } + checkIcon={} + > +

+ + {providerId} +

+
+ ); +}; diff --git a/components/providers/index.ts b/components/providers/index.ts index 58c8dae90f..a0129d3520 100644 --- a/components/providers/index.ts +++ b/components/providers/index.ts @@ -7,6 +7,7 @@ export * from "./DateWithTime"; export * from "./DeleteProvider"; export * from "./ProviderInfo"; export * from "./ScanStatus"; +export * from "./SnippetIdProvider"; export * from "./table/ColumnsProvider"; export * from "./table/DataTableColumnHeader"; export * from "./table/DataTablePagination"; diff --git a/components/providers/table/ColumnsProvider.tsx b/components/providers/table/ColumnsProvider.tsx index e08050e620..725a4e6fd1 100644 --- a/components/providers/table/ColumnsProvider.tsx +++ b/components/providers/table/ColumnsProvider.tsx @@ -31,6 +31,8 @@ import { EditDocumentBulkIcon, } from "@nextui-org/shared-icons"; +import { SnippetIdProvider } from "../SnippetIdProvider"; + const iconClasses = "text-2xl text-default-500 pointer-events-none flex-shrink-0"; @@ -46,18 +48,29 @@ export const ColumnsProvider: ColumnDef[] = [ ), cell: ({ row }) => { const { - attributes: { connection, provider, alias, provider_id }, + attributes: { connection, provider, alias }, } = getProviderData(row); return ( ); }, }, + { + accessorKey: "account", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const { + attributes: { provider_id }, + } = getProviderData(row); + return ; + }, + }, { accessorKey: "status", header: "Scan Status", @@ -144,6 +157,7 @@ export const ColumnsProvider: ColumnDef[] = [ key="new" description="Check the connection to the provider" shortcut="⌘N" + textValue="Check Connection" startContent={} > @@ -152,6 +166,7 @@ export const ColumnsProvider: ColumnDef[] = [ key="edit" description="Allows you to edit the provider" shortcut="⌘⇧E" + textValue="Edit Provider" startContent={ } From 67f572285b89fc79df6a90a804ac428b75edec86 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Wed, 11 Sep 2024 15:03:51 +0200 Subject: [PATCH 4/5] refactor: tweaks styles for providers table --- .env.template | 3 +++ components/filters/CustomSearchInput.tsx | 4 ++-- components/providers/CheckConnectionProvider.tsx | 7 ++++--- components/providers/ProviderInfo.tsx | 13 +++++++------ components/providers/table/ColumnsProvider.tsx | 4 ++-- components/ui/custom/CustomLoader.tsx | 16 ++++++++++++++++ components/ui/custom/index.ts | 1 + 7 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 components/ui/custom/CustomLoader.tsx diff --git a/.env.template b/.env.template index 35ac3b9b58..0ac689045f 100644 --- a/.env.template +++ b/.env.template @@ -1,2 +1,5 @@ +SITE_URL=http://localhost:3000 +API_BASE_URL=http://localhost:8080/api/v1 + # openssl rand -base64 32 AUTH_SECRET=your-secret-key diff --git a/components/filters/CustomSearchInput.tsx b/components/filters/CustomSearchInput.tsx index f5f4508a5a..d1f7edb400 100644 --- a/components/filters/CustomSearchInput.tsx +++ b/components/filters/CustomSearchInput.tsx @@ -3,7 +3,7 @@ import { XCircle } from "lucide-react"; import { useRouter, useSearchParams } from "next/navigation"; import React, { useCallback, useEffect, useState } from "react"; -// import { useDebounce } from "../../hooks/useDebounce"; +import { useDebounce } from "../../hooks/useDebounce"; export const CustomSearchInput: React.FC = () => { const router = useRouter(); @@ -13,7 +13,7 @@ export const CustomSearchInput: React.FC = () => { // }); const [searchQuery, setSearchQuery] = useState(""); - // const debouncedSearchQuery = useDebounce(searchQuery, 500); + const debouncedSearchQuery = useDebounce(searchQuery, 500); // console.log("debouncedSearchQuery", debouncedSearchQuery); const applySearch = useCallback( diff --git a/components/providers/CheckConnectionProvider.tsx b/components/providers/CheckConnectionProvider.tsx index a7d4ec8360..2fadafad04 100644 --- a/components/providers/CheckConnectionProvider.tsx +++ b/components/providers/CheckConnectionProvider.tsx @@ -27,15 +27,16 @@ export const CheckConnectionProvider = ({ id }: { id: string }) => { }); } else { toast({ - title: "Success!", - description: "The connection was updated successfully.", + title: "Checking", + description: "The task was launched successfully", }); } } + return (
- + ); }; diff --git a/components/providers/ProviderInfo.tsx b/components/providers/ProviderInfo.tsx index 39d33a62fb..96f9df0cbd 100644 --- a/components/providers/ProviderInfo.tsx +++ b/components/providers/ProviderInfo.tsx @@ -7,6 +7,7 @@ import { GCPProviderBadge, KS8ProviderBadge, } from "../icons/providers-badge"; +import { CustomLoader } from "../ui/custom"; interface ProviderInfoProps { connected: boolean | null; @@ -24,23 +25,23 @@ export const ProviderInfo: React.FC = ({ case true: return (
- +
); case false: return ( -
- +
+
); case null: return (
- +
); default: - return ; + return ; } }; @@ -67,7 +68,7 @@ export const ProviderInfo: React.FC = ({
{getIcon()}
{providerAlias} - {providerAlias} + {/* */}
diff --git a/components/providers/table/ColumnsProvider.tsx b/components/providers/table/ColumnsProvider.tsx index 725a4e6fd1..33c79a25bd 100644 --- a/components/providers/table/ColumnsProvider.tsx +++ b/components/providers/table/ColumnsProvider.tsx @@ -38,7 +38,7 @@ const iconClasses = export const ColumnsProvider: ColumnDef[] = [ { - header: "ID", + header: "n", cell: ({ row }) =>

{row.index + 1}

, }, { @@ -60,7 +60,7 @@ export const ColumnsProvider: ColumnDef[] = [ }, }, { - accessorKey: "account", + accessorKey: "id", header: ({ column }) => ( ), diff --git a/components/ui/custom/CustomLoader.tsx b/components/ui/custom/CustomLoader.tsx new file mode 100644 index 0000000000..4dd67b6e78 --- /dev/null +++ b/components/ui/custom/CustomLoader.tsx @@ -0,0 +1,16 @@ +"use client"; + +import { useEffect, useState } from "react"; + +export const CustomLoader = (props: { size?: "small" }) => { + const [loadingSpinner, setloadingSpinner] = useState(0); + const loadingChars = "|/-\\"; + + const textClasses = `w-xs px-xs ${props.size === "small" ? "!text-s" : ""}`; + + useEffect(() => { + setTimeout(() => setloadingSpinner(loadingSpinner + 1), 150); + }, [loadingSpinner]); + + return

{loadingChars[loadingSpinner % 4]}

; +}; diff --git a/components/ui/custom/index.ts b/components/ui/custom/index.ts index dbc111a3cc..1599c7850f 100644 --- a/components/ui/custom/index.ts +++ b/components/ui/custom/index.ts @@ -1,3 +1,4 @@ export * from "./CustomBox"; export * from "./CustomButtonClientAction"; export * from "./CustomInput"; +export * from "./CustomLoader"; From e7eb57375e856ab92444db74a5a3e402f16d41e6 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Wed, 11 Sep 2024 16:49:43 +0200 Subject: [PATCH 5/5] feat: the search debounce is working now as expected --- components/filters/CustomSearchInput.tsx | 24 +++++------------------- hooks/index.ts | 1 - hooks/useDebounce.ts | 15 --------------- 3 files changed, 5 insertions(+), 35 deletions(-) delete mode 100644 hooks/useDebounce.ts diff --git a/components/filters/CustomSearchInput.tsx b/components/filters/CustomSearchInput.tsx index d1f7edb400..98310f10b5 100644 --- a/components/filters/CustomSearchInput.tsx +++ b/components/filters/CustomSearchInput.tsx @@ -1,27 +1,20 @@ import { Input } from "@nextui-org/react"; +import debounce from "lodash.debounce"; import { XCircle } from "lucide-react"; import { useRouter, useSearchParams } from "next/navigation"; import React, { useCallback, useEffect, useState } from "react"; -import { useDebounce } from "../../hooks/useDebounce"; - export const CustomSearchInput: React.FC = () => { const router = useRouter(); const searchParams = useSearchParams(); - // const [searchQuery, setSearchQuery] = useState(() => { - // return searchParams.get("filter[search]") || ""; - // }); - const [searchQuery, setSearchQuery] = useState(""); - const debouncedSearchQuery = useDebounce(searchQuery, 500); - // console.log("debouncedSearchQuery", debouncedSearchQuery); + const [searchQuery, setSearchQuery] = useState(""); const applySearch = useCallback( (query: string) => { const params = new URLSearchParams(searchParams.toString()); if (query) { params.set("filter[search]", query); - setSearchQuery(query); } else { params.delete("filter[search]"); } @@ -30,6 +23,8 @@ export const CustomSearchInput: React.FC = () => { [router, searchParams], ); + const debouncedChangeHandler = useCallback(debounce(applySearch, 300), []); + const clearIconSearch = () => { setSearchQuery(""); applySearch(""); @@ -40,15 +35,6 @@ export const CustomSearchInput: React.FC = () => { setSearchQuery(searchFromUrl); }, [searchParams]); - // useEffect(() => { - // const timer = setTimeout(() => { - // applySearch(debouncedSearchQuery); - // console.log("hi from useEffect"); - // }, 2000); - - // return () => clearTimeout(timer); - // }, [debouncedSearchQuery, applySearch, searchParams]); - return ( { onChange={(e) => { const value = e.target.value; setSearchQuery(value); - applySearch(value); + debouncedChangeHandler(value); }} endContent={ searchQuery && ( diff --git a/hooks/index.ts b/hooks/index.ts index b32251b922..709aadd864 100644 --- a/hooks/index.ts +++ b/hooks/index.ts @@ -1,2 +1 @@ -export * from "./useDebounce"; export * from "./useLocalStorage"; diff --git a/hooks/useDebounce.ts b/hooks/useDebounce.ts deleted file mode 100644 index 100322b91c..0000000000 --- a/hooks/useDebounce.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useEffect, useState } from "react"; - -export function useDebounce(value: T, delay?: number): T { - const [debouncedValue, setDebouncedValue] = useState(value); - - useEffect(() => { - const timer = setTimeout(() => setDebouncedValue(value), delay ?? 500); - - return () => { - clearTimeout(timer); - }; - }, [value, delay]); - - return debouncedValue; -}