fix: search and filter in deals page

This commit is contained in:
Sosokker 2024-11-07 01:53:55 +07:00
parent 01eb6c40cc
commit 214a625b3c
4 changed files with 163 additions and 186 deletions

View File

@ -0,0 +1,53 @@
import { Button } from "@/components/ui/button";
import { FilterParams } from "@/lib/data/projectQuery";
export const ShowFilter = ({ filterParams, clearAll }: { filterParams: FilterParams; clearAll: () => void }) => {
const { searchTerm, tagFilter, projectStatusFilter, businessTypeFilter } = filterParams;
if (!searchTerm && !tagFilter && !projectStatusFilter && !businessTypeFilter) {
return <div></div>;
}
if (projectStatusFilter === "all" && businessTypeFilter === "all" && tagFilter === "all") {
return <div></div>;
}
return (
<div className="flex flex-wrap gap-2">
{searchTerm && (
<Button key={searchTerm} variant="secondary">
{searchTerm}
</Button>
)}
{tagFilter && tagFilter !== "all" && (
<Button key={tagFilter} variant="secondary">
{tagFilter}
</Button>
)}
{projectStatusFilter && projectStatusFilter !== "all" && (
<Button key={projectStatusFilter} variant="secondary">
{projectStatusFilter}
</Button>
)}
{businessTypeFilter && businessTypeFilter !== "all" && (
<Button key={businessTypeFilter} variant="secondary">
{businessTypeFilter}
</Button>
)}
{/* {sortByTimeFilter && sortByTimeFilter !== "all" && (
<Button key={sortByTimeFilter} variant="secondary">
{sortByTimeFilter}
</Button>
)} */}
{/* Clear All button */}
<Button variant="destructive" onClick={clearAll}>
Clear All
</Button>
</div>
);
};

View File

@ -4,186 +4,84 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Clock3Icon, UserIcon, UsersIcon } from "lucide-react"; import { Clock3Icon, UserIcon, UsersIcon } from "lucide-react";
import { Separator } from "@/components/ui/separator"; import { Separator } from "@/components/ui/separator";
import { ProjectCard } from "@/components/projectCard"; import { getAllBusinessTypeQuery, getAllTagsQuery, getAllProjectStatusQuery } from "@/lib/data/dropdownQuery";
import { getALlFundedStatusQuery, getAllBusinessTypeQuery } from "@/lib/data/dropdownQuery";
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient"; import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
import { useQuery } from "@supabase-cache-helpers/postgrest-react-query"; import { useQuery } from "@supabase-cache-helpers/postgrest-react-query";
import { searchProjectsQuery, FilterParams, FilterProjectQueryParams } from "@/lib/data/projectQuery"; import { searchProjectsQuery, FilterParams, FilterProjectQueryParams } from "@/lib/data/projectQuery";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { ProjectSection } from "@/components/ProjectSection";
import { ShowFilter } from "./ShowFilter";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import Link from "next/link";
interface Project {
project_id: string;
project_name: string;
published_time: string;
project_short_description: string;
card_image_url: string;
project_status: {
value: string;
};
min_investment: number;
total_investment: number;
target_investment: number;
investment_deadline: string;
tags: {
tag_name: string;
}[];
business_type: {
value: string;
};
business_location: string;
}
const ProjectSection = ({ filteredProjects }: { filteredProjects: Project[] }) => {
interface Tags {
tag_name: string;
}
if (!filteredProjects) {
return <div>No projects found!</div>;
}
return (
<div>
<div className="mt-10">
<h2 className="text-2xl">Deals</h2>
<p className="mt-3">The deals attracting the most interest right now</p>
</div>
{/* Block for all the deals */}
<div className="mt-10 grid grid-cols-3 gap-4">
{filteredProjects.map((item, index) => (
<Link key={index} href={`/deals/${item.project_id}`}>
<ProjectCard
key={index}
name={item.project_name}
description={item.project_short_description}
joinDate={item.published_time}
imageUri={item.card_image_url}
location={item.business_location}
minInvestment={item.min_investment}
totalInvestor={item.total_investment}
totalRaised={item.target_investment}
tags={item.tags.map((tag: Tags) => tag.tag_name)}
/>
</Link>
))}
</div>
</div>
);
};
const ShowFilter = ({ filterParams, clearAll }: { filterParams: FilterParams; clearAll: () => void }) => {
const { searchTerm, tagsFilter, projectStatusFilter, businessTypeFilter, sortByTimeFilter } = filterParams;
if (!searchTerm && !tagsFilter && !projectStatusFilter && !businessTypeFilter && !sortByTimeFilter) {
return <div></div>;
}
if (
projectStatusFilter === "all" &&
businessTypeFilter === "all" &&
sortByTimeFilter === "all" &&
(!tagsFilter || tagsFilter.length === 0)
) {
return <div></div>;
}
return (
<div className="flex flex-wrap gap-2">
{searchTerm && (
<Button key={searchTerm} variant="secondary">
{searchTerm}
</Button>
)}
{tagsFilter &&
tagsFilter.map((tag: string) => (
<Button key={tag} variant="secondary">
{tag}
</Button>
))}
{projectStatusFilter && projectStatusFilter !== "all" && (
<Button key={projectStatusFilter} variant="secondary">
{projectStatusFilter}
</Button>
)}
{businessTypeFilter && businessTypeFilter !== "all" && (
<Button key={businessTypeFilter} variant="secondary">
{businessTypeFilter}
</Button>
)}
{sortByTimeFilter && sortByTimeFilter !== "all" && (
<Button key={sortByTimeFilter} variant="secondary">
{sortByTimeFilter}
</Button>
)}
{/* Clear All button */}
<Button variant="destructive" onClick={clearAll}>
Clear All
</Button>
</div>
);
};
export default function Deals() { export default function Deals() {
const supabase = createSupabaseClient(); const supabase = createSupabaseClient();
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [searchTermVisual, setSearchTermVisual] = useState(""); const [searchTermVisual, setSearchTermVisual] = useState(""); // For the input field
const [sortByTimeFilter, setSortByTimeFilter] = useState("all"); // const [sortByTimeFilter, setSortByTimeFilter] = useState("all");
const [businessTypeFilter, setBusinessTypeFilter] = useState("all"); const [businessTypeFilter, setBusinessTypeFilter] = useState("all");
const [tagFilter, setTagFilter] = useState([]); const [tagFilter, setTagFilter] = useState("all");
const [projectStatusFilter, setProjectStatusFilter] = useState("all"); const [projectStatusFilter, setProjectStatusFilter] = useState("all");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(4); const [pageSize, setPageSize] = useState(4);
const filterParams: FilterParams = { const filterParams: FilterParams = {
searchTerm, searchTerm,
tagsFilter: tagFilter, tagFilter,
projectStatusFilter, projectStatusFilter,
businessTypeFilter, businessTypeFilter,
sortByTimeFilter, // sortByTimeFilter,
}; };
const filterProjectQueryParams: FilterProjectQueryParams = { let filterProjectQueryParams: FilterProjectQueryParams = {
searchTerm: "", searchTerm,
tagsFilter: [], tagFilter,
projectStatusFilter: "all", projectStatusFilter,
businessTypeFilter: "all", businessTypeFilter,
sortByTimeFilter: "all", // sortByTimeFilter,
page, page,
pageSize, pageSize,
}; };
const { data: tagData, isLoading: isLoadingTag, error: isTagError } = useQuery(getAllTagsQuery(supabase));
const { const {
data: projectStatus, data: projectStatusData,
isLoading: isLoadingFunded, isLoading: isLoadingprojectStatus,
error: fundedLoadingError, error: isprojectStatusError,
} = useQuery(getALlFundedStatusQuery(supabase)); } = useQuery(getAllProjectStatusQuery(supabase));
const { const {
data: businessType, data: businessType,
isLoading: isLoadingBusinessType, isLoading: isLoadingBusinessType,
error: businessTypeLoadingError, error: businessTypeLoadingError,
} = useQuery(getAllBusinessTypeQuery(supabase)); } = useQuery(getAllBusinessTypeQuery(supabase));
const { const {
data: projects, data: projects,
isLoading: isLoadingProjects, isLoading: isLoadingProjects,
error: projectsLoadingError, error: projectsLoadingError,
refetch,
} = useQuery(searchProjectsQuery(supabase, filterProjectQueryParams)); } = useQuery(searchProjectsQuery(supabase, filterProjectQueryParams));
const formattedProjects =
projects?.map((project) => ({
id: project.project_id,
project_name: project.project_name,
short_description: project.project_short_description,
image_url: project.card_image_url,
join_date: new Date(project.published_time).toLocaleDateString(),
location: project.business_location,
tags: project.tags.map((tag) => tag.tag_name),
min_investment: project.min_investment || 0,
total_investor: project.total_investment || 0,
total_raise: project.total_investment || 0,
})) || [];
const clearAll = () => { const clearAll = () => {
setSearchTerm(""); setSearchTerm("");
setTagFilter([]); setSearchTermVisual("");
setTagFilter("all");
setProjectStatusFilter("all"); setProjectStatusFilter("all");
setBusinessTypeFilter("all"); setBusinessTypeFilter("all");
setSortByTimeFilter("all"); // setSortByTimeFilter("all");
}; };
const handlePageSizeChange = (value: number) => { const handlePageSizeChange = (value: number) => {
@ -191,9 +89,10 @@ export default function Deals() {
setPage(1); setPage(1);
}; };
useEffect(() => { const handleSearchClick = () => {
setSearchTerm(searchTermVisual); setSearchTerm(searchTermVisual);
}, [searchTermVisual]); refetch();
};
return ( return (
<div className="container max-w-screen-xl mx-auto px-4"> <div className="container max-w-screen-xl mx-auto px-4">
@ -214,13 +113,17 @@ export default function Deals() {
onChange={(e) => setSearchTermVisual(e.target.value)} onChange={(e) => setSearchTermVisual(e.target.value)}
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === "Enter") { if (e.key === "Enter") {
setSearchTerm(e.currentTarget.value); handleSearchClick();
} }
}} }}
/> />
<Button variant="outline" onClick={handleSearchClick}>
Search
</Button>
{/* Posted At Filter */} {/* Posted At Filter */}
<Select onValueChange={(value) => setSortByTimeFilter(value)}> {/* <Select value={sortByTimeFilter} onValueChange={(value) => setSortByTimeFilter(value)}>
<SelectTrigger className="w-full sm:w-[180px]"> <SelectTrigger className="w-full sm:w-[180px]">
<Clock3Icon className="ml-2" /> <Clock3Icon className="ml-2" />
<SelectValue placeholder="Posted at" /> <SelectValue placeholder="Posted at" />
@ -231,10 +134,10 @@ export default function Deals() {
<SelectItem value="This Week">This Week</SelectItem> <SelectItem value="This Week">This Week</SelectItem>
<SelectItem value="This Month">This Month</SelectItem> <SelectItem value="This Month">This Month</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select> */}
{/* Business Type Filter */} {/* Business Type Filter */}
<Select onValueChange={(value) => setBusinessTypeFilter(value)}> <Select value={businessTypeFilter} onValueChange={(value) => setBusinessTypeFilter(value)}>
<SelectTrigger className="w-full sm:w-[180px]"> <SelectTrigger className="w-full sm:w-[180px]">
<UsersIcon className="ml-2" /> <UsersIcon className="ml-2" />
<SelectValue placeholder="Business Type" /> <SelectValue placeholder="Business Type" />
@ -263,25 +166,25 @@ export default function Deals() {
</Select> </Select>
{/* Project Status Filter */} {/* Project Status Filter */}
<Select onValueChange={(key) => setProjectStatusFilter(key)}> <Select value={projectStatusFilter} onValueChange={(value) => setProjectStatusFilter(value)}>
<SelectTrigger className="w-full sm:w-[180px]"> <SelectTrigger className="w-full sm:w-[180px]">
<UserIcon className="ml-2" /> <UserIcon className="ml-2" />
<SelectValue placeholder="Project Status" /> <SelectValue placeholder="Project Status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{isLoadingFunded ? ( {isLoadingprojectStatus ? (
<SelectItem disabled value="_"> <SelectItem disabled value="_">
Loading... Loading...
</SelectItem> </SelectItem>
) : fundedLoadingError ? ( ) : isprojectStatusError ? (
<SelectItem disabled value="_"> <SelectItem disabled value="_">
No data available No data available
</SelectItem> </SelectItem>
) : ( ) : (
<> <>
<SelectItem value="all">All Statuses</SelectItem> <SelectItem value="all">All Statuses</SelectItem>
{projectStatus && {projectStatusData &&
projectStatus.map((status) => ( projectStatusData.map((status) => (
<SelectItem key={status.id} value={status.value}> <SelectItem key={status.id} value={status.value}>
{status.value} {status.value}
</SelectItem> </SelectItem>
@ -290,21 +193,51 @@ export default function Deals() {
)} )}
</SelectContent> </SelectContent>
</Select> </Select>
{/* Tags Filter */}
<Select value={tagFilter} onValueChange={(value) => setTagFilter(value)}>
<SelectTrigger className="w-full sm:w-[180px]">
<UserIcon className="ml-2" />
<SelectValue placeholder="Tags" />
</SelectTrigger>
<SelectContent>
{isLoadingTag ? (
<SelectItem disabled value="_">
Loading...
</SelectItem>
) : isTagError ? (
<SelectItem disabled value="_">
No data available
</SelectItem>
) : (
<>
<SelectItem value="all">All Tags</SelectItem>
{tagData &&
tagData.map((tag) => (
<SelectItem key={tag.id} value={tag.value}>
{tag.value}
</SelectItem>
))}
</>
)}
</SelectContent>
</Select>
</div> </div>
{/* Active Filters */} {/* Active Filters */}
<Separator className="mt-4 bg-background" />
<ShowFilter filterParams={filterParams} clearAll={clearAll} /> <ShowFilter filterParams={filterParams} clearAll={clearAll} />
<Separator className="mt-10" /> <Separator className="my-3" />
{/* Project Cards Section */} {/* Project Cards Section */}
{isLoadingProjects ? ( {isLoadingProjects ? (
<div>Loading...</div> <div>Loading...</div>
) : projectsLoadingError ? ( ) : projectsLoadingError ? (
<div>Error loading projects.</div> <div>Error loading projects</div>
) : projects ? (
<ProjectSection filteredProjects={projects} />
) : ( ) : (
<div>No projects found!</div> <div>
<ProjectSection projectsData={formattedProjects} />
</div>
)} )}
{/* Pagination Controls */} {/* Pagination Controls */}
<div className="mt-6 flex items-center justify-between"> <div className="mt-6 flex items-center justify-between">

View File

@ -12,4 +12,8 @@ function getAllBusinessTypeQuery(client: SupabaseClient) {
return client.from("business_type").select("id, value, description"); return client.from("business_type").select("id, value, description");
} }
export { getAllBusinessTypeQuery, getALlFundedStatusQuery, getAllTagsQuery }; function getAllProjectStatusQuery(client: SupabaseClient) {
return client.from("project_status").select("id, value, description");
}
export { getAllBusinessTypeQuery, getALlFundedStatusQuery, getAllTagsQuery, getAllProjectStatusQuery };

View File

@ -96,7 +96,7 @@ async function getProjectData(client: SupabaseClient, projectId: number) {
export interface FilterParams { export interface FilterParams {
searchTerm?: string; searchTerm?: string;
tagsFilter?: string[]; tagFilter?: string;
projectStatus?: string; projectStatus?: string;
projectStatusFilter?: string; projectStatusFilter?: string;
businessTypeFilter?: string; businessTypeFilter?: string;
@ -112,8 +112,8 @@ function searchProjectsQuery(
client: SupabaseClient, client: SupabaseClient,
{ {
searchTerm, searchTerm,
tagsFilter, tagFilter,
projectStatus, projectStatusFilter,
businessTypeFilter, businessTypeFilter,
sortByTimeFilter, sortByTimeFilter,
page = 1, page = 1,
@ -121,7 +121,7 @@ function searchProjectsQuery(
}: FilterProjectQueryParams }: FilterProjectQueryParams
) { ) {
const start = (page - 1) * pageSize; const start = (page - 1) * pageSize;
const end = start + pageSize; const end = start + pageSize - 1;
let query = client let query = client
.from("project") .from("project")
@ -132,8 +132,8 @@ function searchProjectsQuery(
published_time, published_time,
project_short_description, project_short_description,
card_image_url, card_image_url,
...project_status!inner ( project_status!inner (
project_status:value value
), ),
...project_investment_detail!inner ( ...project_investment_detail!inner (
min_investment, min_investment,
@ -154,42 +154,29 @@ function searchProjectsQuery(
) )
` `
) )
.order("published_time", { ascending: false })
.range(start, end); .range(start, end);
if (sortByTimeFilter === "all") { // if (sortByTimeFilter && sortByTimeFilter !== "all") {
sortByTimeFilter = undefined; // query = query.eq("published_time", sortByTimeFilter);
// }
if (projectStatusFilter && projectStatusFilter !== "all") {
query = query.eq("project_status.value", projectStatusFilter);
} }
if (projectStatus === "all") { if (businessTypeFilter && businessTypeFilter !== "all") {
projectStatus = undefined; query = query.eq("business.business_type.value", businessTypeFilter);
} }
if (businessTypeFilter === "all") { if (tagFilter && tagFilter !== "all") {
businessTypeFilter = undefined; query = query.in("tags.tag_name", [tagFilter]);
}
if (tagsFilter?.length === 0) {
tagsFilter = undefined;
} }
if (searchTerm) { if (searchTerm) {
query = query.ilike("project_name", `%${searchTerm}%`); query = query.ilike("project_name", `%${searchTerm}%`);
} }
if (tagsFilter) { return query.order("published_time", { ascending: false });
query = query.in("project_tag.tag.value", tagsFilter);
}
if (projectStatus) {
query = query.eq("project_status.value", projectStatus);
}
if (businessTypeFilter) {
query = query.eq("business.business_type.value", businessTypeFilter);
}
return query;
} }
const getProjectByBusinessId = (client: SupabaseClient, businessIds: string[]) => { const getProjectByBusinessId = (client: SupabaseClient, businessIds: string[]) => {