From 86103baa7fbab1a63caf867084fbb2d8cdc7b90f Mon Sep 17 00:00:00 2001 From: sirin Date: Wed, 9 Oct 2024 02:49:19 +0700 Subject: [PATCH] feat: filtering functionality on all deals page --- src/app/deals/page.tsx | 320 +++++++++++++++++++++++++---------- src/app/landing/page.tsx | 7 - src/lib/data/projectQuery.ts | 137 ++++++++++++++- 3 files changed, 365 insertions(+), 99 deletions(-) delete mode 100644 src/app/landing/page.tsx diff --git a/src/app/deals/page.tsx b/src/app/deals/page.tsx index c60cd9a..f83da6e 100644 --- a/src/app/deals/page.tsx +++ b/src/app/deals/page.tsx @@ -1,66 +1,166 @@ "use client"; + import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { useState } from "react"; -import { Clock3Icon, MessageSquareIcon, UserIcon, UsersIcon } from "lucide-react"; +import { Clock3Icon, UserIcon, UsersIcon } from "lucide-react"; import { Separator } from "@/components/ui/separator"; import { ProjectCard } from "@/components/projectCard"; +import { getAllTagsQuery, getALlFundedStatusQuery, getAllBusinessTypeQuery } from "@/lib/data/dropdownQuery"; +import { createSupabaseClient } from "@/lib/supabase/clientComponentClient"; +import { useQuery } from "@supabase-cache-helpers/postgrest-react-query"; +import { searchProjectsQuery, FilterParams, FilterProjectQueryParams } from "@/lib/data/projectQuery"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; + +const ProjectSection = ({ filteredProjects }) => { + interface Tags { + tag_name: string; + } + + if (!filteredProjects) { + return
No projects found!
; + } + + return ( +
+
+

Deals

+

The deals attracting the most interest right now

+
+ + {/* Block for all the deals */} +
+ {filteredProjects.map((item, index) => ( + tag.tag_name)} + /> + ))} +
+
+ ); +}; + +const ShowFilter = ({ filterParams, clearAll }: { filterParams: FilterParams; clearAll: () => void }) => { + const { searchTerm, tagsFilter, projectStatusFilter, businessTypeFilter, sortByTimeFilter } = filterParams; + + if (!searchTerm && !tagsFilter && !projectStatusFilter && !businessTypeFilter && !sortByTimeFilter) { + return
; + } + + if ( + projectStatusFilter === "all" && + businessTypeFilter === "all" && + sortByTimeFilter === "all" && + (!tagsFilter || tagsFilter.length === 0) + ) { + return
; + } + + return ( +
+ {searchTerm && ( + + )} + + {tagsFilter && + tagsFilter.map((tag: string) => ( + + ))} + + {projectStatusFilter && projectStatusFilter !== "all" && ( + + )} + + {businessTypeFilter && businessTypeFilter !== "all" && ( + + )} + + {sortByTimeFilter && sortByTimeFilter !== "all" && ( + + )} + + {/* Clear All button */} + +
+ ); +}; export default function Deals() { - const [postAtFilter, setPostAtFilter] = useState(""); - const [contentTypeFilter, setContentTypeFilter] = useState(""); - const [authorFilter, setAuthorFilter] = useState(""); - const [groupsFilter, setGroupFilter] = useState(""); - const [selectedTag, setSelectedTag] = useState(""); - const data = [ - { - name: "NVDA", - description: "Founded in 1993, NVIDIA is a key innovator of computer graphics and AI technology", - joinDate: "December 2021", - location: "Bangkok, Thailand", - tags: ["AI", "Technology"], - imageUri: "/login.png", - minInvestment: 10000, - totalInvestor: 58400, - totalRaised: 9000000, - }, - { - name: "Apple Inc.", - description: - "Founded in 1976, Apple Inc. is a leading innovator in consumer electronics, software, and online services, known for products like the iPhone, MacBook, and the App Store.", - joinDate: "February 2020", - location: "Cupertino, California, USA", - tags: ["Consumer Electronics", "Software"], - imageUri: "/money.png", - minInvestment: 10000, - totalInvestor: 58400, - totalRaised: 9000000, - }, - { - name: "Google LLC", - description: - "Founded in 1998, Google LLC specializes in internet-related services and products, including search engines, online advertising, cloud computing, and the Android operating system.", - joinDate: "April 2019", - location: "Mountain View, California, USA", - tags: ["Internet", "Search Engine"], - imageUri: "/money.png", - minInvestment: 10000, - totalInvestor: 5000, - totalRaised: 1500000000, - }, - { - name: "Microsoft Corporation", - description: "Microsoft Corporation is a multinational technology company.", - joinDate: "January 2018", - location: "California, USA", - tags: ["Technology", "Software"], - imageUri: null, - minInvestment: 250, - totalInvestor: 5000, - totalRaised: 1500000, - }, - ]; + const supabase = createSupabaseClient(); - const filteredData = selectedTag ? data.filter((item) => item.tags.includes(selectedTag)) : data; + const [searchTerm, setSearchTerm] = useState(""); + const [searchTermVisual, setSearchTermVisual] = useState(""); + const [sortByTimeFilter, setSortByTimeFilter] = useState("all"); + const [businessTypeFilter, setBusinessTypeFilter] = useState("all"); + const [tagFilter, setTagFilter] = useState([]); + const [projectStatusFilter, setprojectStatusFilter] = useState("all"); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(4); + + const filterParams: FilterParams = { + searchTerm, + tagsFilter: tagFilter, + projectStatusFilter, + businessTypeFilter, + sortByTimeFilter, + }; + + const filterProjectQueryParams: FilterProjectQueryParams = { + searchTerm, + tagsFilter: tagFilter, + projectStatusFilter, + businessTypeFilter, + sortByTimeFilter, + page, + pageSize, + }; + + const { data: tags, isLoading: isLoadingTags, error: tagsLoadingError } = useQuery(getAllTagsQuery(supabase)); + const { + data: projectStatus, + isLoading: isLoadingFunded, + error: fundedLoadingError, + } = useQuery(getALlFundedStatusQuery(supabase)); + const { + data: businessType, + isLoading: isLoadingBusinessType, + error: businessTypeLoadingError, + } = useQuery(getAllBusinessTypeQuery(supabase)); + + const { + data: projects, + isLoading: isLoadingProjects, + error: projectsLoadingError, + } = useQuery(searchProjectsQuery(supabase, filterProjectQueryParams)); + + const clearAll = () => { + setSearchTerm(""); + setTagFilter([]); + setprojectStatusFilter("all"); + setBusinessTypeFilter("all"); + setSortByTimeFilter("all"); + }; return (
@@ -72,58 +172,98 @@ export default function Deals() { All companies are vetted & pass due diligence.

- {/* Filters */} -
- setSearchTermVisual(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + setSearchTerm(e.currentTarget.value); + } + }} + /> + + {/* Posted At Filter */} + - setBusinessTypeFilter}> - - + + - All Tags - AI - Technology - Consumer Electronics - Software - Internet + {isLoadingBusinessType ? ( + + Loading... + + ) : businessTypeLoadingError ? ( + + No data available + + ) : ( + <> + All Types + {businessType && + businessType.map((type) => ( + + {type.value} + + ))} + + )} + + + + {/* Project Status Filter */} +
+ -
-
-

Deals

-

The deals attracting the most interest right now

-
- - {/* Block for all the deals */} -
- {filteredData.map((item, index) => ( - - ))} + {/* Project Cards Section */} +
); diff --git a/src/app/landing/page.tsx b/src/app/landing/page.tsx deleted file mode 100644 index 604a8de..0000000 --- a/src/app/landing/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -"use client"; - -import React from "react"; - -export default function Page() { - return
hello
; -} diff --git a/src/lib/data/projectQuery.ts b/src/lib/data/projectQuery.ts index aefe0e7..ce652b9 100644 --- a/src/lib/data/projectQuery.ts +++ b/src/lib/data/projectQuery.ts @@ -1,6 +1,5 @@ import { SupabaseClient } from "@supabase/supabase-js"; - async function getTopProjects(client: SupabaseClient, numberOfRecords: number = 4) { try { const { data, error } = await client @@ -45,5 +44,139 @@ async function getTopProjects(client: SupabaseClient, numberOfRecords: number = } } - export { getTopProjects }; +async function searchProjects(client: SupabaseClient, searchTerm: string | null, page: number = 1, pageSize: number = 4) { + const start = (page - 1) * pageSize; + const end = start + pageSize - 1; + + try { + let query = client.from("Project").select( + ` + id, + projectName, + businessId, + publishedTime, + projectShortDescription, + cardImage, + ProjectInvestmentDetail ( + minInvestment, + totalInvestment, + targetInvestment, + investmentDeadline + ), + ItemTag ( + Tag ( + id, + value + ) + ), + Business ( + location + ) + ` + ).order("publishedTime", { ascending: false }) + .range(start, end); + + if (searchTerm) { + query = query.ilike('projectName', `%${searchTerm}%`); + } + + const { data, error } = await query; + + if (error) { + console.error("Error searching projects:", error.message); + return { data: null, error: error.message }; + } + + return { data, error: null }; + } catch (err) { + console.error("Unexpected error:", err); + return { data: null, error: "An unexpected error occurred." }; + } +} + +export interface FilterParams { + searchTerm?: string; + tagsFilter?: string[]; + projectStatus?: string; + projectStatusFilter?: string; + businessTypeFilter?: string; + sortByTimeFilter?: string; +} + +export interface FilterProjectQueryParams extends FilterParams { + page: number, + pageSize: number +} + +function searchProjectsQuery(client: SupabaseClient, {searchTerm, tagsFilter, projectStatus, businessTypeFilter, sortByTimeFilter, page = 1, pageSize = 4}: FilterProjectQueryParams) { + const start = (page - 1) * pageSize; + const end = start + pageSize - 1; + + let query = client.from("Project").select( + ` + id, + project_name:projectName, + published_time:publishedTime, + project_short_description:projectShortDescription, + card_image_url:cardImage, + ...ProjectStatus!Project_projectStatusId_fkey!inner ( + project_status:value + ), + ...ProjectInvestmentDetail!inner ( + min_investment:minInvestment, + total_investment:totalInvestment, + target_investment:targetInvestment, + investment_deadline:investmentDeadline + ), + tags:ItemTag!inner ( + ...Tag!inner ( + tag_name:value + ) + ), + ...Business!inner ( + ...businessType!inner ( + business_type:value + ), + business_location:location + ) + ` + ).order("publishedTime", { ascending: false }).range(start, end) + + if (sortByTimeFilter === "all") { + sortByTimeFilter = undefined; + } + + if (projectStatus === "all") { + projectStatus = undefined; + } + + if (businessTypeFilter === "all") { + businessTypeFilter = undefined; + } + + if (tagsFilter?.length === 0) { + tagsFilter = undefined; + } + + if (searchTerm) { + query = query.ilike('projectName', `%${searchTerm}%`) + } + + if (tagsFilter) { + query = query.in('ItemTag.Tag.value', tagsFilter) + } + + if (projectStatus) { + query = query.eq("ProjectStatus.value", projectStatus) + } + + if (businessTypeFilter) { + query = query.eq("Business.businessType.value", businessTypeFilter) + } + + return query; +} + + +export { getTopProjects, searchProjects, searchProjectsQuery };