Merge branch 'front-end' into back-end

This commit is contained in:
Sosokker 2024-11-10 23:24:00 +07:00
commit d5c6590cd3
9 changed files with 480 additions and 320 deletions

View File

@ -5,13 +5,12 @@ import ReactMarkdown from "react-markdown";
import * as Tabs from "@radix-ui/react-tabs";
import { Button } from "@/components/ui/button";
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";
import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
import { Separator } from "@/components/ui/separator";
import { createSupabaseClient } from "@/lib/supabase/serverComponentClient";
import FollowShareButtons from "./followShareButton";
import { DisplayFullImage } from "./displayImage";
import { getProjectData } from "@/lib/data/projectQuery";
import { getDealList } from "@/app/api/dealApi";
import { sumByKey, toPercentage } from "@/lib/utils";
@ -19,6 +18,7 @@ import { redirect } from "next/navigation";
import { isOwnerOfProject } from "./query";
import { UpdateTab } from "./UpdateTab";
import remarkGfm from "remark-gfm";
import Gallery from "@/components/carousel";
const PHOTO_MATERIAL_ID = 2;
@ -75,7 +75,6 @@ export default async function ProjectDealPage({ params }: { params: { id: number
? projectMaterial.flatMap((item) =>
(item.material_url || ["/boiler1.jpg"]).map((url: string) => ({
src: url,
alt: "Image",
}))
)
: [{ src: "/boiler1.jpg", alt: "Default Boiler Image" }];
@ -104,35 +103,8 @@ export default async function ProjectDealPage({ params }: { params: { id: number
</div>
<div id="sub-content" className="flex flex-row mt-5">
{/* image carousel */}
<div id="image-carousel" className="shrink-0 w-[700px] flex flex-col">
{/* first carousel */}
<Carousel className="w-full h-[400px] ml-1 overflow-hidden">
<CarouselContent className="flex h-full">
{carouselData.map((item, index) => (
<CarouselItem key={index}>
<Image src={item.src} alt={item.alt} width={700} height={400} className="rounded-lg object-cover" />
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious className="absolute left-2 top-1/2 transform -translate-y-1/2 z-10 text-white bg-black opacity-50 hover:opacity-100" />
<CarouselNext className="absolute right-2 top-1/2 transform -translate-y-1/2 z-10 text-white bg-black opacity-50 hover:opacity-100" />
</Carousel>
{/* second carousel */}
<Carousel className="w-full ml-1 h-[100px] mt-5 overflow-hidden">
<CarouselContent className="flex space-x-1 h-[100px]">
{carouselData.map((item, index) => (
<CarouselItem key={index} className="flex">
<DisplayFullImage
src={item.src}
alt={item.alt}
width={200}
height={100}
className="rounded-lg object-cover h-[100px] w-[200px]"
/>
</CarouselItem>
))}
</CarouselContent>
</Carousel>
<div id="image-carousel" className="w-full">
<Gallery images={carouselData} />
</div>
<Card className="w-[80%] ml-10 shadow-sm">

View File

@ -1,53 +0,0 @@
import { useEffect, useState } from "react";
import { Deal, getDealList, convertToGraphData, getRecentDealData } from "../api/dealApi";
import { RecentDealData } from "@/components/recent-funds";
import { getCurrentUserID } from "../api/userApi";
// custom hook for deal list
export function useDealList() {
const [dealList, setDealList] = useState<Deal[]>([]);
const fetchDealList = async () => {
// set the state to the deal list of current business user
setDealList(await getDealList(await getCurrentUserID()));
};
useEffect(() => {
fetchDealList();
}, []);
return dealList;
}
export function useGraphData() {
const [graphData, setGraphData] = useState({});
const fetchGraphData = async () => {
// fetch the state to the deal list of current business user
const dealList = await getDealList(await getCurrentUserID());
if (dealList) {
setGraphData(convertToGraphData(dealList));
}
};
useEffect(() => {
fetchGraphData();
}, []);
return graphData;
}
export function useRecentDealData() {
const [recentDealData, setRecentDealData] = useState<RecentDealData[]>();
const fetchRecentDealData = async () => {
// set the state to the deal list of current business user
setRecentDealData(await getRecentDealData(await getCurrentUserID()));
};
useEffect(() => {
fetchRecentDealData();
}, []);
return recentDealData;
}

View File

@ -5,84 +5,104 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Overview } from "@/components/ui/overview";
import { RecentFunds } from "@/components/recent-funds";
import { useEffect, useState } from "react";
import { useDealList } from "./hook";
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
import useSession from "@/lib/supabase/useSession";
import { getProjectByUserId } from "@/lib/data/projectQuery";
import { Loader } from "@/components/loading/loader";
const data = [
{
name: "Jan",
value: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Feb",
value: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Mar",
value: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Apr",
value: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "May",
value: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Jun",
value: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Jul",
value: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Aug",
value: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Sep",
value: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Oct",
value: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Nov",
value: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Dec",
value: Math.floor(Math.random() * 5000) + 1000,
},
];
import { getInvestmentByProjectsIds } from "@/lib/data/investmentQuery";
import { useQuery } from "@supabase-cache-helpers/postgrest-react-query";
import { overAllGraphData, Deal, fourYearGraphData, dayOftheWeekData } from "../portfolio/[uid]/query";
import CountUp from "react-countup";
import { Button } from "@/components/ui/button";
export default function Dashboard() {
let supabase = createSupabaseClient();
const supabase = createSupabaseClient();
const userId = useSession().session?.user.id;
const [projects, setProjects] = useState<
{ id: number; project_name: string; business_id: { user_id: number }[]; dataroom_id: number }[]
>([]);
const [latestInvestment, setLatestInvestment] = useState<
{
avatarUrl: string;
createdTime: Date;
dealAmount: number;
dealStatus: string;
investorId: string;
username: string;
}[]
>([]);
const tabOptions = ["daily", "monthly", "yearly"];
const [activeTab, setActiveTab] = useState("daily");
const [isSuccess, setIsSuccess] = useState(false);
const [graphType, setGraphType] = useState("line");
const dealList = useDealList();
const totalDealAmount = dealList?.reduce((sum, deal) => sum + deal.deal_amount, 0) || 0;
const [currentProjectId, setCurrentProjectId] = useState<number>(projects[0]?.id);
const investmentDetail = useQuery(
getInvestmentByProjectsIds(
supabase,
projects.map((item) => {
return item.id.toString();
})
)
);
let graphData = [];
const filteredData = (investmentDetail?.data || []).filter((deal) => deal.project_id === currentProjectId);
const handleTabChange = (tab: string) => {
setActiveTab(tab);
};
if (activeTab === "daily") {
graphData = dayOftheWeekData(filteredData);
} else if (activeTab === "yearly") {
graphData = fourYearGraphData(filteredData);
} else {
graphData = overAllGraphData(filteredData);
}
useEffect(() => {
const setTopLatestInvestment = () => {
if (investmentDetail?.data) {
setLatestInvestment(
investmentDetail.data
.slice(0, 8)
.map((item) => {
// set the project according to current project id
if (item.project_id === currentProjectId) {
return {
avatarUrl: item.avatar_url,
createdTime: item.created_time,
dealAmount: item.deal_amount,
dealStatus: item.deal_status,
investorId: item.investor_id,
username: item.username,
};
}
return undefined;
})
.filter((item) => item !== undefined) as {
avatarUrl: string;
createdTime: Date;
dealAmount: number;
dealStatus: string;
investorId: string;
username: string;
}[]
);
// console.table(latestInvestment);
}
};
setTopLatestInvestment();
}, [supabase, investmentDetail]);
useEffect(() => {
const fetchProjects = async () => {
if (userId) {
const { data, error } = await getProjectByUserId(supabase, userId);
// alert(JSON.stringify(data));
if (error) {
console.error("Error while fetching projects");
}
if (data) {
// set project and current project id
setProjects(data);
// console.table(data);
setCurrentProjectId(data[0].id);
}
} else {
console.error("Error with UserId while fetching projects");
@ -93,7 +113,7 @@ export default function Dashboard() {
}, [supabase, userId]);
return (
<>
<div className="container max-w-screen-xl">
<Loader isSuccess={isSuccess} />
<div className="md:hidden">
<Image
@ -116,15 +136,21 @@ export default function Dashboard() {
<div className="flex items-center justify-between space-y-2">
<h2 className="text-3xl font-bold tracking-tight">Business Dashboard</h2>
</div>
<Tabs defaultValue={projects[0].project_name} className="space-y-4">
{projects && projects.length > 0 && (
<Tabs className="space-y-4" defaultValue={projects[0].project_name}>
<TabsList>
{projects.map((project) => (
<TabsTrigger key={project.id} value={project.project_name}>
<TabsTrigger
key={project.id}
value={project.project_name}
onClick={() => setCurrentProjectId(project.id)}
>
{project.project_name}
</TabsTrigger>
))}
</TabsList>
<TabsContent value={projects[0].project_name} className="space-y-4">
{projects.map((project) => (
<TabsContent value={project.project_name} className="space-y-4">
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
@ -143,10 +169,15 @@ export default function Dashboard() {
</svg>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">${totalDealAmount}</div>
{/* <p className="text-xs text-muted-foreground">
+20.1% from last month
</p> */}
<div className="text-2xl font-bold">
$
<CountUp
end={filteredData
.filter((project) => project.deal_status === "Completed")
.reduce((sum, current) => sum + current.deal_amount, 0)}
duration={1}
/>
</div>
</CardContent>
</Card>
<Card>
@ -167,7 +198,9 @@ export default function Dashboard() {
</svg>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">+2350</div>
<div className="text-2xl font-bold">
+<CountUp end={2350} duration={1} />
</div>
{/* <p className="text-xs text-muted-foreground">
+180.1% from last month
</p> */}
@ -192,46 +225,45 @@ export default function Dashboard() {
</svg>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">+12,234</div>
<div className="text-2xl font-bold">
+<CountUp end={12234} duration={1} />
</div>
{/* <p className="text-xs text-muted-foreground">
+19% from last month
</p> */}
</CardContent>
</Card>
{/* <Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Active Now
</CardTitle>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
className="h-4 w-4 text-muted-foreground"
<Button
onClick={() => {
window.location.href = `/project/${project.id}/edit`;
}}
className="h-full bg-emerald-500 hover:bg-emerald-800 font-bold text-xl"
>
<path d="M22 12h-4l-3 9L9 3l-3 9H2" />
Edit Project
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M3 17.25V21h3.75l11.05-11.05-3.75-3.75L3 17.25zM20.71 7.04a1.003 1.003 0 000-1.42l-2.34-2.34a1.003 1.003 0 00-1.42 0L15.13 4.5l3.75 3.75 1.83-1.21z"
fill="currentColor"
/>
</svg>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">+573</div>
<p className="text-xs text-muted-foreground">
+201 since last hour
</p>
</CardContent>
</Card> */}
</Button>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-7">
<Card className="col-span-4">
<CardHeader>
<CardTitle>Overview</CardTitle>
</CardHeader>
<CardContent className="pl-2">
<Overview graphType={graphType} data={data} />
{/* tab to switch between line and bar graph */}
<Tabs value={activeTab} onValueChange={handleTabChange} className="w-full">
<TabsList className="grid w-56 grid-cols-3 ml-5">
{tabOptions.map((tab) => (
<TabsTrigger key={tab} value={tab}>
{tab.charAt(0).toUpperCase() + tab.slice(1)}
</TabsTrigger>
))}
</TabsList>
<CardContent className="pl-2 mt-5">
<Overview graphType={graphType} data={graphData} />
<Tabs defaultValue="line" className="space-y-4 ml-[50%] mt-2">
<TabsList>
<TabsTrigger value="line" onClick={() => setGraphType("line")}>
@ -243,21 +275,34 @@ export default function Dashboard() {
</TabsList>
</Tabs>
</CardContent>
</Tabs>
</Card>
<Card className="col-span-3">
<CardHeader>
<CardTitle>Recent Funds</CardTitle>
<CardDescription>You made {dealList?.length || 0} sales this month.</CardDescription>
</CardHeader>
<CardContent>
<RecentFunds></RecentFunds>
<RecentFunds
data={latestInvestment.map((item) => {
return {
name: item.username,
amount: item.dealAmount,
avatar: item.avatarUrl,
date: new Date(item.createdTime),
status: item.dealStatus,
profile_url: `/profile/${item.investorId}`,
};
})}
/>
</CardContent>
</Card>
</div>
</TabsContent>
))}
</Tabs>
)}
</div>
</div>
</div>
</>
);
}

View File

@ -52,7 +52,6 @@ export default async function Portfolio({ params }: { params: { uid: string } })
);
}
const username = localUser ? localUser.user.user_metadata.name : "Anonymous";
// console.log(username)
const overAllData = deals ? overAllGraphData(deals) : [];
const fourYearData = deals ? fourYearGraphData(deals) : [];
const dayOfWeekData = deals ? dayOftheWeekData(deals) : [];
@ -66,31 +65,13 @@ export default async function Portfolio({ params }: { params: { uid: string } })
)
: [];
const totalInvestment = deals ? getTotalInvestment(deals) : 0;
// console.log(latestDeals);
const tagCount = countTags(tags);
// console.log(investedBusinessIds);
const businessType = deals
? await Promise.all(deals.map(async (item) => await getBusinessTypeName(supabase, item.project_id)))
: [];
const countedBusinessType = countValues(businessType.filter((item) => item !== null));
// console.log(countedBusinessType);
// console.log(tagCount);
return (
<div className="container max-w-screen-xl">
{/* {JSON.stringify(params.uid)} */}
{/* {JSON.stringify(tagCount)} */}
{/* {JSON.stringify(deals)} */}
{/* {JSON.stringify(dayOfWeekData)} */}
{/* {JSON.stringify(overAllGraphData)} */}
{/* {JSON.stringify(threeYearGraphData)} */}
{/* {JSON.stringify(uniqueProjectIds)} */}
{/* <div className="flex flex-row">
<h1>Total Invest : </h1>
<div>{totalInvest}</div>
</div> */}
{/* <CountUpComponent end={100} duration={3} /> */}
<div className="text-center py-4">
<h1 className="text-2xl font-semibold">Welcome to your Portfolio, {username}!</h1>
<p className="text-lg text-muted-foreground">

View File

@ -26,11 +26,11 @@ function getTotalInvestment(deals: { deal_amount: number }[]) {
}
async function getLatestInvestment(
supabase: SupabaseClient,
deals: { project_id: number; deal_amount: number; created_time: Date }[]
deals: { project_id: number; deal_amount: number; created_time: Date;}[]
) {
const llist = [];
const count = 8;
// select project name from the given id
for (let i = deals.length - 1; i >= 0 && llist.length < count; --i) {
let { data: project, error } = await supabase.from("project").select("project_name").eq("id", deals[i].project_id);
if (error) {
@ -38,6 +38,7 @@ async function getLatestInvestment(
}
let url = fetchLogoURL(supabase, deals[i].project_id);
llist.push({
projectId: deals[i].project_id,
name: project?.[0]?.project_name,
amount: deals[i].deal_amount,
date: new Date(deals[i].created_time),
@ -104,7 +105,7 @@ async function getBusinessTypeName(supabase: SupabaseClient, projectId: number)
}
// only use deal that were made at most year ago
interface Deal {
export interface Deal {
created_time: string | number | Date;
deal_amount: any;
}

View File

@ -0,0 +1,88 @@
"use client";
import { useEffect, useState, useMemo } from "react";
import { Carousel, CarouselContent, CarouselItem, type CarouselApi } from "./ui/carousel";
import Image from "next/image";
interface GalleryProps {
images: { src: string }[];
}
const Gallery = ({ images }: GalleryProps) => {
const [mainApi, setMainApi] = useState<CarouselApi>();
const [thumbnailApi, setThumbnailApi] = useState<CarouselApi>();
const [current, setCurrent] = useState(0);
const mainImage = useMemo(
() =>
images.map((image, index) => (
<CarouselItem key={index} className="relative aspect-video w-full border-8 border-b">
<Image src={image.src} alt={`Carousel Main Image ${index + 1}`} fill style={{ objectFit: "contain" }} />
</CarouselItem>
)),
[images]
);
const thumbnailImages = useMemo(
() =>
images.map((image, index) => (
<CarouselItem key={index} className="relative aspect-square basis-1/4" onClick={() => handleClick(index)}>
<Image
className={`${index === current ? "border-2" : ""}`}
src={image.src}
fill
alt={`Carousel Thumbnail Image ${index + 1}`}
style={{ objectFit: "contain" }}
/>
</CarouselItem>
)),
[images, current]
);
useEffect(() => {
if (!mainApi || !thumbnailApi) {
return;
}
const handleTopSelect = () => {
const selected = mainApi.selectedScrollSnap();
setCurrent(selected);
thumbnailApi.scrollTo(selected);
};
const handleBottomSelect = () => {
const selected = thumbnailApi.selectedScrollSnap();
setCurrent(selected);
mainApi.scrollTo(selected);
};
mainApi.on("select", handleTopSelect);
thumbnailApi.on("select", handleBottomSelect);
return () => {
mainApi.off("select", handleTopSelect);
thumbnailApi.off("select", handleBottomSelect);
};
}, [mainApi, thumbnailApi]);
const handleClick = (index: number) => {
if (!mainApi || !thumbnailApi) {
return;
}
thumbnailApi.scrollTo(index);
mainApi.scrollTo(index);
setCurrent(index);
};
return (
<div className="w-full max-w-xl sm:w-auto">
<Carousel setApi={setMainApi}>
<CarouselContent className="m-1">{mainImage}</CarouselContent>
</Carousel>
<Carousel setApi={setThumbnailApi}>
<CarouselContent className="m-1 h-16">{thumbnailImages}</CarouselContent>
</Carousel>
</div>
);
};
export default Gallery;

View File

@ -71,15 +71,19 @@ export function ProjectCard(props: ProjectCardProps) {
<span className="text-xs">{props.location}</span>
</div>
<div className="flex flex-wrap mt-1 items-center text-muted-foreground">
{props.tags && Array.isArray(props.tags) ? (
props.tags.map((tag) => (
<span id="tag" key={tag} className="text-[10px] rounded-md bg-slate-200 dark:bg-slate-700 p-1 mr-1">
{props.tags?.length !== 0 && Array.isArray(props.tags)
? props.tags
.filter((tag) => tag && tag.trim() !== "") // Filters out null or blank tags
.map((tag) => (
<span
id="tag"
key={tag}
className="text-[10px] rounded-md bg-slate-200 dark:bg-slate-700 p-1 mr-1"
>
{tag}
</span>
))
) : (
<span className="text-xs text-muted-foreground">No tags available</span>
)}
: null}
</div>
</div>
</div>

View File

@ -1,4 +1,5 @@
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import Link from "next/link";
export type RecentDealData = {
created_time: Date;
@ -6,29 +7,127 @@ export type RecentDealData = {
investor_id: string;
username: string;
logo_url?: string;
status?: string;
// email: string;
};
interface RecentFundsProps {
data?: { name?: string; amount?: number; avatar?: string; date?: Date; logo_url?: string }[];
data?: { name?: string; amount?: number; avatar?: string; date?: Date; logo_url?: string; status?: string; profile_url?: string }[];
}
export function RecentFunds(props: RecentFundsProps) {
const content = (
<div>
</div>
)
return (
<div className="space-y-8">
{(props?.data || []).map((deal, index) => (
<div className="flex items-center" key={index}>
<div key={index}>
{deal.profile_url ? (
<Link
href={deal.profile_url}
className="flex items-center w-full"
target="_blank"
rel="noopener noreferrer"
>
<Avatar className="h-9 w-9">
<AvatarImage src={deal.logo_url} alt={deal.name} />
<AvatarImage src={deal.logo_url ? deal.logo_url : deal.avatar} alt={deal.name} />
<AvatarFallback>{(deal.name ?? "").slice(0, 2)}</AvatarFallback>
</Avatar>
<div className="ml-4 space-y-1">
<p className="text-sm font-medium leading-none">{deal.name}</p>
<p className="text-xs text-muted-foreground">{deal?.date?.toLocaleDateString()}</p>
{deal.status && (
<div className="flex items-center space-x-1">
<span className="relative flex h-3 w-3">
<span
className={`animate-ping absolute inline-flex h-3 w-3 rounded-full opacity-75 ${
deal?.status === "In Progress"
? "bg-sky-400"
: deal?.status === "Completed"
? "bg-green-400"
: "bg-yellow-400"
}`}
></span>
<span
className={`relative inline-flex rounded-full h-2 w-2 mt-[2px] ml-0.5 ${
deal?.status === "In Progress"
? "bg-sky-500"
: deal?.status === "Completed"
? "bg-green-500"
: "bg-yellow-500"
}`}
></span>
</span>
<p
className={`text-xs m-0 ${
deal?.status === "In Progress"
? "text-sky-500"
: deal?.status === "Completed"
? "text-green-500"
: "text-yellow-500"
}`}
>
{deal?.status}
</p>
</div>
)}
</div>
<div className="ml-auto font-medium">+${deal.amount}</div>
</Link>
) : (
<div className="flex items-center w-full">
<Avatar className="h-9 w-9">
<AvatarImage src={deal.logo_url ? deal.logo_url : deal.avatar} alt={deal.name} />
<AvatarFallback>{(deal.name ?? "").slice(0, 2)}</AvatarFallback>
</Avatar>
<div className="ml-4 space-y-1">
<p className="text-sm font-medium leading-none">{deal.name}</p>
<p className="text-xs text-muted-foreground">{deal?.date?.toLocaleDateString()}</p>
{deal.status && (
<div className="flex items-center space-x-1">
<span className="relative flex h-3 w-3">
<span
className={`animate-ping absolute inline-flex h-3 w-3 rounded-full opacity-75 ${
deal?.status === "In Progress"
? "bg-sky-400"
: deal?.status === "Completed"
? "bg-green-400"
: "bg-yellow-400"
}`}
></span>
<span
className={`relative inline-flex rounded-full h-2 w-2 mt-[2px] ml-0.5 ${
deal?.status === "In Progress"
? "bg-sky-500"
: deal?.status === "Completed"
? "bg-green-500"
: "bg-yellow-500"
}`}
></span>
</span>
<p
className={`text-xs m-0 ${
deal?.status === "In Progress"
? "text-sky-500"
: deal?.status === "Completed"
? "text-green-500"
: "text-yellow-500"
}`}
>
{deal?.status}
</p>
</div>
)}
</div>
<div className="ml-auto font-medium">+${deal.amount}</div>
</div>
)}
</div>
))}
</div>
);
}

View File

@ -10,6 +10,29 @@ export const getInvestmentCountsByProjectsIds = (client: SupabaseClient, project
.in("project_id", projectIds);
};
export const getInvestmentByProjectsIds = (client: SupabaseClient, projectIds: string[]) => {
return client
.from("investment_deal")
.select(
`
id,
...deal_status_id(
deal_status:value
),
project_id,
deal_amount,
created_time,
...profiles (
investor_id:id,
username,
avatar_url
)
`
)
.in("project_id", projectIds)
.order("created_time", { ascending: false });
};
export const getInvestmentByUserId = (client: SupabaseClient, userId: string) => {
return client
.from("investment_deal")