mirror of
https://github.com/Sosokker/B2D-Ventures.git
synced 2025-12-18 21:44:06 +01:00
Use React-Query to query business data
This commit is contained in:
parent
30e4c9fe10
commit
ebd38f5da1
767
package-lock.json
generated
767
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -19,8 +19,11 @@
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"@supabase-cache-helpers/postgrest-react-query": "^1.10.1",
|
||||
"@supabase/ssr": "^0.4.1",
|
||||
"@supabase/supabase-js": "^2.45.2",
|
||||
"@tanstack/react-query": "^5.59.0",
|
||||
"@tanstack/react-query-devtools": "^5.59.0",
|
||||
"b2d-ventures": "file:",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
|
||||
@ -1,150 +1,145 @@
|
||||
"use client";
|
||||
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || '';
|
||||
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || '';
|
||||
if (!supabaseUrl || !supabaseKey) {
|
||||
throw new Error('Supabase URL and Key must be provided');
|
||||
}
|
||||
const supabase = createClient(supabaseUrl, supabaseKey);
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { createClient, SupabaseClient } from "@supabase/supabase-js";
|
||||
import { useEffect, useState } from "react";
|
||||
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
|
||||
import { dehydrate, HydrationBoundary, QueryClient } from "@tanstack/react-query";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-react-query";
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
|
||||
interface ProjectInvestmentDetail {
|
||||
minInvestment: number;
|
||||
totalInvestment: number;
|
||||
targetInvestment: number;
|
||||
minInvestment: number;
|
||||
totalInvestment: number;
|
||||
targetInvestment: number;
|
||||
}
|
||||
|
||||
interface Project {
|
||||
id: string;
|
||||
projectName: string;
|
||||
businessId: string;
|
||||
investmentCount: number;
|
||||
ProjectInvestmentDetail: ProjectInvestmentDetail[];
|
||||
tags: string[];
|
||||
id: string;
|
||||
projectName: string;
|
||||
businessId: string;
|
||||
investmentCount: number;
|
||||
ProjectInvestmentDetail: ProjectInvestmentDetail[];
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
interface Business {
|
||||
id: string;
|
||||
businessName: string;
|
||||
joinedDate: string;
|
||||
Projects: Project[];
|
||||
id: string;
|
||||
businessName: string;
|
||||
joinedDate: string;
|
||||
Projects: Project[];
|
||||
}
|
||||
|
||||
function getBusinesses(client: SupabaseClient, query: string | null) {
|
||||
return client.from("Business").select("id, businessName, joinedDate").ilike("businessName", `%${query}%`);
|
||||
}
|
||||
|
||||
function getProjects(client: SupabaseClient, businessIds: string[]) {
|
||||
return client
|
||||
.from("Project")
|
||||
.select(
|
||||
`
|
||||
id,
|
||||
projectName,
|
||||
businessId,
|
||||
ProjectInvestmentDetail (
|
||||
minInvestment,
|
||||
totalInvestment,
|
||||
targetInvestment
|
||||
)
|
||||
`
|
||||
)
|
||||
.in("businessId", businessIds);
|
||||
}
|
||||
|
||||
function getTags(client: SupabaseClient, projectIds: string[]) {
|
||||
return client.from("ItemTag").select("itemId, Tag (value)").in("itemId", projectIds);
|
||||
}
|
||||
|
||||
function getInvestmentCounts(client: SupabaseClient, projectIds: string[]) {
|
||||
return client.from("InvestmentDeal").select("*", { count: "exact", head: true }).in("projectId", projectIds);
|
||||
}
|
||||
|
||||
export default function Find() {
|
||||
const searchParams = useSearchParams();
|
||||
const query = searchParams.get('query');
|
||||
const [results, setResults] = useState<Business[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const searchParams = useSearchParams();
|
||||
const query = searchParams.get("query");
|
||||
// const query = "neon";
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
let supabase = createSupabaseClient();
|
||||
|
||||
// fetch businesses
|
||||
const { data: businesses, error: businessError } = await supabase
|
||||
.from('Business')
|
||||
.select('id, businessName, joinedDate')
|
||||
.ilike('businessName', `%${query}%`);
|
||||
const {
|
||||
data: businesses,
|
||||
isLoading: isLoadingBusinesses,
|
||||
error: businessError,
|
||||
} = useQuery(getBusinesses(supabase, query));
|
||||
|
||||
if (businessError) {
|
||||
console.error('Error fetching businesses:', businessError);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
const businessIds = businesses?.map((b) => b.id) || [];
|
||||
const {
|
||||
data: projects,
|
||||
isLoading: isLoadingProjects,
|
||||
error: projectError,
|
||||
} = useQuery(getProjects(supabase, businessIds), { enabled: businessIds.length > 0 });
|
||||
|
||||
// fetch projects for these businesses
|
||||
const { data: projects, error: projectError } = await supabase
|
||||
.from('Project')
|
||||
.select(`
|
||||
id,
|
||||
projectName,
|
||||
businessId,
|
||||
ProjectInvestmentDetail (
|
||||
minInvestment,
|
||||
totalInvestment,
|
||||
targetInvestment
|
||||
)
|
||||
`)
|
||||
.in('businessId', businesses?.map(b => b.id) || []);
|
||||
const projectIds = projects?.map((p) => p.id) || [];
|
||||
const {
|
||||
data: tags,
|
||||
isLoading: isLoadingTags,
|
||||
error: tagError,
|
||||
} = useQuery(getTags(supabase, projectIds), { enabled: projectIds.length > 0 });
|
||||
|
||||
if (projectError) {
|
||||
console.error('Error fetching projects:', projectError);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
const {
|
||||
data: investmentCounts,
|
||||
isLoading: isLoadingInvestments,
|
||||
error: investmentError,
|
||||
} = useQuery(getInvestmentCounts(supabase, projectIds), { enabled: projectIds.length > 0 });
|
||||
|
||||
// fetch tags for these projects
|
||||
const { data: tags, error: tagError } = await supabase
|
||||
.from('ItemTag')
|
||||
.select('itemId, Tag (value)')
|
||||
.in('itemId', projects?.map(p => p.id) || []);
|
||||
// -----
|
||||
|
||||
if (tagError) {
|
||||
console.error('Error fetching tags:', tagError);
|
||||
}
|
||||
const isLoading = isLoadingBusinesses || isLoadingProjects || isLoadingTags || isLoadingInvestments;
|
||||
const error = businessError || projectError || tagError || investmentError;
|
||||
|
||||
// fetch investment counts
|
||||
const { data: investmentCounts, error: investmentError } = await supabase
|
||||
.from('InvestmentDeal')
|
||||
.select('projectId, count', { count: 'exact', head: false })
|
||||
.in('projectId', projects?.map(p => p.id) || []);
|
||||
const results: Business[] =
|
||||
businesses?.map((business) => ({
|
||||
...business,
|
||||
Projects:
|
||||
projects
|
||||
?.filter((project) => project.businessId === business.id)
|
||||
.map((project) => ({
|
||||
...project,
|
||||
tags: tags?.filter((tag) => tag.itemId === project.id).map((tag) => tag.Tag.value) || [],
|
||||
investmentCount: investmentCounts?.find((ic) => ic.projectId === project.id)?.count || 0,
|
||||
})) || [],
|
||||
})) || [];
|
||||
|
||||
if (investmentError) {
|
||||
console.error('Error fetching investment counts:', investmentError);
|
||||
}
|
||||
if (isLoading) return <p>Loading...</p>;
|
||||
if (error) return <p>Error fetching data: {error.message}</p>;
|
||||
|
||||
// combine all data
|
||||
const processedResults = businesses?.map(business => ({
|
||||
...business,
|
||||
Projects: projects
|
||||
?.filter(project => project.businessId === business.id)
|
||||
.map(project => ({
|
||||
...project,
|
||||
tags: tags
|
||||
?.filter(t => t.itemId === project.id)
|
||||
.map(t => t.Tag.values as unknown as string) || [],
|
||||
investmentCount: investmentCounts?.find(ic => ic.projectId === project.id)?.count || 0
|
||||
})) || []
|
||||
})) || [];
|
||||
|
||||
setResults(processedResults);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
if (query) {
|
||||
fetchData();
|
||||
}
|
||||
}, [query]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{loading && <p>Loading...</p>}
|
||||
{!loading && results.length === 0 && <p>No results found.</p>}
|
||||
{!loading && results.length > 0 && (
|
||||
<ul>
|
||||
{results.map(business => (
|
||||
<li key={business.id}>
|
||||
<h2>{business.businessName}</h2>
|
||||
<p>Joined Date: {new Date(business.joinedDate).toLocaleDateString()}</p>
|
||||
<ul>
|
||||
{business.Projects.map((project) => (
|
||||
<li key={project.id}>
|
||||
<h3>{project.projectName}</h3>
|
||||
<p>Investment Count: {project.investmentCount}</p>
|
||||
<p>Min Investment: ${project.ProjectInvestmentDetail[0]?.minInvestment}</p>
|
||||
<p>Total Investment: ${project.ProjectInvestmentDetail[0]?.totalInvestment}</p>
|
||||
<p>Target Investment: ${project.ProjectInvestmentDetail[0]?.targetInvestment}</p>
|
||||
<p>Tags: {project.tags.join(', ')}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
{results.length === 0 && <p>No results found.</p>}
|
||||
{results.length > 0 && (
|
||||
<ul>
|
||||
{results.map((business) => (
|
||||
<li key={business.id}>
|
||||
<h2>{business.businessName}</h2>
|
||||
<p>Joined Date: {new Date(business.joinedDate).toLocaleDateString()}</p>
|
||||
<ul>
|
||||
{business.Projects.map((project) => (
|
||||
<li key={project.id}>
|
||||
<h3>{project.projectName}</h3>
|
||||
<p>Investment Count: {project.investmentCount}</p>
|
||||
<p>Min Investment: ${project.ProjectInvestmentDetail[0]?.minInvestment}</p>
|
||||
<p>Total Investment: ${project.ProjectInvestmentDetail[0]?.totalInvestment}</p>
|
||||
<p>Target Investment: ${project.ProjectInvestmentDetail[0]?.targetInvestment}</p>
|
||||
<p>Tags: {project.tags.join(", ")}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Montserrat } from "next/font/google";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import { ReactQueryClientProvider } from "@/components/ReactQueryClientProvider";
|
||||
import "@/app/globals.css";
|
||||
|
||||
import { NavigationBar } from "@/components/navigationBar/nav";
|
||||
@ -24,16 +25,18 @@ interface RootLayoutProps {
|
||||
|
||||
export default function RootLayout({ children }: RootLayoutProps) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head />
|
||||
<body className={`${montserrat.className}`}>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<div className="relative flex min-h-screen flex-col">
|
||||
<NavigationBar />
|
||||
<div className="flex-1 bg-background">{children}</div>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
<ReactQueryClientProvider>
|
||||
<html lang="en">
|
||||
<head />
|
||||
<body className={`${montserrat.className}`}>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<div className="relative flex min-h-screen flex-col">
|
||||
<NavigationBar />
|
||||
<div className="flex-1 bg-background">{children}</div>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
</ReactQueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
20
src/components/ReactQueryClientProvider.tsx
Normal file
20
src/components/ReactQueryClientProvider.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
|
||||
export const ReactQueryClientProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [queryClient] = useState(
|
||||
() =>
|
||||
new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
// With SSR, we usually want to set some default staleTime
|
||||
// above 0 to avoid refetching immediately on the client
|
||||
staleTime: 60 * 1000,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user