Skip to content
This repository was archived by the owner on Mar 16, 2026. It is now read-only.

Commit 6ec8985

Browse files
authored
Merge pull request #742 from aibtcdev/feat/search
Feat/search
2 parents 2f12e72 + 27ac97b commit 6ec8985

2 files changed

Lines changed: 92 additions & 22 deletions

File tree

src/components/aidaos/DAOTabLayout.tsx

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,25 @@ export function DAOTabLayout({
2929
<div className="mx-auto space-y-4 rounded-sm">
3030
{/* Header Section */}
3131
<div className="space-y-2 px-2">
32-
<div className="flex items-center gap-3 px-2">
33-
{Icon && (
34-
<div className="w-10 h-10 rounded-sm bg-gradient-to-br from-secondary/20 to-secondary/10 flex items-center justify-center">
35-
<Icon className="h-5 w-5 text-secondary" />
36-
</div>
37-
)}
38-
<div>
39-
<h2 className="text-2xl font-bold tracking-tight text-foreground">
40-
{title}
41-
</h2>
42-
{description && (
43-
<p className="text-muted-foreground mt-1">{description}</p>
32+
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 px-2">
33+
<div className="flex items-center gap-3 min-w-0">
34+
{Icon && (
35+
<div className="w-10 h-10 rounded-sm bg-gradient-to-br from-secondary/20 to-secondary/10 flex items-center justify-center flex-shrink-0">
36+
<Icon className="h-5 w-5 text-secondary" />
37+
</div>
4438
)}
39+
<div className="min-w-0">
40+
<h2 className="text-2xl font-bold tracking-tight text-foreground">
41+
{title}
42+
</h2>
43+
{description && (
44+
<p className="text-muted-foreground mt-1">{description}</p>
45+
)}
46+
</div>
4547
</div>
48+
{/* Toolbar Section */}
49+
{toolbar && <div className="w-full lg:w-[400px]">{toolbar}</div>}
4650
</div>
47-
{/* Toolbar Section */}
48-
{toolbar && (
49-
<div className="flex flex-col space-y-4 sm:flex-row sm:items-center sm:justify-between sm:space-y-0">
50-
{toolbar}
51-
</div>
52-
)}
5351
</div>
5452

5553
{/* Content Section */}

src/components/proposals/DAOProposals.tsx

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
import { useMemo, useState, useEffect, useRef, useCallback } from "react";
44
import ProposalCard from "@/components/proposals/ProposalCard";
55
import type { Proposal } from "@/types";
6-
import { FileText, Loader2 } from "lucide-react";
6+
import { FileText, Loader2, Search, X } from "lucide-react";
77
import { DAOTabLayout } from "@/components/aidaos/DAOTabLayout";
88
import { enableSingleDaoMode, singleDaoName } from "@/config/features";
99
import { useBatchProposalVotes } from "@/hooks/useBatchProposalVotes";
1010
import { useQuery } from "@tanstack/react-query";
1111
import { fetchLatestChainState } from "@/services/chain-state.service";
12+
import { Input } from "@/components/ui/input";
13+
import { Button } from "@/components/ui/button";
1214

1315
const ITEMS_PER_BATCH = 5;
1416

@@ -25,9 +27,10 @@ const DAOProposals = ({
2527
}: DAOProposalsProps) => {
2628
const [visibleCount, setVisibleCount] = useState(ITEMS_PER_BATCH);
2729
const [isLoading, setIsLoading] = useState(false);
30+
const [searchQuery, setSearchQuery] = useState("");
2831
const loaderRef = useRef<HTMLDivElement>(null);
2932

30-
// Filter deployed proposals
33+
// Filter deployed proposals with search and status filter
3134
const deployedProposals = useMemo(() => {
3235
let filtered = proposals.filter(
3336
(proposal) => proposal.status === "DEPLOYED"
@@ -38,8 +41,24 @@ const DAOProposals = ({
3841
) {
3942
filtered = [];
4043
}
44+
45+
// Apply search filter (search by username in reference link)
46+
if (searchQuery.trim()) {
47+
const query = searchQuery.toLowerCase();
48+
filtered = filtered.filter((proposal) => {
49+
// Extract reference link from content
50+
const referenceMatch = proposal.content?.match(
51+
/Reference:\s*(https?:\/\/\S+)/i
52+
);
53+
const referenceLink = referenceMatch?.[1] || "";
54+
55+
// Search in the reference link for username
56+
return referenceLink.toLowerCase().includes(query);
57+
});
58+
}
59+
4160
return filtered;
42-
}, [proposals, daoName]);
61+
}, [proposals, daoName, searchQuery]);
4362

4463
// Fetch chain state once for all proposals
4564
const { data: chainState } = useQuery({
@@ -99,19 +118,72 @@ const DAOProposals = ({
99118
setVisibleCount(ITEMS_PER_BATCH);
100119
}, [proposals]);
101120

121+
// Reset visible count when search changes
122+
useEffect(() => {
123+
setVisibleCount(ITEMS_PER_BATCH);
124+
}, [searchQuery]);
125+
126+
const clearSearch = () => {
127+
setSearchQuery("");
128+
};
129+
130+
const hasActiveSearch = searchQuery.trim() !== "";
131+
102132
return (
103133
<DAOTabLayout
104134
title="Submission History"
105135
description="Explore all the work that has been submitted to AIBTC."
106-
isEmpty={deployedProposals.length === 0}
136+
isEmpty={deployedProposals.length === 0 && !hasActiveSearch}
107137
emptyTitle="No Contributions Found"
108138
emptyIcon={FileText}
139+
toolbar={
140+
<div className="relative w-full">
141+
<Search className="absolute left-3 sm:left-4 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none" />
142+
<Input
143+
placeholder="Search contributions by X username"
144+
value={searchQuery}
145+
onChange={(e) => setSearchQuery(e.target.value)}
146+
className="pl-9 border-none py-7"
147+
/>
148+
{hasActiveSearch && (
149+
<Button
150+
variant="ghost"
151+
size="icon"
152+
onClick={clearSearch}
153+
className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7"
154+
title="Clear search"
155+
>
156+
<X className="h-4 w-4" />
157+
</Button>
158+
)}
159+
</div>
160+
}
109161
>
110162
<div className="space-y-4">
163+
{/* Results count */}
164+
{hasActiveSearch && (
165+
<div className="text-sm text-muted-foreground px-4">
166+
Found {deployedProposals.length}{" "}
167+
{deployedProposals.length === 1 ? "contribution" : "contributions"}
168+
</div>
169+
)}
111170
{isLoadingData && visibleProposals.length === 0 ? (
112171
<div className="flex justify-center py-12">
113172
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
114173
</div>
174+
) : deployedProposals.length === 0 && hasActiveSearch ? (
175+
<div className="flex flex-col items-center justify-center py-12 text-center">
176+
<FileText className="h-12 w-12 text-muted-foreground mb-4" />
177+
<h3 className="text-lg font-semibold mb-2">
178+
No contributions found
179+
</h3>
180+
<p className="text-sm text-muted-foreground mb-4">
181+
Try adjusting your search
182+
</p>
183+
<Button variant="outline" onClick={clearSearch}>
184+
Clear search
185+
</Button>
186+
</div>
115187
) : (
116188
<div className="divide-y">
117189
{visibleProposals.map((proposal) => {

0 commit comments

Comments
 (0)