diff --git a/src/app/styles.css b/src/app/styles.css index 8751453..c49feab 100644 --- a/src/app/styles.css +++ b/src/app/styles.css @@ -20,6 +20,10 @@ @apply bg-base-100; } + .card-body { + @apply p-4 md:p-8; + } + .dropdown-content { @apply z-10; } diff --git a/src/components/containers/sidebar.tsx b/src/components/containers/sidebar.tsx index f5db872..84983e3 100644 --- a/src/components/containers/sidebar.tsx +++ b/src/components/containers/sidebar.tsx @@ -45,7 +45,7 @@ const Sidebar = () => { className={cn( "h-12 flex items-center px-6", isActive && - "bg-primary text-primary-content hover:bg-primary/60 focus:bg-primary" + "bg-primary text-primary-content hover:bg-primary/60 focus:bg-primary focus:text-primary-content" )} > diff --git a/src/components/layouts/main-layout.tsx b/src/components/layouts/main-layout.tsx index b9632b2..db6c261 100644 --- a/src/components/layouts/main-layout.tsx +++ b/src/components/layouts/main-layout.tsx @@ -45,29 +45,33 @@ const Header = ({ onSidebarOpen }: HeaderProps) => { const navigate = useNavigate(); return ( -
- {page?.prev ? ( - - ) : ( - + ) : ( +
); }; diff --git a/src/components/ui/toggle.tsx b/src/components/ui/toggle.tsx index 62bf409..6a3d1a0 100644 --- a/src/components/ui/toggle.tsx +++ b/src/components/ui/toggle.tsx @@ -42,7 +42,13 @@ export const ToggleField = ({ title={title} className={className} render={(field) => ( - + field.onChange(e.target.checked)} + /> )} /> ); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 1fd65bf..380b23e 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -22,3 +22,12 @@ export const readableBytes = (bytes?: number | null, divider = 1024) => { export const handleError = (err: unknown) => { toast.error((err as Error)?.message || "Unknown error"); }; + +export const copyToClipboard = async (text: string) => { + try { + await navigator.clipboard.writeText(text); + toast.success("Copied to clipboard"); + } catch (err) { + handleError(err); + } +}; diff --git a/src/pages/buckets/components/bucket-card.tsx b/src/pages/buckets/components/bucket-card.tsx index 449e201..7225a08 100644 --- a/src/pages/buckets/components/bucket-card.tsx +++ b/src/pages/buckets/components/bucket-card.tsx @@ -10,8 +10,8 @@ type Props = { const BucketCard = ({ data }: Props) => { return (
-
-
+
+

@@ -38,7 +38,7 @@ const BucketCard = ({ data }: Props) => {

-
+
{/* */}
diff --git a/src/pages/buckets/page.tsx b/src/pages/buckets/page.tsx index 48b6c2d..5240543 100644 --- a/src/pages/buckets/page.tsx +++ b/src/pages/buckets/page.tsx @@ -3,9 +3,24 @@ import { useBuckets } from "./hooks"; import { Input } from "react-daisyui"; import BucketCard from "./components/bucket-card"; import CreateBucketDialog from "./components/create-bucket-dialog"; +import { useMemo, useState } from "react"; const BucketsPage = () => { const { data } = useBuckets(); + const [search, setSearch] = useState(""); + + const items = useMemo(() => { + if (!search?.length) { + return data; + } + + const q = search.toLowerCase(); + return data?.filter( + (bucket) => + bucket.id.includes(q) || + bucket.globalAliases.find((alias) => alias.includes(q)) + ); + }, [data, search]); return (
@@ -13,13 +28,17 @@ const BucketsPage = () => {
- + setSearch(e.target.value)} + />
- {data?.map((bucket) => ( + {items?.map((bucket) => ( ))}
diff --git a/src/pages/keys/page.tsx b/src/pages/keys/page.tsx index da5fa79..e8ce2e2 100644 --- a/src/pages/keys/page.tsx +++ b/src/pages/keys/page.tsx @@ -1,14 +1,18 @@ import Button from "@/components/ui/button"; import Page from "@/context/page-context"; -import { Trash } from "lucide-react"; +import { Copy, Eye, Trash } from "lucide-react"; import { Card, Input, Table } from "react-daisyui"; import { useKeys, useRemoveKey } from "./hooks"; import CreateKeyDialog from "./components/create-key-dialog"; import { toast } from "sonner"; -import { handleError } from "@/lib/utils"; +import { copyToClipboard, handleError } from "@/lib/utils"; +import { useCallback, useMemo, useState } from "react"; +import api from "@/lib/api"; const KeysPage = () => { const { data, refetch } = useKeys(); + const [search, setSearch] = useState(""); + const [secretKeys, setSecretKeys] = useState>({}); const removeKey = useRemoveKey({ onSuccess: () => { @@ -18,18 +22,45 @@ const KeysPage = () => { onError: handleError, }); + const fetchSecretKey = useCallback(async (id: string) => { + try { + const result = await api.get("/v1/key", { + params: { id, showSecretKey: "true" }, + }); + if (!result?.secretAccessKey) { + throw new Error("Failed to fetch secret key"); + } + setSecretKeys((prev) => ({ ...prev, [id]: result.secretAccessKey })); + } catch (err) { + handleError(err); + } + }, []); + const onRemove = (id: string) => { if (window.confirm("Are you sure you want to remove this key?")) { removeKey.mutate(id); } }; + const items = useMemo(() => { + if (!search?.length) { + return data; + } + + const q = search.toLowerCase(); + return data?.filter((item) => item.id.includes(q) || item.name.includes(q)); + }, [data, search]); + return (
- + setSearch(e.target.value)} + />
@@ -39,17 +70,51 @@ const KeysPage = () => { # - Key ID Name + Key ID + Secret Key - {data?.map((key, idx) => ( + {items?.map((key, idx) => ( {idx + 1} - {key.id} {key.name} +
+

+ {key.id} +

+
+ {!secretKeys[key.id] ? ( + + ) : ( +
+

+ {secretKeys[key.id]} +

+
+ )} +