Skip to content

[Dashboard] add wallet user search #7182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use client";

import { Input } from "@/components/ui/input";
import { SearchIcon } from "lucide-react";

export function SearchInput(props: {
placeholder: string;
value: string;
onValueChange: (value: string) => void;
}) {
return (
<div className="relative">
<Input
placeholder={props.placeholder}
value={props.value}
onChange={(e) => props.onValueChange(e.target.value)}
className="bg-card pl-9"
/>
<SearchIcon className="-translate-y-1/2 absolute top-1/2 left-3 size-4 text-muted-foreground" />
Comment on lines +13 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add accessibility attributes for better screen reader support.

The input field lacks proper accessibility attributes which could impact users with assistive technologies.

      <Input
        placeholder={props.placeholder}
        value={props.value}
        onChange={(e) => props.onValueChange(e.target.value)}
        className="bg-card pl-9"
+       aria-label={props.placeholder || "Search"}
+       type="search"
      />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Input
placeholder={props.placeholder}
value={props.value}
onChange={(e) => props.onValueChange(e.target.value)}
className="bg-card pl-9"
/>
<SearchIcon className="-translate-y-1/2 absolute top-1/2 left-3 size-4 text-muted-foreground" />
<Input
placeholder={props.placeholder}
value={props.value}
onChange={(e) => props.onValueChange(e.target.value)}
className="bg-card pl-9"
aria-label={props.placeholder || "Search"}
type="search"
/>
<SearchIcon className="-translate-y-1/2 absolute top-1/2 left-3 size-4 text-muted-foreground" />
🤖 Prompt for AI Agents
In apps/dashboard/src/components/embedded-wallets/Users/SearchInput.tsx around
lines 13 to 19, the Input component is missing accessibility attributes. Add
appropriate attributes such as aria-label or aria-labelledby to the Input
element to provide descriptive text for screen readers, ensuring the input field
is accessible to users with assistive technologies.

</div>
);
}
38 changes: 36 additions & 2 deletions apps/dashboard/src/components/embedded-wallets/Users/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react";
import Papa from "papaparse";
import { useCallback, useState } from "react";
import type { WalletUser } from "thirdweb/wallets";
import { SearchInput } from "./SearchInput";

const getUserIdentifier = (accounts: WalletUser["linkedAccounts"]) => {
const mainDetail = accounts[0]?.details;
Expand Down Expand Up @@ -103,12 +104,38 @@ export function InAppWalletUsersPageContent(props: {
authToken: string;
}) {
const [activePage, setActivePage] = useState(1);
const [searchValue, setSearchValue] = useState("");
const walletsQuery = useEmbeddedWallets({
authToken: props.authToken,
clientId: props.clientId,
page: activePage,
});
const wallets = walletsQuery?.data?.users || [];
const filteredWallets = searchValue
? wallets.filter((wallet) => {
const term = searchValue.toLowerCase();
if (wallet.id.toLowerCase().includes(term)) {
return true;
}
if (
wallet.wallets?.some((w) => w.address?.toLowerCase().includes(term))
) {
return true;
}
if (
wallet.linkedAccounts?.some((acc) => {
return Object.values(acc.details).some(
(detail) =>
typeof detail === "string" &&
detail.toLowerCase().includes(term),
);
})
) {
return true;
}
return false;
})
: wallets;
Comment on lines +114 to +138
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add performance optimization and fix potential null access.

The filtering logic is comprehensive and handles multiple search criteria well. However, consider adding debouncing for better performance and fix a potential null access issue.

+import { useMemo } from "react";
+import { useDebounce } from "@/hooks/useDebounce"; // or implement debouncing

 export function InAppWalletUsersPageContent(props: {
   clientId: string;
   trackingCategory: string;
   authToken: string;
 }) {
   const [activePage, setActivePage] = useState(1);
   const [searchValue, setSearchValue] = useState("");
+  const debouncedSearchValue = useDebounce(searchValue, 300);
   
-  const filteredWallets = searchValue
+  const filteredWallets = useMemo(() => debouncedSearchValue
     ? wallets.filter((wallet) => {
-        const term = searchValue.toLowerCase();
+        const term = debouncedSearchValue.toLowerCase();
         if (wallet.id.toLowerCase().includes(term)) {
           return true;
         }
         if (
           wallet.wallets?.some((w) => w.address?.toLowerCase().includes(term))
         ) {
           return true;
         }
         if (
           wallet.linkedAccounts?.some((acc) => {
+            if (!acc.details) return false;
             return Object.values(acc.details).some(
               (detail) =>
                 typeof detail === "string" &&
                 detail.toLowerCase().includes(term),
             );
           })
         ) {
           return true;
         }
         return false;
       })
-    : wallets;
+    : wallets, [debouncedSearchValue, wallets]);

Note: The current implementation only searches within the current page of results. Consider if this meets the expected user behavior or if server-side search would be more appropriate for larger datasets.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const filteredWallets = searchValue
? wallets.filter((wallet) => {
const term = searchValue.toLowerCase();
if (wallet.id.toLowerCase().includes(term)) {
return true;
}
if (
wallet.wallets?.some((w) => w.address?.toLowerCase().includes(term))
) {
return true;
}
if (
wallet.linkedAccounts?.some((acc) => {
return Object.values(acc.details).some(
(detail) =>
typeof detail === "string" &&
detail.toLowerCase().includes(term),
);
})
) {
return true;
}
return false;
})
: wallets;
import { useMemo } from "react";
import { useDebounce } from "@/hooks/useDebounce";
export function InAppWalletUsersPageContent(props: {
clientId: string;
trackingCategory: string;
authToken: string;
}) {
const [activePage, setActivePage] = useState(1);
const [searchValue, setSearchValue] = useState("");
const debouncedSearchValue = useDebounce(searchValue, 300);
const filteredWallets = useMemo(
() =>
debouncedSearchValue
? wallets.filter((wallet) => {
const term = debouncedSearchValue.toLowerCase();
if (wallet.id.toLowerCase().includes(term)) {
return true;
}
if (
wallet.wallets?.some((w) =>
w.address?.toLowerCase().includes(term)
)
) {
return true;
}
if (
wallet.linkedAccounts?.some((acc) => {
if (!acc.details) return false;
return Object.values(acc.details).some(
(detail) =>
typeof detail === "string" &&
detail.toLowerCase().includes(term)
);
})
) {
return true;
}
return false;
})
: wallets,
[debouncedSearchValue, wallets]
);
// …rest of component
}
🤖 Prompt for AI Agents
In apps/dashboard/src/components/embedded-wallets/Users/index.tsx around lines
114 to 138, the filtering logic should be optimized by adding debouncing to the
search input to reduce the frequency of filter executions and improve
performance. Additionally, ensure all optional chaining is correctly applied to
avoid potential null or undefined access, especially when accessing nested
properties like wallet.wallets and wallet.linkedAccounts. Review whether the
current client-side filtering on the current page meets user expectations or if
a server-side search implementation is needed for larger datasets.

const { mutateAsync: getAllEmbeddedWallets, isPending } =
useAllEmbeddedWallets({
authToken: props.authToken,
Expand Down Expand Up @@ -146,7 +173,14 @@ export function InAppWalletUsersPageContent(props: {
<div>
<div className="flex flex-col gap-4">
{/* Top section */}
<div className="flex items-center justify-end">
<div className="flex items-center justify-end gap-3">
<div className="w-full max-w-xs">
<SearchInput
placeholder="Search"
value={searchValue}
onValueChange={setSearchValue}
/>
</div>
<Button
disabled={wallets.length === 0 || isPending}
variant="outline"
Expand All @@ -162,7 +196,7 @@ export function InAppWalletUsersPageContent(props: {
<div>
<TWTable
title="in-app wallets"
data={wallets}
data={filteredWallets}
columns={columns}
isPending={walletsQuery.isPending}
isFetched={walletsQuery.isFetched}
Expand Down
Loading