mirror of
https://github.com/Sosokker/B2D-Ventures.git
synced 2025-12-19 22:14:06 +01:00
refactor - fix: find page also search by project/business name
This commit is contained in:
parent
6269406772
commit
a0e25d0268
6
package-lock.json
generated
6
package-lock.json
generated
@ -11230,9 +11230,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/supabase": {
|
||||
"version": "1.207.9",
|
||||
"resolved": "https://registry.npmjs.org/supabase/-/supabase-1.207.9.tgz",
|
||||
"integrity": "sha512-BJPwsAd2UBIpQawcQV3/xKHEZ8YrrkHYpgibxCZbG+RuxuhTtkHG7zR4I3LylIIEwcKp3hmDKu/hO1m2NT5RXA==",
|
||||
"version": "1.215.0",
|
||||
"resolved": "https://registry.npmjs.org/supabase/-/supabase-1.215.0.tgz",
|
||||
"integrity": "sha512-ITHqEnDl3F/b44AYdBUSzinqZhmGxrx205Yt2HfyaVTd5W/JppF3/naqVPIdRiUZBBLKkvfQO/wUk48W8SoVxw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
|
||||
55
src/app/find/BusinessSection.tsx
Normal file
55
src/app/find/BusinessSection.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { BusinessCard } from "@/components/BusinessCard";
|
||||
import { BusinessCardProps } from "@/types/BusinessCard";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import Link from "next/link";
|
||||
|
||||
interface BusinessSectionProps extends BusinessCardProps {
|
||||
user_id: string;
|
||||
}
|
||||
|
||||
export function BusinessSection({ businessData }: { businessData: BusinessSectionProps[] }) {
|
||||
if (!businessData || businessData.length === 0) {
|
||||
return (
|
||||
<Card className="text-center">
|
||||
<CardHeader>
|
||||
<CardTitle>No Business Found</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p>Sorry, we could not find any businesses matching your search criteria.</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div id="project-card">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Businesses</CardTitle>
|
||||
<CardDescription>Found {businessData.length} projects!</CardDescription>
|
||||
</CardHeader>
|
||||
<Separator className="my-3" />
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{businessData.map((business) => (
|
||||
<div key={business.business_id}>
|
||||
<Link href={`/profile/${business.user_id}`}>
|
||||
<BusinessCard
|
||||
business_id={business.business_id}
|
||||
business_name={business.business_name}
|
||||
joined_date={business.joined_date}
|
||||
location={business.location}
|
||||
business_type={business.business_type}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
57
src/app/find/ProjectSection.tsx
Normal file
57
src/app/find/ProjectSection.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import React from "react";
|
||||
import { ProjectCard } from "@/components/projectCard";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { ProjectCardProps } from "@/types/ProjectCard";
|
||||
import Link from "next/link";
|
||||
|
||||
export function ProjectSection({ projectsData }: { projectsData: ProjectCardProps[] | null }) {
|
||||
if (!projectsData || projectsData.length === 0) {
|
||||
return (
|
||||
<Card className="text-center">
|
||||
<CardHeader>
|
||||
<CardTitle>No Project Found</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p>Sorry, we could not find any projects matching your search criteria.</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div id="project-card">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Projects</CardTitle>
|
||||
<CardDescription>Found {projectsData.length} projects!</CardDescription>
|
||||
</CardHeader>
|
||||
<Separator className="my-3" />
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{projectsData.map((project) => (
|
||||
<div key={project.id}>
|
||||
<Link href={`/deals/${project.id}`}>
|
||||
<ProjectCard
|
||||
name={project.project_name}
|
||||
description={project.short_description}
|
||||
imageUri={project.image_url}
|
||||
joinDate={new Date(project.join_date).toLocaleDateString()}
|
||||
location={project.location}
|
||||
tags={project.tags}
|
||||
minInvestment={project.min_investment}
|
||||
totalInvestor={project.total_investor}
|
||||
totalRaised={project.total_raise}
|
||||
/>
|
||||
</Link>
|
||||
<Separator />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,89 +1,43 @@
|
||||
"use client";
|
||||
|
||||
import React, { Suspense } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-react-query";
|
||||
import { ProjectCard } from "@/components/projectCard";
|
||||
import { getBusinessByName } from "@/lib/data/businessQuery";
|
||||
import { createSupabaseClient } from "@/lib/supabase/serverComponentClient";
|
||||
import { BusinessSection } from "./BusinessSection";
|
||||
import { ProjectSection } from "./ProjectSection";
|
||||
import { getProjectCardData } from "@/lib/data/projectQuery";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { getBusinessAndProject } from "@/lib/data/businessQuery";
|
||||
|
||||
function FindContent() {
|
||||
const searchParams = useSearchParams();
|
||||
const query = searchParams.get("query");
|
||||
export default async function FindContent({ searchParams }: { searchParams: { query: string } }) {
|
||||
const query = searchParams.query;
|
||||
|
||||
let supabase = createSupabaseClient();
|
||||
const supabase = createSupabaseClient();
|
||||
|
||||
const {
|
||||
data: businesses,
|
||||
isLoading: isLoadingBusinesses,
|
||||
error: businessError,
|
||||
} = useQuery(getBusinessAndProject(supabase, { businessName: query }));
|
||||
const { data: projectIds, error: projectIdsError } = await supabase
|
||||
.from("project")
|
||||
.select(`id`)
|
||||
.ilike("project_name", `%${query}%`);
|
||||
|
||||
const isLoading = isLoadingBusinesses;
|
||||
const error = businessError;
|
||||
const { data: businessData, error: businessDataError } = await getBusinessByName(supabase, { businessName: query });
|
||||
|
||||
if (isLoading) return <p>Loading...</p>;
|
||||
if (error) return <p>Error fetching data: {error.message}</p>;
|
||||
if (businessDataError || projectIdsError) {
|
||||
throw new Error(businessDataError?.message || projectIdsError?.message || "Unknown error");
|
||||
}
|
||||
|
||||
const projectIdList: string[] = projectIds.map((item) => item.id);
|
||||
const { data: projectsData, error: projectsDataError } = await getProjectCardData(supabase, projectIdList);
|
||||
|
||||
if (projectsDataError) {
|
||||
throw new Error(projectsDataError || "Unknown error");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container max-w-screen-xl">
|
||||
<div className="mt-4">
|
||||
<h1 className="text-4xl font-bold">Result</h1>
|
||||
|
||||
<Separator className="my-4" />
|
||||
|
||||
<Card className="w-full">
|
||||
<CardContent className="my-2">
|
||||
{businesses!.length === 0 && <p>No results found.</p>}
|
||||
{businesses!.length > 0 && (
|
||||
<ul>
|
||||
{businesses!.map((business) => (
|
||||
<li key={business.business_id}>
|
||||
<Card className="w-full">
|
||||
<CardHeader>
|
||||
<CardTitle>{business.business_name}</CardTitle>
|
||||
<CardDescription>
|
||||
Joined Date: {new Date(business.joined_date).toLocaleDateString()}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="grid grid-cols-4 gap-4">
|
||||
{business?.projects && business.projects.length > 0 ? (
|
||||
business.projects.map((project) => (
|
||||
<ProjectCard
|
||||
key={project.id}
|
||||
name={project.project_name}
|
||||
description={project.project_short_description}
|
||||
joinDate={project.published_time}
|
||||
location={business.location}
|
||||
minInvestment={project.min_investment}
|
||||
totalInvestor={project.total_investment}
|
||||
totalRaised={project.target_investment}
|
||||
tags={project.tags?.map((tag) => String(tag.tag_value)) || []}
|
||||
imageUri={project.card_image_url}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<p>No Projects</p>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="container max-w-screen-xl my-5 space-y-5">
|
||||
<Suspense fallback={<div>Loading Business and Projects...</div>}>
|
||||
<BusinessSection businessData={businessData} />
|
||||
</Suspense>
|
||||
<Separator className="my-3" />
|
||||
<Suspense fallback={<div>Loading Projects...</div>}>
|
||||
<ProjectSection projectsData={projectsData} />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Find() {
|
||||
return (
|
||||
<Suspense fallback={<p>Loading search parameters...</p>}>
|
||||
<FindContent />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,53 +1,34 @@
|
||||
import Image from "next/image";
|
||||
import {
|
||||
Card,
|
||||
CardFooter,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Card, CardFooter, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { CalendarDaysIcon } from "lucide-react";
|
||||
|
||||
interface XMap {
|
||||
// tagName: colorCode
|
||||
[tag: string]: string;
|
||||
}
|
||||
|
||||
interface BusinessCardProps {
|
||||
name: string;
|
||||
description: string;
|
||||
joinDate: string;
|
||||
location: string;
|
||||
tags: XMap | null;
|
||||
}
|
||||
import { BusinessCardProps } from "@/types/BusinessCard";
|
||||
import Image from "next/image";
|
||||
|
||||
export function BusinessCard(props: BusinessCardProps) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="h-[200px] hover:h-[100px] duration-75 pb-2">
|
||||
<Card className="rounded-xl border border-gray-200 dark:border-gray-700 hover:shadow-xl transition-shadow h-full">
|
||||
<CardHeader className="flex flex-row items-center gap-4 pb-4 border-b border-gray-100 dark:border-gray-800">
|
||||
<div className="relative w-14 h-14 flex-shrink-0">
|
||||
<Image
|
||||
src={"/money.png"}
|
||||
width={0}
|
||||
height={0}
|
||||
sizes="100vw"
|
||||
style={{ width: "100%", height: "100%" }}
|
||||
alt="nvidia"
|
||||
src="/money.png"
|
||||
alt="Business logo"
|
||||
fill
|
||||
className="rounded-full object-cover bg-white dark:bg-sky-900"
|
||||
/>
|
||||
</div>
|
||||
<CardTitle>{props.name}</CardTitle>
|
||||
<CardDescription>
|
||||
{props.description}
|
||||
<span className="flex items-center pt-2 gap-1">
|
||||
<CalendarDaysIcon width={20} />
|
||||
Joined {props.joinDate}
|
||||
</span>
|
||||
</CardDescription>
|
||||
<div>
|
||||
<CardTitle className="text-lg font-semibold text-gray-900 dark:text-white">{props.business_name}</CardTitle>
|
||||
<CardDescription className="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
<span className="flex items-center gap-1">
|
||||
<CalendarDaysIcon width={16} />
|
||||
Joined {props.joined_date}
|
||||
</span>
|
||||
</CardDescription>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardFooter className="flex-col items-start">
|
||||
{props.location}
|
||||
<span className="text-xs rounded-md bg-slate-200 dark:bg-slate-700 p-1">
|
||||
Technology
|
||||
<CardFooter className="flex flex-col gap-2 pt-4">
|
||||
<span className="text-sm text-gray-700 dark:text-gray-300">{props.location}</span>
|
||||
<span className="text-xs font-medium rounded-md bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 px-2 py-1">
|
||||
{props.business_type}
|
||||
</span>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
@ -22,6 +22,18 @@ interface ProjectCardProps {
|
||||
}
|
||||
|
||||
export function ProjectCard(props: ProjectCardProps) {
|
||||
if (props.minInvestment === null) {
|
||||
props.minInvestment = 0;
|
||||
}
|
||||
|
||||
if (props.totalInvestor === null) {
|
||||
props.minInvestment = 0;
|
||||
}
|
||||
|
||||
if (props.totalRaised === null) {
|
||||
props.minInvestment = 0;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@ -18,6 +18,33 @@ export const getAllBusinesses = (client: SupabaseClient) => {
|
||||
`);
|
||||
};
|
||||
|
||||
export const getBusinessByName = (
|
||||
client: SupabaseClient,
|
||||
params: { businessName?: string | null; single?: boolean } = { single: false }
|
||||
) => {
|
||||
const query = client.from("business").select(`
|
||||
business_id:id,
|
||||
location,
|
||||
business_name,
|
||||
...business_type (
|
||||
business_type_id: id,
|
||||
business_type: value
|
||||
),
|
||||
joined_date,
|
||||
user_id
|
||||
`);
|
||||
|
||||
if (params.businessName && params.businessName.trim() !== "") {
|
||||
query.ilike("business_name", `%${params.businessName}%`);
|
||||
}
|
||||
|
||||
if (params.single) {
|
||||
query.single();
|
||||
}
|
||||
|
||||
return query;
|
||||
};
|
||||
|
||||
export const getBusinessAndProject = (
|
||||
client: SupabaseClient,
|
||||
params: { businessName?: String | null; businessId?: number | null; single?: boolean } = { single: false }
|
||||
@ -53,7 +80,7 @@ export const getBusinessAndProject = (
|
||||
`);
|
||||
|
||||
if (params.businessName && params.businessName.trim() !== "") {
|
||||
return query.ilike("business_name", `%${params.businessName}%`);
|
||||
return query.or(`business_name.ilike.%${params.businessName}%,project_name.ilike.%${params.businessName}%`);
|
||||
}
|
||||
|
||||
if (params.businessId) {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
import { ProjectCardProps } from "@/types/ProjectCard";
|
||||
|
||||
async function getTopProjects(client: SupabaseClient, numberOfRecords: number = 4) {
|
||||
try {
|
||||
@ -241,6 +242,62 @@ const getProjectByUserId = (client: SupabaseClient, userId: string) => {
|
||||
.eq("business.user_id", userId);
|
||||
};
|
||||
|
||||
function getProjectByName(client: SupabaseClient, searchTerm: string) {
|
||||
return client
|
||||
.from("project")
|
||||
.select(
|
||||
`
|
||||
id,
|
||||
project_name,
|
||||
business_id,
|
||||
published_time,
|
||||
project_short_description,
|
||||
card_image_url,
|
||||
project_investment_detail (
|
||||
min_investment,
|
||||
total_investment,
|
||||
target_investment,
|
||||
investment_deadline
|
||||
),
|
||||
project_tag (
|
||||
tag (
|
||||
id,
|
||||
value
|
||||
)
|
||||
),
|
||||
business (
|
||||
location
|
||||
)
|
||||
`
|
||||
)
|
||||
.ilike("project_name", `%${searchTerm}%`);
|
||||
}
|
||||
|
||||
const getProjectCardData = async (client: SupabaseClient, projectIds: string[]) => {
|
||||
const { data, error } = await client.from("project_card").select("*").in("project_id", projectIds);
|
||||
|
||||
if (error) {
|
||||
return { data: null, error: error.message };
|
||||
}
|
||||
|
||||
const projectSections = data.map((project) => {
|
||||
const projectSection: ProjectCardProps = {
|
||||
id: project.project_id,
|
||||
project_name: project.project_name,
|
||||
short_description: project.short_description,
|
||||
image_url: project.image_url,
|
||||
join_date: project.join_date,
|
||||
location: project.location,
|
||||
tags: project.tags || [],
|
||||
min_investment: project.min_investment,
|
||||
total_investor: project.total_investor,
|
||||
total_raise: project.total_raise,
|
||||
};
|
||||
return projectSection;
|
||||
});
|
||||
|
||||
return { data: projectSections, error: null };
|
||||
};
|
||||
export {
|
||||
getProjectData,
|
||||
getProjectDataQuery,
|
||||
@ -248,4 +305,6 @@ export {
|
||||
searchProjectsQuery,
|
||||
getProjectByBusinessId,
|
||||
getProjectByUserId,
|
||||
getProjectByName,
|
||||
getProjectCardData,
|
||||
};
|
||||
|
||||
7
src/types/BusinessCard.ts
Normal file
7
src/types/BusinessCard.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export interface BusinessCardProps {
|
||||
business_id: number;
|
||||
business_name: string;
|
||||
joined_date: string;
|
||||
location: string;
|
||||
business_type: string;
|
||||
}
|
||||
12
src/types/ProjectCard.ts
Normal file
12
src/types/ProjectCard.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export interface ProjectCardProps {
|
||||
id?: number;
|
||||
project_name: string;
|
||||
short_description: string;
|
||||
image_url: string;
|
||||
join_date: string;
|
||||
location: string;
|
||||
tags: string[] | null;
|
||||
min_investment: number;
|
||||
total_investor: number;
|
||||
total_raise: number;
|
||||
}
|
||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user