Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"use client";

import { SearchIcon } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";

import type { SearchType } from "./types";

export function AdvancedSearchInput(props: {
onSearch: (searchType: SearchType, query: string) => void;
onClear: () => void;
isLoading: boolean;
hasResults: boolean;
}) {
const [searchType, setSearchType] = useState<SearchType>("email");
const [query, setQuery] = useState("");

const handleSearch = () => {
if (query.trim()) {
props.onSearch(searchType, query.trim());
}
};

const handleClear = () => {
setQuery("");
props.onClear();
};

const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Enter") {
handleSearch();
}
};

return (
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<Select
value={searchType}
onValueChange={(value) => setSearchType(value as SearchType)}
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

Fix type safety issue in search type casting.

The type casting value as SearchType is not type-safe. The Select component should already ensure the value is valid, but consider adding runtime validation.

-          onValueChange={(value) => setSearchType(value as SearchType)}
+          onValueChange={(value) => {
+            if (value === "email" || value === "phone" || value === "id" || value === "address") {
+              setSearchType(value);
+            }
+          }}
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/users/components/AdvancedSearchInput.tsx
at line 48, the current type casting of value to SearchType is unsafe. Replace
the direct cast with a runtime check to confirm the value is a valid SearchType
before calling setSearchType. This can be done by verifying the value against
the allowed SearchType values and only setting the state if valid, otherwise
handle the invalid case appropriately.

>
<SelectTrigger className="w-[140px] bg-card">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="email">Email</SelectItem>
<SelectItem value="phone">Phone</SelectItem>
<SelectItem value="id">ID</SelectItem>
<SelectItem value="address">Address</SelectItem>
</SelectContent>
</Select>

<div className="relative flex-1">
<Input
className="bg-card pl-9"
placeholder={`Search by ${searchType}...`}
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={handleKeyDown}
/>
<SearchIcon className="-translate-y-1/2 absolute top-1/2 left-3 size-4 text-muted-foreground" />
</div>

<Button
onClick={handleSearch}
disabled={!query.trim() || props.isLoading}
size="sm"
>
{props.isLoading ? "Searching..." : "Search"}
</Button>
</div>

{props.hasResults && (
<div className="flex justify-center">
<Button
variant="outline"
size="sm"
onClick={handleClear}
className="gap-2"
>
Clear Search & Return to List
</Button>
</div>
)}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"use client";

import { format } from "date-fns";
import type { ThirdwebClient } from "thirdweb";
import { WalletAddress } from "@/components/blocks/wallet-address";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import type { UserSearchResult } from "./types";

const getUserIdentifier = (user: UserSearchResult) => {
return user.email ?? user.phone ?? user.walletAddress ?? user.userId;
};

export function SearchResults(props: {
results: UserSearchResult[];
client: ThirdwebClient;
}) {
if (props.results.length === 0) {
return (
<Card>
<CardContent className="py-8">
<div className="flex flex-col items-center gap-3">
<p className="text-sm">No users found</p>
<p className="text-muted-foreground text-sm">
Try searching with different criteria
</p>
</div>
</CardContent>
</Card>
);
}

return (
<div className="space-y-4">
{props.results.map((user) => (
<Card key={user.userId}>
<CardHeader>
<CardTitle className="text-lg">User Details</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<p className="text-sm font-medium text-muted-foreground">
User Identifier
</p>
<p className="text-sm">{getUserIdentifier(user)}</p>
</div>

<div>
<p className="text-sm font-medium text-muted-foreground">
Wallet Address
</p>
<WalletAddress
address={user.walletAddress}
client={props.client}
/>
</div>

{user.email && (
<div>
<p className="text-sm font-medium text-muted-foreground">
Email
</p>
<p className="text-sm">{user.email}</p>
</div>
)}

{user.phone && (
<div>
<p className="text-sm font-medium text-muted-foreground">
Phone
</p>
<p className="text-sm">{user.phone}</p>
</div>
)}

<div>
<p className="text-sm font-medium text-muted-foreground">
Created
</p>
<p className="text-sm">
{format(new Date(user.createdAt), "MMM dd, yyyy")}
</p>
</div>

<div>
<p className="text-sm font-medium text-muted-foreground">
Login Methods
</p>
<div className="flex flex-wrap gap-1">
{user.linkedAccounts.map((account, index) => (
<TooltipProvider
key={`${user.userId}-${account.type}-${index}`}
>
<Tooltip>
<TooltipTrigger>
<Badge variant="secondary" className="text-xs">
{account.type}
</Badge>
</TooltipTrigger>
<TooltipContent>
<div className="text-sm space-y-1">
{Object.entries(account.details).map(
([key, value]) => (
<div key={key}>
<span className="font-medium">{key}:</span>{" "}
{String(value)}
</div>
),
)}
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 safety check for account details rendering.

The code assumes account.details exists and contains valid data. Consider adding safety checks to prevent rendering errors.

                         <TooltipContent>
                           <div className="text-sm space-y-1">
-                            {Object.entries(account.details).map(
+                            {Object.entries(account.details || {}).map(
                               ([key, value]) => (
-                                <div key={key}>
-                                  <span className="font-medium">{key}:</span>{" "}
-                                  {String(value)}
-                                </div>
+                                value !== undefined && value !== null && (
+                                  <div key={key}>
+                                    <span className="font-medium">{key}:</span>{" "}
+                                    {String(value)}
+                                  </div>
+                                )
                               ),
                             )}
                           </div>
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/users/components/SearchResults.tsx
around lines 109 to 116, the code assumes account.details always exists and is
iterable, which can cause runtime errors if it is undefined or null. Add a
safety check before mapping over account.details, such as verifying it is an
object or has entries, to prevent rendering errors when account.details is
missing or invalid.

</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
))}
</div>
</div>
</div>
</CardContent>
</Card>
))}
</div>
);
}
Loading
Loading