fix: correct image loading issue in ImageModal and optimize performance

This commit is contained in:
THIS ONE IS A LITTLE BIT TRICKY KRUB 2024-11-06 16:36:01 +07:00
parent 2b51f4d025
commit 9953aae4a4

View File

@ -2,14 +2,7 @@ import { useEffect, useState } from "react";
import { SubmitHandler, useForm, ControllerRenderProps } from "react-hook-form"; import { SubmitHandler, useForm, ControllerRenderProps } from "react-hook-form";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { MultipleOptionSelector } from "@/components/multipleSelector"; import { MultipleOptionSelector } from "@/components/multipleSelector";
import { import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { projectFormSchema } from "@/types/schemas/application.schema"; import { projectFormSchema } from "@/types/schemas/application.schema";
import { z } from "zod"; import { z } from "zod";
@ -17,19 +10,8 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient"; import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
import { Textarea } from "./ui/textarea"; import { Textarea } from "./ui/textarea";
import { import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
Command, import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { ChevronsUpDown, Check, X } from "lucide-react"; import { ChevronsUpDown, Check, X } from "lucide-react";
@ -39,30 +21,21 @@ type FieldType = ControllerRenderProps<any, "projectPhotos">;
interface ProjectFormProps { interface ProjectFormProps {
onSubmit: SubmitHandler<projectSchema>; onSubmit: SubmitHandler<projectSchema>;
} }
const ProjectForm = ({ const ProjectForm = ({ onSubmit }: ProjectFormProps & { onSubmit: SubmitHandler<projectSchema> }) => {
onSubmit,
}: ProjectFormProps & { onSubmit: SubmitHandler<projectSchema> }) => {
const form = useForm<projectSchema>({ const form = useForm<projectSchema>({
resolver: zodResolver(projectFormSchema), resolver: zodResolver(projectFormSchema),
defaultValues: {}, defaultValues: {},
}); });
let supabase = createSupabaseClient(); let supabase = createSupabaseClient();
const [projectType, setProjectType] = useState< const [projectType, setProjectType] = useState<{ id: number; name: string }[]>([]);
{ id: number; name: string }[]
>([]);
const [projectPitch, setProjectPitch] = useState("text"); const [projectPitch, setProjectPitch] = useState("text");
const [selectedImages, setSelectedImages] = useState<File[]>([]); const [selectedImages, setSelectedImages] = useState<File[]>([]);
const [projectPitchFile, setProjectPitchFile] = useState(""); const [projectPitchFile, setProjectPitchFile] = useState("");
const [tag, setTag] = useState<{ id: number; value: string }[]>([]); const [tag, setTag] = useState<{ id: number; value: string }[]>([]);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [selectedTag, setSelectedTag] = useState< const [selectedTag, setSelectedTag] = useState<{ id: number; value: string }[]>([]);
{ id: number; value: string }[]
>([]);
const handleFileChange = ( const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>, field: FieldType) => {
event: React.ChangeEvent<HTMLInputElement>,
field: FieldType
) => {
if (event.target.files) { if (event.target.files) {
const filesArray = Array.from(event.target.files); const filesArray = Array.from(event.target.files);
console.log("first file", filesArray); console.log("first file", filesArray);
@ -86,9 +59,7 @@ const ProjectForm = ({
}; };
const fetchProjectType = async () => { const fetchProjectType = async () => {
let { data: ProjectType, error } = await supabase let { data: ProjectType, error } = await supabase.from("project_type").select("id, value");
.from("project_type")
.select("id, value");
if (error) { if (error) {
console.error(error); console.error(error);
@ -125,10 +96,7 @@ const ProjectForm = ({
}, []); }, []);
return ( return (
<Form {...form}> <Form {...form}>
<form <form onSubmit={form.handleSubmit(onSubmit as SubmitHandler<projectSchema>)} className="space-y-8">
onSubmit={form.handleSubmit(onSubmit as SubmitHandler<projectSchema>)}
className="space-y-8"
>
<div className="ml-96 space-y-10"> <div className="ml-96 space-y-10">
{/* project name */} {/* project name */}
<FormField <FormField
@ -137,17 +105,10 @@ const ProjectForm = ({
render={({ field }: { field: any }) => ( render={({ field }: { field: any }) => (
<FormItem> <FormItem>
<div className="space-y-5"> <div className="space-y-5">
<FormLabel className="font-bold text-lg"> <FormLabel className="font-bold text-lg">Project name</FormLabel>
Project name
</FormLabel>
<FormControl> <FormControl>
<div className="flex space-x-5"> <div className="flex space-x-5">
<Input <Input type="text" id="projectName" className="w-96" {...field} />
type="text"
id="projectName"
className="w-96"
{...field}
/>
</div> </div>
</FormControl> </FormControl>
</div> </div>
@ -169,9 +130,7 @@ const ProjectForm = ({
handleFunction={(selectedValues: any) => { handleFunction={(selectedValues: any) => {
field.onChange(selectedValues.id); field.onChange(selectedValues.id);
}} }}
description={ description={<>Please specify the primary purpose of the funds</>}
<>Please specify the primary purpose of the funds</>
}
placeholder="Select a Project type" placeholder="Select a Project type"
selectLabel="Project type" selectLabel="Project type"
/> />
@ -189,18 +148,11 @@ const ProjectForm = ({
<FormItem> <FormItem>
<FormControl> <FormControl>
<div className="mt-10 space-y-5"> <div className="mt-10 space-y-5">
<FormLabel className="font-bold text-lg"> <FormLabel className="font-bold text-lg">Short description</FormLabel>
Short description
</FormLabel>
<div className="flex space-x-5"> <div className="flex space-x-5">
<Textarea <Textarea id="shortDescription" className="w-96" {...field} />
id="shortDescription"
className="w-96"
{...field}
/>
<span className="text-[12px] text-neutral-500 self-center"> <span className="text-[12px] text-neutral-500 self-center">
Could you provide a brief description of your project{" "} Could you provide a brief description of your project <br /> in one or two sentences?
<br /> in one or two sentences?
</span> </span>
</div> </div>
</div> </div>
@ -225,9 +177,7 @@ const ProjectForm = ({
<div className="flex space-x-2 w-96"> <div className="flex space-x-2 w-96">
<Button <Button
type="button" type="button"
variant={ variant={projectPitch === "text" ? "default" : "outline"}
projectPitch === "text" ? "default" : "outline"
}
onClick={() => setProjectPitch("text")} onClick={() => setProjectPitch("text")}
className="w-32 h-12 text-base" className="w-32 h-12 text-base"
> >
@ -235,9 +185,7 @@ const ProjectForm = ({
</Button> </Button>
<Button <Button
type="button" type="button"
variant={ variant={projectPitch === "file" ? "default" : "outline"}
projectPitch === "file" ? "default" : "outline"
}
onClick={() => setProjectPitch("file")} onClick={() => setProjectPitch("file")}
className="w-32 h-12 text-base" className="w-32 h-12 text-base"
> >
@ -247,11 +195,7 @@ const ProjectForm = ({
<div className="flex space-x-5"> <div className="flex space-x-5">
<Input <Input
type={projectPitch === "file" ? "file" : "text"} type={projectPitch === "file" ? "file" : "text"}
placeholder={ placeholder={projectPitch === "file" ? "Upload your Markdown file" : "https:// "}
projectPitch === "file"
? "Upload your Markdown file"
: "https:// "
}
accept={projectPitch === "file" ? ".md" : undefined} accept={projectPitch === "file" ? ".md" : undefined}
onChange={(e) => { onChange={(e) => {
const value = e.target; const value = e.target;
@ -266,11 +210,9 @@ const ProjectForm = ({
/> />
<span className="text-[12px] text-neutral-500 self-center"> <span className="text-[12px] text-neutral-500 self-center">
Please upload a file or paste a link to your pitch, Please upload a file or paste a link to your pitch, which should <br />
which should <br /> cover key aspects of your project: what it will do, what investors <br /> can expect to gain,
cover key aspects of your project: what it will do, and any highlights that make it stand out.
what investors <br /> can expect to gain, and any
highlights that make it stand out.
</span> </span>
</div> </div>
{projectPitchFile && ( {projectPitchFile && (
@ -302,23 +244,22 @@ const ProjectForm = ({
<FormItem> <FormItem>
<FormControl> <FormControl>
<div className="mt-10 space-y-5"> <div className="mt-10 space-y-5">
<FormLabel className="font-bold text-lg mt-10"> <FormLabel className="font-bold text-lg mt-10">Project logo</FormLabel>
Project logo <div className="flex space-x-5">
</FormLabel> <Input
<Input type="file"
type="file" id="projectLogo"
id="projectLogo" className="w-96"
className="w-96" accept="image/*"
accept="image/*" onChange={(e) => {
onChange={(e) => { const file = e.target.files?.[0];
const file = e.target.files?.[0]; field.onChange(file || "");
field.onChange(file || ""); }}
}} />
/> <span className="text-[12px] text-neutral-500 self-center">
<span className="text-[12px] text-neutral-500 self-center"> Please upload the logo picture that best represents your project.
Please upload the logo picture that best represents your </span>
project. </div>
</span>
</div> </div>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@ -334,9 +275,7 @@ const ProjectForm = ({
<FormItem> <FormItem>
<FormControl> <FormControl>
<div className="mt-10 space-y-5"> <div className="mt-10 space-y-5">
<FormLabel className="font-bold text-lg mt-10"> <FormLabel className="font-bold text-lg mt-10">Project photos</FormLabel>
Project photos
</FormLabel>
<div className="flex space-x-5"> <div className="flex space-x-5">
<Input <Input
type="file" type="file"
@ -349,16 +288,15 @@ const ProjectForm = ({
}} }}
/> />
<span className="text-[12px] text-neutral-500 self-center"> <span className="text-[12px] text-neutral-500 self-center">
Please upload the logo picture that best represents your Please upload the photo that best represents your project.
project. <p className="text-red-500">
*** It is recommended that the photo be horizontal for better presentation.
</p>
</span> </span>
</div> </div>
<div className="mt-5 space-y-2 w-96"> <div className="mt-5 space-y-2 w-96">
{selectedImages.map((image, index) => ( {selectedImages.map((image, index) => (
<div <div key={index} className="flex justify-between items-center border p-2 rounded">
key={index}
className="flex justify-between items-center border p-2 rounded"
>
<span>{image.name}</span> <span>{image.name}</span>
<Button <Button
variant="outline" variant="outline"
@ -385,9 +323,7 @@ const ProjectForm = ({
render={({ field }: { field: any }) => ( render={({ field }: { field: any }) => (
<FormItem> <FormItem>
<div className="mt-10 space-y-5"> <div className="mt-10 space-y-5">
<FormLabel className="font-bold text-lg"> <FormLabel className="font-bold text-lg">Minimum investment</FormLabel>
Minimum investment
</FormLabel>
<FormControl> <FormControl>
<div className="flex space-x-5"> <div className="flex space-x-5">
<Input <Input
@ -419,9 +355,7 @@ const ProjectForm = ({
render={({ field }: { field: any }) => ( render={({ field }: { field: any }) => (
<FormItem> <FormItem>
<div className="mt-10 space-y-5"> <div className="mt-10 space-y-5">
<FormLabel className="font-bold text-lg"> <FormLabel className="font-bold text-lg">Target investment</FormLabel>
Target investment
</FormLabel>
<FormControl> <FormControl>
<div className="flex space-x-5"> <div className="flex space-x-5">
<Input <Input
@ -437,8 +371,8 @@ const ProjectForm = ({
value={field.value} value={field.value}
/> />
<span className="text-[12px] text-neutral-500 self-center"> <span className="text-[12px] text-neutral-500 self-center">
We encourage you to set a specific target investment{" "} We encourage you to set a specific target investment <br /> amount that reflects your funding
<br /> amount that reflects your funding goals. goals.
</span> </span>
</div> </div>
</FormControl> </FormControl>
@ -457,16 +391,10 @@ const ProjectForm = ({
<FormLabel className="font-bold text-lg">Deadline</FormLabel> <FormLabel className="font-bold text-lg">Deadline</FormLabel>
<FormControl> <FormControl>
<div className="flex space-x-5"> <div className="flex space-x-5">
<Input <Input type="datetime-local" id="deadline" className="w-96" {...field} />
type="datetime-local"
id="deadline"
className="w-96"
{...field}
/>
<span className="text-[12px] text-neutral-500 self-center"> <span className="text-[12px] text-neutral-500 self-center">
What is the deadline for your fundraising project? What is the deadline for your fundraising project? Setting <br /> a clear timeline can help
Setting <br /> a clear timeline can help motivate motivate potential investors.
potential investors.
</span> </span>
</div> </div>
</FormControl> </FormControl>
@ -493,9 +421,7 @@ const ProjectForm = ({
aria-expanded={open} aria-expanded={open}
className="w-96 justify-between overflow-hidden text-ellipsis whitespace-nowrap" className="w-96 justify-between overflow-hidden text-ellipsis whitespace-nowrap"
> >
{selectedTag.length > 0 {selectedTag.length > 0 ? selectedTag.map((t) => t.value).join(", ") : "Select tags..."}
? selectedTag.map((t) => t.value).join(", ")
: "Select tags..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" /> <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
@ -511,15 +437,11 @@ const ProjectForm = ({
value={tag.value} value={tag.value}
onSelect={() => { onSelect={() => {
setSelectedTag((prev) => { setSelectedTag((prev) => {
const exists = prev.find( const exists = prev.find((t) => t.id === tag.id);
(t) => t.id === tag.id
);
const updatedTags = exists const updatedTags = exists
? prev.filter((t) => t.id !== tag.id) ? prev.filter((t) => t.id !== tag.id)
: [...prev, tag]; : [...prev, tag];
field.onChange( field.onChange(updatedTags.map((t) => t.id));
updatedTags.map((t) => t.id)
);
return updatedTags; return updatedTags;
}); });
setOpen(false); setOpen(false);
@ -528,9 +450,7 @@ const ProjectForm = ({
<Check <Check
className={cn( className={cn(
"h-4", "h-4",
selectedTag.some((t) => t.id === tag.id) selectedTag.some((t) => t.id === tag.id) ? "opacity-100" : "opacity-0"
? "opacity-100"
: "opacity-0"
)} )}
/> />
{tag.value} {tag.value}
@ -542,8 +462,7 @@ const ProjectForm = ({
</PopoverContent> </PopoverContent>
</Popover> </Popover>
<span className="text-[12px] text-neutral-500 self-center"> <span className="text-[12px] text-neutral-500 self-center">
Add 1 to 5 tags that describe your project. Tags help{" "} Add 1 to 5 tags that describe your project. Tags help <br />
<br />
investors understand your focus. investors understand your focus.
</span> </span>
</div> </div>
@ -561,9 +480,7 @@ const ProjectForm = ({
<button <button
onClick={() => { onClick={() => {
setSelectedTag((prev) => { setSelectedTag((prev) => {
const updatedTags = prev.filter( const updatedTags = prev.filter((t) => t.id !== tag.id);
(t) => t.id !== tag.id
);
field.onChange(updatedTags.map((t) => t.id)); field.onChange(updatedTags.map((t) => t.id));
return updatedTags; return updatedTags;
}); });
@ -579,10 +496,7 @@ const ProjectForm = ({
/> />
</div> </div>
<center> <center>
<Button <Button className="mt-12 mb-20 h-10 text-base font-bold py-6 px-5 " type="submit">
className="mt-12 mb-20 h-10 text-base font-bold py-6 px-5 "
type="submit"
>
Submit application Submit application
</Button> </Button>
</center> </center>