diff --git a/src/Home.jsx b/src/Home.jsx index 6b4a77f..8572ad9 100644 --- a/src/Home.jsx +++ b/src/Home.jsx @@ -11,35 +11,52 @@ import { useQuery } from '@tanstack/react-query'; import { generateToken } from './api/authApi'; import { useNavigate } from "react-router-dom"; import { Toaster } from '@/components/ui/toaster'; +import { getProjectById } from './api/projectApi'; +import { Loader } from 'lucide-react'; +import CanvasContext from './components/Context/canvasContext/CanvasContext'; export const Home = () => { const { activeObject } = useContext(ActiveObjectContext); + const { canvas, selectedPanel } = useContext(CanvasContext); const params = useParams(); - const getToken = localStorage.getItem('canvas_token'); - - const { id } = params; const navigate = useNavigate(); + const getToken = () => localStorage.getItem('canvas_token'); + + const { id } = params; + + // Fetch token only if it doesn't exist const { data, isLoading } = useQuery({ queryKey: ['get-token'], queryFn: () => generateToken(id), - enabled: !getToken - }) - console.log(data); + }); + + // Fetch project data only if token and id exist + const { data: projectData, isLoading: projectLoading } = useQuery({ + queryKey: ['project', id], + queryFn: async () => await getProjectById(id), + enabled: !!getToken() && !!id && selectedPanel === "project", + }); useEffect(() => { - if (!getToken && !isLoading) { + const token = getToken(); // Get latest token + + if (!token && !isLoading) { navigate("/unAuthenticated"); } - if (getToken && !isLoading && data?.status === 201) { + if (token && !isLoading && data?.status === 201) { navigate("/"); } - }, [getToken, navigate, isLoading, data]) + if (projectData?.status === 200 & !projectLoading && projectData?.data?.object && canvas && canvas?._objects?.length === 0 && selectedPanel === "project") { + canvas.loadFromJSON(projectData?.data?.object); + canvas.renderAll(); + } + }, [navigate, isLoading, data, projectData, projectLoading, canvas, selectedPanel]); return (
{ - isLoading &&
Loading...
+ isLoading &&

} {!isLoading && <> diff --git a/src/api/projectApi.ts b/src/api/projectApi.ts index 4289d2c..f4f2191 100644 --- a/src/api/projectApi.ts +++ b/src/api/projectApi.ts @@ -8,7 +8,7 @@ interface Project { export const getProjects = async () => { try { - const response = await fetch(`${import.meta.env.VITE_SERVER_URL}/projects`, { + const response = await fetch(`${import.meta.env.VITE_SERVER_URL}/projects/`, { method: 'GET', credentials: 'include', headers: { 'Content-Type': 'application/json' }, diff --git a/src/components/CanvasSetting.jsx b/src/components/CanvasSetting.jsx index c5c9c91..e27c25a 100644 --- a/src/components/CanvasSetting.jsx +++ b/src/components/CanvasSetting.jsx @@ -31,7 +31,12 @@ const CanvasSetting = () => { const createEmptyProject = async () => { try { const response = await createProject(); - console.log(response); + if (response?.status === 200) { + toast({ + title: response?.status, + description: response?.message + }) + } if (response?.data?.id) { navigate(`/${response.data.id}`); } @@ -42,7 +47,7 @@ const CanvasSetting = () => { if (!id) { createEmptyProject(); } - }, [id, navigate]); + }, [id, navigate, toast]); // upload bg-image handler const { mutate: uploadBackgroundImage } = useMutation({ diff --git a/src/components/EachComponent/UploadImage.jsx b/src/components/EachComponent/UploadImage.jsx index 753ab95..6f9960f 100644 --- a/src/components/EachComponent/UploadImage.jsx +++ b/src/components/EachComponent/UploadImage.jsx @@ -26,9 +26,9 @@ import ImageCustomization from "./Customization/ImageCustomization"; import { Separator } from "../ui/separator"; import ActiveObjectContext from "../Context/activeObject/ObjectContext"; import { useNavigate, useParams } from "react-router-dom"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { deleteImage, uploadImage } from "../../api/uploadApi"; -import { createProject } from "../../api/projectApi"; +import { createProject, getProjectById, updateProject } from "../../api/projectApi"; import { useToast } from "../../hooks/use-toast"; const UploadImage = () => { @@ -56,6 +56,12 @@ const UploadImage = () => { const createEmptyProject = async () => { try { const response = await createProject(); + if (response?.status === 200) { + toast({ + title: response?.status, + description: response?.message + }) + } if (response?.data?.id) { navigate(`/${response.data.id}`); } @@ -66,7 +72,38 @@ const UploadImage = () => { if (!id) { createEmptyProject(); } - }, [id, navigate]); + }, [id, navigate, toast]); + + const queryClient = useQueryClient(); + + // to get each canvas_project data + const { data: projectData } = useQuery({ + queryKey: ['project', id], + queryFn: async () => await getProjectById(id), + enabled: !!id, + }); + + console.log(projectData); + + // to update the project + const { mutate: projectUpdate } = useMutation({ + mutationFn: async ({ id, updateData }) => { + return await updateProject({ id, ...updateData }) + }, + onSuccess: (data) => { + if (data?.status === 200) { + // Invalidate a single query key + queryClient.invalidateQueries({ queryKey: ["project", id] }); + } + else { + toast({ + title: data?.status, + description: data?.message, + variant: "destructive" + }) + } + } + }); // upload image handler const { mutate } = useMutation({ @@ -94,6 +131,12 @@ const UploadImage = () => { }, { crossOrigin: "anonymous" } ); + if (canvas) { + const object = canvas.toJSON(['id', 'selectable']); // Include any custom properties you need + const updateData = { ...projectData?.data, object, preview_url: "" }; + // Wait for the project update before continuing + projectUpdate({ id, updateData }); + } } else { toast({ @@ -104,7 +147,7 @@ const UploadImage = () => { removeFile(); } } - }) + }); // handle image remove const { mutate: deleteMutate } = useMutation({ @@ -117,6 +160,12 @@ const UploadImage = () => { title: data?.status, description: data?.message }) + if (canvas) { + const object = canvas.toJSON(['id', 'selectable']); // Include any custom properties you need + const updateData = { ...projectData?.data, object }; + // Wait for the project update before continuing + projectUpdate({ id, updateData }); + } } else { toast({ diff --git a/src/components/Panel/EditorPanel.jsx b/src/components/Panel/EditorPanel.jsx index ef328b0..3180b83 100644 --- a/src/components/Panel/EditorPanel.jsx +++ b/src/components/Panel/EditorPanel.jsx @@ -12,6 +12,8 @@ import PositionPanel from "./PositionPanel"; import ImagePanel from "./ImagePanel"; import GroupObjectPanel from "./GroupObjectPanel"; import CanvasPanel from "./CanvasPanel"; +import { ProjectPanel } from "./ProjectPanel"; +import ImageLibrary from "./ImageLibrary"; const EditorPanel = () => { const { selectedPanel } = useContext(CanvasContext); @@ -42,6 +44,10 @@ const EditorPanel = () => { return ; case "canvas": return ; + case "project": + return ; + case "image": + return ; default: return; } diff --git a/src/components/Panel/ImageLibrary.jsx b/src/components/Panel/ImageLibrary.jsx new file mode 100644 index 0000000..f5a2e76 --- /dev/null +++ b/src/components/Panel/ImageLibrary.jsx @@ -0,0 +1,28 @@ +import { useContext } from 'react' +import CanvasContext from '../Context/canvasContext/CanvasContext'; +import { Button } from '../ui/button'; +import { X } from 'lucide-react'; +import { ScrollArea } from '../ui/scroll-area'; +const ImageLibrary = () => { + const { setSelectedPanel } = useContext(CanvasContext); + return ( +
+
+

Image Library

+ +
+ + + {/* Image library content goes here */} + +
+ ); +} + +export default ImageLibrary \ No newline at end of file diff --git a/src/components/Panel/ProjectPanel.jsx b/src/components/Panel/ProjectPanel.jsx new file mode 100644 index 0000000..7ce1176 --- /dev/null +++ b/src/components/Panel/ProjectPanel.jsx @@ -0,0 +1,119 @@ +import { useContext } from 'react'; +import CanvasContext from '../Context/canvasContext/CanvasContext'; +import { Button } from '../ui/button'; +import { Loader, Trash, X } from 'lucide-react'; +import { ScrollArea } from '../ui/scroll-area'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { deleteProject, getProjects } from '@/api/projectApi'; +import { useNavigate } from 'react-router-dom'; +import { toast } from '@/hooks/use-toast'; +import ActiveObjectContext from '../Context/activeObject/ObjectContext'; +import { FixedSizeList as List } from 'react-window'; // Import FixedSizeList from react-window + +export const ProjectPanel = () => { + const { setSelectedPanel, canvas } = useContext(CanvasContext); + const { setActiveObject } = useContext(ActiveObjectContext); + const navigate = useNavigate(); + const queryClient = useQueryClient(); // Initialize query client + + const { data: projects, isLoading: projectLoading, isSuccess: projectSuccess } = useQuery({ + queryKey: ['projects'], + queryFn: getProjects, // Simplified function call + }); + + // To delete a project + const { mutate: projectDelete, isPending: deletePending } = useMutation({ + mutationFn: async (id) => await deleteProject(id), + onSuccess: (data, id) => { + if (data?.status === 200) { + toast({ + title: data?.status, + description: data?.message, + }); + + // Invalidate queries to refresh data + queryClient.invalidateQueries({ queryKey: ["projects"] }); + queryClient.invalidateQueries({ queryKey: ["project", id] }); + + // Clear canvas if it exists + if (canvas) { + canvas.clear(); + canvas.renderAll(); + canvas.setBackgroundColor("#ffffff", canvas.renderAll.bind(canvas)); + } + setActiveObject(null); + + setSelectedPanel(""); + navigate("/"); + + } else { + toast({ + title: data?.status, + description: data?.message, + variant: "destructive" + }); + } + }, + onError: (error) => { + toast({ + title: "Error", + description: error.message || "Failed to delete the project", + variant: "destructive", + }); + } + }); + + // Row renderer for react-window + const Row = ({ index, style }) => { + const project = projects?.data?.[index]; // Get project by index + + return ( +
+
navigate(`/${project.id}`)}> +

{project?.name}

+

{project?.description}

+ each_project +
+ + +
+ ); + }; + + return ( +
+
+

Projects

+ +
+ + + { + projectLoading &&

+ } + { + !projectLoading && projectSuccess && projects?.status === 200 && + + {Row} + + } + { + projects?.status !== 200 &&

{projects?.message}

+ } +
+
+ ); +}; diff --git a/src/components/SaveCanvas.jsx b/src/components/SaveCanvas.jsx index 973363e..eb1ac24 100644 --- a/src/components/SaveCanvas.jsx +++ b/src/components/SaveCanvas.jsx @@ -49,16 +49,18 @@ const SaveCanvas = () => { title: data?.status, description: data?.message, }) - console.log(data); // Invalidate a single query key queryClient.invalidateQueries({ queryKey: ["project", id] }); - // to load the object into canvas - // canvas.loadFromJSON(data?.data?.object, () => { - // canvas.renderAll(); - // }, (err) => { - // console.error('Error loading object:', err); - // }); + // Clear canvas if it exists + if (canvas) { + canvas.clear(); + canvas.renderAll(); + canvas.setBackgroundColor("#ffffff", canvas.renderAll.bind(canvas)); + } + setActiveObject(null); + setSelectedPanel(""); + navigate("/"); } else { toast({ @@ -81,15 +83,19 @@ const SaveCanvas = () => { title: data?.status, description: data?.message, }) + // Clear canvas if it exists + if (canvas) { + canvas.clear(); + canvas.renderAll(); + canvas.setBackgroundColor("#ffffff", canvas.renderAll.bind(canvas)); + } + setActiveObject(null); + // Invalidate a single query key queryClient.invalidateQueries({ queryKey: ["project", id] }); setSelectedPanel(""); navigate("/"); - // for clear canvas - canvas.clear(); - canvas.renderAll(); - canvas.setBackgroundColor("#ffffff", canvas.renderAll.bind(canvas)); - setActiveObject(null); + } else { toast({ @@ -179,19 +185,14 @@ const SaveCanvas = () => { if (saveCanvas?.preview_url) { try { removeCanvasImage(saveCanvas?.preview_url); - console.log("Image removed", saveCanvas?.preview_url); } catch (error) { console.error("Error removing image:", error); } } else { try { - console.log("Capturing image..."); const file = await captureImage(); - console.log("Captured file:", file); - if (file) { uploadCanvasImage({ file, id }); - console.log("Image uploaded successfully"); } } catch (error) { console.error("Error capturing image:", error); @@ -270,13 +271,18 @@ const SaveCanvas = () => { return (
-

Save Canvas

-
- { - saveCanvas?.preview_url && - - } -
+

Save Canvas

+ + { + saveCanvas?.preview_url && +
+ { + saveCanvas?.preview_url && + + } +
+ } +