feat: store investment statement in database

This commit is contained in:
sirin 2024-10-13 21:18:32 +07:00
parent fc1dd4b4d9
commit d8a2f13d6c
4 changed files with 148 additions and 53 deletions

View File

@ -1,7 +1,7 @@
"use client"; "use client";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useStripe, useElements, PaymentElement } from "@stripe/react-stripe-js"; import { useStripe, useElements, CardElement } from "@stripe/react-stripe-js";
import convertToSubcurrency from "@/lib/convertToSubcurrency"; import convertToSubcurrency from "@/lib/convertToSubcurrency";
import { import {
Dialog, Dialog,
@ -14,12 +14,19 @@ import {
DialogClose, DialogClose,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import useSession from "@/lib/supabase/useSession";
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
import { useRouter } from "next/navigation";
const CheckoutPage = ({ const CheckoutPage = ({
amount, amount,
project_id,
investor_id,
isAcceptTermAndService, isAcceptTermAndService,
}: { }: {
amount: number; amount: number;
project_id: number;
investor_id: string;
isAcceptTermAndService: () => boolean; isAcceptTermAndService: () => boolean;
}) => { }) => {
const stripe = useStripe(); const stripe = useStripe();
@ -27,8 +34,12 @@ const CheckoutPage = ({
const [errorMessage, setErrorMessage] = useState<string>(); const [errorMessage, setErrorMessage] = useState<string>();
const [clientSecret, setClientSecret] = useState(""); const [clientSecret, setClientSecret] = useState("");
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [isDialogOpen, setIsDialogOpen] = useState(false); // State for dialog open/close const [isDialogOpen, setIsDialogOpen] = useState(false);
const isAcceptTerm = isAcceptTermAndService(); const isAcceptTerm = isAcceptTermAndService();
const router = useRouter();
const { session } = useSession();
const user = session?.user;
useEffect(() => { useEffect(() => {
fetch("/api/create-payment-intent", { fetch("/api/create-payment-intent", {
@ -58,19 +69,39 @@ const CheckoutPage = ({
return; return;
} }
const { error } = await stripe.confirmPayment({ await stripe
elements, .confirmCardPayment(clientSecret, {
clientSecret, payment_method: {
confirmParams: { card: elements.getElement(CardElement)!,
return_url: `http://www.localhost:3000/payment-success?amount=${amount}`, },
}, })
}); .then(async (result) => {
if (result.error) {
setErrorMessage(result.error.message);
} else {
try {
const supabase = createSupabaseClient();
const { data, error } = await supabase.from("InvestmentDeal").insert([
{
investorId: investor_id,
projectId: project_id,
dealAmount: amount,
},
]);
console.log("ADADSADWADWWAD");
if (error) { if (error) {
setErrorMessage(error.message); console.error("Supabase Insert Error:", error.message);
} } else {
console.log("Insert successful:", data);
setLoading(false); router.push(`http://www.localhost:3000/payment-success?amount=${amount}`);
}
} catch (err) {
console.error("Unexpected error during Supabase insert:", err);
}
}
setLoading(false);
});
}; };
if (!clientSecret || !stripe || !elements) { if (!clientSecret || !stripe || !elements) {
@ -89,7 +120,7 @@ const CheckoutPage = ({
return ( return (
<div> <div>
{clientSecret && <PaymentElement />} {clientSecret && <CardElement />}
{errorMessage && <div>{errorMessage}</div>} {errorMessage && <div>{errorMessage}</div>}

View File

@ -2,15 +2,21 @@
import { useState } from "react"; import { useState } from "react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useParams } from "next/navigation";
import { Separator } from "@/components/ui/separator"; import { Separator } from "@/components/ui/separator";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { useQuery } from "@supabase-cache-helpers/postgrest-react-query";
import convertToSubcurrency from "@/lib/convertToSubcurrency"; import convertToSubcurrency from "@/lib/convertToSubcurrency";
import CheckoutPage from "./checkoutPage"; import CheckoutPage from "./checkoutPage";
import { Elements } from "@stripe/react-stripe-js"; import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js"; import { loadStripe } from "@stripe/stripe-js";
import { getProjectDataQuery } from "@/lib/data/projectQuery";
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
import useSession from "@/lib/supabase/useSession";
if (process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY === undefined) { if (process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY === undefined) {
throw new Error("NEXT_PUBLIC_STRIPE_PUBLIC_KEY is not defined"); throw new Error("NEXT_PUBLIC_STRIPE_PUBLIC_KEY is not defined");
} }
@ -47,6 +53,14 @@ export default function InvestPage() {
const [checkedTerms, setCheckedTerms] = useState(Array(term_data.length).fill(false)); const [checkedTerms, setCheckedTerms] = useState(Array(term_data.length).fill(false));
const [investAmount, setInvestAmount] = useState(10); const [investAmount, setInvestAmount] = useState(10);
const { session } = useSession();
const investor_id = session!.user.id;
const params = useParams<{ id: string }>();
const supabase = createSupabaseClient();
const { data: projectData, isLoading: isLoadingProject } = useQuery(getProjectDataQuery(supabase, Number(params.id)));
const handleCheckboxChange = (index: number) => { const handleCheckboxChange = (index: number) => {
const updatedCheckedTerms = [...checkedTerms]; const updatedCheckedTerms = [...checkedTerms];
updatedCheckedTerms[index] = !updatedCheckedTerms[index]; updatedCheckedTerms[index] = !updatedCheckedTerms[index];
@ -62,7 +76,7 @@ export default function InvestPage() {
return ( return (
<div className="mx-10 md:mx-40 my-10"> <div className="mx-10 md:mx-40 my-10">
<h1 className="text-2xl md:text-4xl font-bold">Invest on NVIDIA</h1> <h1 className="text-2xl md:text-4xl font-bold">Invest on ${projectData?.project_name}</h1>
<Separator className="my-4" /> <Separator className="my-4" />
<div></div> <div></div>
<div> <div>
@ -113,7 +127,12 @@ export default function InvestPage() {
amount: convertToSubcurrency(investAmount), amount: convertToSubcurrency(investAmount),
currency: "usd", currency: "usd",
}}> }}>
<CheckoutPage amount={investAmount} isAcceptTermAndService={isAcceptTermAndService} /> <CheckoutPage
amount={investAmount}
isAcceptTermAndService={isAcceptTermAndService}
project_id={Number(params.id)}
investor_id={investor_id}
/>
</Elements> </Elements>
</div> </div>
</div> </div>

View File

@ -1,4 +1,10 @@
import { redirect } from "next/navigation";
export default function PaymentSuccess({ searchParams: { amount } }: { searchParams: { amount: string } }) { export default function PaymentSuccess({ searchParams: { amount } }: { searchParams: { amount: string } }) {
if (!amount) {
redirect("/");
}
return ( return (
<main className="max-w-6xl mx-auto p-10 text-white text-center border m-10 rounded-md bg-gradient-to-tr from-blue-500 to-purple-500"> <main className="max-w-6xl mx-auto p-10 text-white text-center border m-10 rounded-md bg-gradient-to-tr from-blue-500 to-purple-500">
<div className="mb-10"> <div className="mb-10">

View File

@ -1,11 +1,14 @@
import { SupabaseClient } from "@supabase/supabase-js"; import { SupabaseClient } from "@supabase/supabase-js";
async function getTopProjects(client: SupabaseClient, numberOfRecords: number = 4) { async function getTopProjects(
try { client: SupabaseClient,
const { data, error } = await client numberOfRecords: number = 4,
.from("Project") ) {
.select( try {
` const { data, error } = await client
.from("Project")
.select(
`
id, id,
projectName, projectName,
businessId, businessId,
@ -27,22 +30,44 @@ async function getTopProjects(client: SupabaseClient, numberOfRecords: number =
Business ( Business (
location location
) )
` `,
) )
.order("publishedTime", { ascending: false }) .order("publishedTime", { ascending: false })
.limit(numberOfRecords); .limit(numberOfRecords);
if (error) { if (error) {
console.error("Error fetching top projects:", error.message); console.error("Error fetching top projects:", error.message);
return { data: null, error: 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." };
} }
return { data, error: null };
} catch (err) {
console.error("Unexpected error:", err);
return { data: null, error: "An unexpected error occurred." };
} }
}
function getProjectDataQuery(client: SupabaseClient, projectId: number) {
return client.from("Project").select(
`
project_name:projectName,
project_short_description:projectShortDescription,
project_description:projectDescription,
published_time:publishedTime,
...ProjectInvestmentDetail!inner (
min_investment:minInvestment,
total_investment:totalInvestment,
target_investment:targetInvestment,
investment_deadline:investmentDeadline
),
tags:ItemTag!inner (
...Tag!inner (
tag_name:value
)
)
`,
).eq("id", projectId).single();
}
async function getProjectData(client: SupabaseClient, projectId: number) { async function getProjectData(client: SupabaseClient, projectId: number) {
const query = client.from("Project").select( const query = client.from("Project").select(
@ -62,11 +87,11 @@ async function getProjectData(client: SupabaseClient, projectId: number) {
tag_name:value tag_name:value
) )
) )
` `,
).eq("id", projectId).single() ).eq("id", projectId).single();
const {data, error} = await query; const { data, error } = await query;
return { data, error } return { data, error };
} }
export interface FilterParams { export interface FilterParams {
@ -79,14 +104,25 @@ export interface FilterParams {
} }
export interface FilterProjectQueryParams extends FilterParams { export interface FilterProjectQueryParams extends FilterParams {
page: number, page: number;
pageSize: number pageSize: number;
} }
function searchProjectsQuery(client: SupabaseClient, {searchTerm, tagsFilter, projectStatus, businessTypeFilter, sortByTimeFilter, page = 1, pageSize = 4}: FilterProjectQueryParams) { function searchProjectsQuery(
client: SupabaseClient,
{
searchTerm,
tagsFilter,
projectStatus,
businessTypeFilter,
sortByTimeFilter,
page = 1,
pageSize = 4,
}: FilterProjectQueryParams,
) {
const start = (page - 1) * pageSize; const start = (page - 1) * pageSize;
const end = start + pageSize - 1; const end = start + pageSize - 1;
let query = client.from("Project").select( let query = client.from("Project").select(
` `
project_id:id, project_id:id,
@ -114,8 +150,8 @@ function searchProjectsQuery(client: SupabaseClient, {searchTerm, tagsFilter, pr
), ),
business_location:location business_location:location
) )
` `,
).order("publishedTime", { ascending: false }).range(start, end) ).order("publishedTime", { ascending: false }).range(start, end);
if (sortByTimeFilter === "all") { if (sortByTimeFilter === "all") {
sortByTimeFilter = undefined; sortByTimeFilter = undefined;
@ -134,24 +170,27 @@ function searchProjectsQuery(client: SupabaseClient, {searchTerm, tagsFilter, pr
} }
if (searchTerm) { if (searchTerm) {
query = query.ilike('projectName', `%${searchTerm}%`) query = query.ilike("projectName", `%${searchTerm}%`);
} }
if (tagsFilter) { if (tagsFilter) {
query = query.in('ItemTag.Tag.value', tagsFilter) query = query.in("ItemTag.Tag.value", tagsFilter);
} }
if (projectStatus) { if (projectStatus) {
query = query.eq("ProjectStatus.value", projectStatus) query = query.eq("ProjectStatus.value", projectStatus);
} }
if (businessTypeFilter) { if (businessTypeFilter) {
query = query.eq("Business.businessType.value", businessTypeFilter) query = query.eq("Business.businessType.value", businessTypeFilter);
} }
return query; return query;
} }
export {
export { getTopProjects, getProjectData, searchProjectsQuery }; getProjectData,
getProjectDataQuery,
getTopProjects,
searchProjectsQuery,
};