feat: enhance ImageModal and DisplayFullImage components to support className prop and improve image handling

This commit is contained in:
THIS ONE IS A LITTLE BIT TRICKY KRUB 2024-11-06 16:07:54 +07:00
parent b3fd664cb4
commit 80b63c02bc
2 changed files with 114 additions and 90 deletions

View File

@ -16,26 +16,27 @@ interface ItemProps {
alt: string; alt: string;
width: number; width: number;
height: number; height: number;
className?: string;
} }
const ImageModal = ({ src, alt, width, height }: ItemProps) => { const ImageModal = ({ src, alt, width, height, className }: ItemProps) => {
return ( return (
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Image src={src} alt={alt} width={width} height={height} className="rounded-lg basis-0" /> <Image src={src} alt={alt} width={width} height={height} className={className} />
</DialogTrigger> </DialogTrigger>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Image Preview</DialogTitle> <DialogTitle>Image Preview</DialogTitle>
<DialogDescription>Click outside to close the image preview.</DialogDescription> <DialogDescription>Click outside to close the image preview.</DialogDescription>
</DialogHeader> </DialogHeader>
<Image src={src} alt={alt} width={700} height={400} /> <Image src={src} alt={alt} width={700} height={400} className={className} />
<DialogFooter /> <DialogFooter />
</DialogContent> </DialogContent>
</Dialog> </Dialog>
); );
}; };
export function DisplayFullImage({ src, alt, width, height }: ItemProps) { export function DisplayFullImage({ src, alt, width, height, className }: ItemProps) {
return <ImageModal src={src} alt={alt} width={width} height={height} />; return <ImageModal src={src} alt={alt} width={width} height={height} className={className} />;
} }

View File

@ -12,16 +12,25 @@ import { Separator } from "@/components/ui/separator";
import { createSupabaseClient } from "@/lib/supabase/serverComponentClient"; import { createSupabaseClient } from "@/lib/supabase/serverComponentClient";
import FollowShareButtons from "./followShareButton"; import FollowShareButtons from "./followShareButton";
import { DisplayFullImage } from "./displayImage"; import { DisplayFullImage } from "./displayImage";
import { getProjectData } from "@/lib/data/projectQuery"; import { getProjectData } from "@/lib/data/projectQuery";
import { getDealList } from "@/app/api/dealApi"; import { getDealList } from "@/app/api/dealApi";
import { sumByKey, toPercentage } from "@/lib/utils"; import { sumByKey, toPercentage } from "@/lib/utils";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
const PHOTO_MATERIAL_ID = 2;
export default async function ProjectDealPage({ params }: { params: { id: number } }) { export default async function ProjectDealPage({ params }: { params: { id: number } }) {
const supabase = createSupabaseClient(); const supabase = createSupabaseClient();
const { data: projectData, error: projectDataError } = await getProjectData(supabase, params.id); const { data: projectData, error: projectDataError } = await getProjectData(supabase, params.id);
const { data: projectMaterial, error: projectMaterialError } = await supabase
.from("project_material")
.select("material_url")
.eq("project_id", params.id)
.eq("material_type_id", PHOTO_MATERIAL_ID);
// console.log(projectMaterial);
if (projectMaterialError) {
console.error("Error while fetching project material" + projectMaterialError);
}
if (!projectData) { if (!projectData) {
redirect("/deals"); redirect("/deals");
} }
@ -44,96 +53,110 @@ export default async function ProjectDealPage({ params }: { params: { id: number
const timeDiff = Math.max(new Date(projectData.investment_deadline).getTime() - new Date().getTime(), 0); const timeDiff = Math.max(new Date(projectData.investment_deadline).getTime() - new Date().getTime(), 0);
const hourLeft = Math.floor(timeDiff / (1000 * 60 * 60)); const hourLeft = Math.floor(timeDiff / (1000 * 60 * 60));
const carouselData = Array(5).fill({ // const carouselData = Array(5).fill({
src: projectData.card_image_url || "/boiler1.jpg", // src: projectData.card_image_url || "/boiler1.jpg",
alt: `${projectData.project_name} Image`, // alt: `${projectData.project_name} Image`,
}); // });
const carouselData = projectMaterial
? projectMaterial.flatMap((item) =>
(item.material_url || ["/boiler1.jpg"]).map((url: string) => ({
src: url,
alt: "Image",
}))
)
: [];
console.log(carouselData);
return ( return (
<div className="container max-w-screen-xl my-5"> <div className="container max-w-screen-xl my-5">
<div className="flex flex-col gap-y-10"> <div className="flex flex-col gap-y-10">
<div id="content"> {/* Name, star and share button packed */}
{/* Name, star and share button packed */} <div id="header" className="flex flex-col">
<div id="header" className="flex flex-col"> <div className="flex justify-between">
<div className="flex justify-between"> <span className="flex">
<span className="flex"> <Image src="/logo.svg" alt="logo" width={50} height={50} className="sm:scale-75" />
<Image src="/logo.svg" alt="logo" width={50} height={50} className="sm:scale-75" /> <h1 className="mt-3 font-bold text-lg md:text-3xl">{projectData?.project_name}</h1>
<h1 className="mt-3 font-bold text-lg md:text-3xl">{projectData?.project_name}</h1> </span>
</span> <FollowShareButtons />
<FollowShareButtons /> </div>
</div> {/* end of pack */}
{/* end of pack */} <p className="mt-2 sm:text-sm">{projectData?.project_short_description}</p>
<p className="mt-2 sm:text-sm">{projectData?.project_short_description}</p> <div className="flex flex-wrap mt-3">
<div className="flex flex-wrap mt-3"> {projectData?.tags.map((tag, index) => (
{projectData?.tags.map((tag, index) => ( <span key={index} className="text-xs rounded-md bg-slate-200 dark:bg-slate-700 p-1 mx-1 mb-1">
<span key={index} className="text-xs rounded-md bg-slate-200 dark:bg-slate-700 p-1 mx-1 mb-1"> {tag.tag_name}
{tag.tag_name} </span>
</span> ))}
))} </div>
</div> </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 />
<CarouselNext />
</Carousel>
{/* second carousel */}
<Carousel className="w-full ml-1 h-[100px] mt-5">
<CarouselContent className="flex space-x-1">
{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 basis-0"
/>
</CarouselItem>
))}
</CarouselContent>
</Carousel>
</div> </div>
<div id="sub-content" className="flex flex-row mt-5">
{/* image carousel */}
<div id="image-corousel" className="shrink-0 w-[700px] flex flex-col">
<Carousel className="w-full h-full ml-1">
<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" />
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
<Carousel className="w-full ml-1 h-[100px] mt-5"> <div id="stats" className="flex flex-col w-full mt-4 pl-12">
<CarouselContent className="flex space-x-1"> <div className="pl-5">
{carouselData.map((item, index) => ( <span>
<CarouselItem key={index} className="flex"> <h1 className="font-semibold text-xl md:text-4xl mt-8">${totalDealAmount}</h1>
<CarouselItem key={index} className="flex"> <p className="text-sm md:text-lg">
<DisplayFullImage src="/path/to/image.jpg" alt="Image description" width={300} height={200} /> {toPercentage(totalDealAmount, projectData?.target_investment)}% raised of $
</CarouselItem> {projectData?.target_investment} max goal
</CarouselItem> </p>
))} <Progress
</CarouselContent> value={toPercentage(totalDealAmount, projectData?.target_investment)}
</Carousel> className="w-[60%] h-3 mt-3"
</div> />
<div id="stats" className="flex flex-col w-full mt-4 pl-12"> </span>
<div className="pl-5"> <span>
<span> <h1 className="font-semibold text-4xl md:mt-8">
<h1 className="font-semibold text-xl md:text-4xl mt-8">${totalDealAmount}</h1> <p className="text-xl md:text-4xl">{dealList.length}</p>
<p className="text-sm md:text-lg"> </h1>
{toPercentage(totalDealAmount, projectData?.target_investment)}% raised of $ <p className="text-sm md:text-lg">Investors</p>
{projectData?.target_investment} max goal </span>
</p> <Separator decorative className="mt-3 w-3/4 ml-5" />
<Progress <span>
value={toPercentage(totalDealAmount, projectData?.target_investment)} <h1 className="font-semibold text-xl md:text-4xl mt-8 ml-5"></h1>
className="w-[60%] h-3 mt-3" {projectData?.investment_deadline ? (
/> <>
</span> <p className="text-xl md:text-4xl">{Math.floor(hourLeft)} hours</p>
<span> <p>Left to invest</p>
<h1 className="font-semibold text-4xl md:mt-8"> </>
<p className="text-xl md:text-4xl">{dealList.length}</p> ) : (
</h1> <p className="text-xl md:text-4xl">No deadline</p>
<p className="text-sm md:text-lg">Investors</p> )}
</span> </span>
<Separator decorative className="mt-3 w-3/4 ml-5" /> <Button className="mt-5 w-3/4 h-12">
<span> <Link href={`/invest/${params.id}`}>Invest in {projectData?.project_name}</Link>
<h1 className="font-semibold text-xl md:text-4xl mt-8 ml-5"></h1> </Button>
{projectData?.investment_deadline ? (
<>
<p className="text-xl md:text-4xl">{Math.floor(hourLeft)} hours</p>
<p>Left to invest</p>
</>
) : (
<p className="text-xl md:text-4xl">No deadline</p>
)}
</span>
<Button className="mt-5 w-3/4 h-12">
<Link href={`/invest/${params.id}`}>Invest in {projectData?.project_name}</Link>
</Button>
</div>
</div> </div>
</div> </div>
</div> </div>