feat: add profile search

This commit is contained in:
zhom committed 2025-08-24 21:49:08 +04:00
1 parent 274b275c03
commit 181c76980a
2 files changed
+61 -8

No files matched your search

+32 -4
View File
@@ -80,6 +80,7 @@ export default function Home() {
string[] string[]
>([]); >([]);
const [selectedProfiles, setSelectedProfiles] = useState<string[]>([]); const [selectedProfiles, setSelectedProfiles] = useState<string[]>([]);
const [searchQuery, setSearchQuery] = useState<string>("");
const [pendingUrls, setPendingUrls] = useState<PendingUrl[]>([]); const [pendingUrls, setPendingUrls] = useState<PendingUrl[]>([]);
const [currentProfileForCamoufoxConfig, setCurrentProfileForCamoufoxConfig] = const [currentProfileForCamoufoxConfig, setCurrentProfileForCamoufoxConfig] =
useState<BrowserProfile | null>(null); useState<BrowserProfile | null>(null);
@@ -655,14 +656,39 @@ export default function Home() {
} }
}, [isInitialized, checkAllPermissions]); }, [isInitialized, checkAllPermissions]);
// Filter data by selected group // Filter data by selected group and search query
const filteredProfiles = useMemo(() => { const filteredProfiles = useMemo(() => {
let filtered = profiles;
// Filter by group
if (!selectedGroupId || selectedGroupId === "default") { if (!selectedGroupId || selectedGroupId === "default") {
return profiles.filter((profile) => !profile.group_id); filtered = profiles.filter((profile) => !profile.group_id);
} else {
filtered = profiles.filter(
(profile) => profile.group_id === selectedGroupId,
);
} }
return profiles.filter((profile) => profile.group_id === selectedGroupId); // Filter by search query
}, [profiles, selectedGroupId]); if (searchQuery.trim()) {
const query = searchQuery.toLowerCase().trim();
filtered = filtered.filter((profile) => {
// Search in profile name
if (profile.name.toLowerCase().includes(query)) return true;
// Search in browser name
if (profile.browser.toLowerCase().includes(query)) return true;
// Search in tags
if (profile.tags?.some((tag) => tag.toLowerCase().includes(query)))
return true;
return false;
});
}
return filtered;
}, [profiles, selectedGroupId, searchQuery]);
// Update loading states // Update loading states
const isLoading = profilesLoading || groupsLoading || proxiesLoading; const isLoading = profilesLoading || groupsLoading || proxiesLoading;
@@ -680,6 +706,8 @@ export default function Home() {
onImportProfileDialogOpen={setImportProfileDialogOpen} onImportProfileDialogOpen={setImportProfileDialogOpen}
onProxyManagementDialogOpen={setProxyManagementDialogOpen} onProxyManagementDialogOpen={setProxyManagementDialogOpen}
onSettingsDialogOpen={setSettingsDialogOpen} onSettingsDialogOpen={setSettingsDialogOpen}
searchQuery={searchQuery}
onSearchQueryChange={setSearchQuery}
/> />
</div> </div>
<div className="space-y-4 w-full"> <div className="space-y-4 w-full">
+29 -4
View File
@@ -1,7 +1,7 @@
import { FaDownload } from "react-icons/fa"; import { FaDownload } from "react-icons/fa";
import { FiWifi } from "react-icons/fi"; import { FiWifi } from "react-icons/fi";
import { GoGear, GoKebabHorizontal, GoPlus } from "react-icons/go"; import { GoGear, GoKebabHorizontal, GoPlus } from "react-icons/go";
import { LuTrash2, LuUsers } from "react-icons/lu"; import { LuSearch, LuTrash2, LuUsers, LuX } from "react-icons/lu";
import { Logo } from "./icons/logo"; import { Logo } from "./icons/logo";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { CardTitle } from "./ui/card"; import { CardTitle } from "./ui/card";
@@ -11,6 +11,7 @@ import {
DropdownMenuItem, DropdownMenuItem,
DropdownMenuTrigger, DropdownMenuTrigger,
} from "./ui/dropdown-menu"; } from "./ui/dropdown-menu";
import { Input } from "./ui/input";
import { RippleButton } from "./ui/ripple"; import { RippleButton } from "./ui/ripple";
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
@@ -23,6 +24,8 @@ type Props = {
onGroupManagementDialogOpen: (open: boolean) => void; onGroupManagementDialogOpen: (open: boolean) => void;
onImportProfileDialogOpen: (open: boolean) => void; onImportProfileDialogOpen: (open: boolean) => void;
onCreateProfileDialogOpen: (open: boolean) => void; onCreateProfileDialogOpen: (open: boolean) => void;
searchQuery: string;
onSearchQueryChange: (query: string) => void;
}; };
const HomeHeader = ({ const HomeHeader = ({
@@ -34,6 +37,8 @@ const HomeHeader = ({
onGroupManagementDialogOpen, onGroupManagementDialogOpen,
onImportProfileDialogOpen, onImportProfileDialogOpen,
onCreateProfileDialogOpen, onCreateProfileDialogOpen,
searchQuery,
onSearchQueryChange,
}: Props) => { }: Props) => {
const handleLogoClick = () => { const handleLogoClick = () => {
// Trigger the same URL handling logic as if the URL came from the system // Trigger the same URL handling logic as if the URL came from the system
@@ -76,7 +81,7 @@ const HomeHeader = ({
className="flex gap-2 items-center" className="flex gap-2 items-center"
> >
<LuTrash2 className="w-4 h-4" /> <LuTrash2 className="w-4 h-4" />
Delete Selected Delete
</RippleButton> </RippleButton>
</div> </div>
</div> </div>
@@ -85,12 +90,32 @@ const HomeHeader = ({
)} )}
</div> </div>
<div className="flex gap-2 items-center"> <div className="flex gap-2 items-center">
<div className="relative">
<Input
type="text"
placeholder="Search profiles..."
value={searchQuery}
onChange={(e) => onSearchQueryChange(e.target.value)}
className="pr-8 pl-10 w-48"
/>
<LuSearch className="absolute left-3 top-1/2 w-4 h-4 transform -translate-y-1/2 text-muted-foreground" />
{searchQuery && (
<button
type="button"
onClick={() => onSearchQueryChange("")}
className="absolute right-2 top-1/2 p-1 rounded-sm transition-colors transform -translate-y-1/2 hover:bg-accent"
aria-label="Clear search"
>
<LuX className="w-4 h-4 text-muted-foreground hover:text-foreground" />
</button>
)}
</div>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button <Button
size="sm" size="sm"
variant="outline" variant="outline"
className="flex gap-2 items-center" className="flex gap-2 items-center h-[36px]"
> >
<GoKebabHorizontal className="w-4 h-4" /> <GoKebabHorizontal className="w-4 h-4" />
</Button> </Button>
@@ -138,7 +163,7 @@ const HomeHeader = ({
onClick={() => { onClick={() => {
onCreateProfileDialogOpen(true); onCreateProfileDialogOpen(true);
}} }}
className="flex gap-2 items-center" className="flex gap-2 items-center h-[36px]"
> >
<GoPlus className="w-4 h-4" /> <GoPlus className="w-4 h-4" />
</Button> </Button>