canvas-backend/src/components/Panel/ProjectPanel.jsx
2025-02-09 11:36:11 +06:00

129 lines
4.9 KiB
JavaScript

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';
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",
});
}
});
const Row = ({ index, style }) => {
const project = filteredProjects[index];
return (
<div key={project?.id} className="flex flex-col gap-1 p-1 rounded-md border">
<div
className="rounded-md flex p-1 flex-col gap-1 bg-red-50 hover:bg-red-100 cursor-pointer transition-all"
onClick={() => navigate(`/${project.id}`)}
>
<p className="font-bold text-sm truncate">{project?.name}</p>
<p className="text-xs truncate">{project?.description} </p>
<img className="rounded-md" src={project?.preview_url} alt="each_project" />
</div>
<Button
disabled={deletePending}
className="w-fit p-1 ml-auto" size="small"
onClick={() => { projectDelete(project?.id) }}
>
<Trash className="h-4 w-4" />
</Button>
</div>
);
};
// Filter projects where preview_url is not empty or null
const filteredProjects = projects?.data?.filter(project => project?.preview_url) || [];
return (
<div>
<div className="flex justify-between items-center p-4 border-b">
<h2 className="text-lg font-semibold">Projects</h2>
<Button
variant="ghost"
size="icon"
onClick={() => setSelectedPanel("")}
>
<X className="h-4 w-4" />
</Button>
</div>
<ScrollArea className="md:h-[calc(90vh-190px)] px-4 py-4">
{
projectLoading && <p><Loader className="animate-spin mx-auto" /></p>
}
{
!projectLoading && projectSuccess && projects?.status === 200 &&
<List
height={400} // Set the height of the viewport
itemCount={filteredProjects.length} // Use length of filtered projects
itemSize={280} // Height of each row
width="100%" // Full width of the container
>
{Row}
</List>
}
{
projects?.status !== 200 && <p>{projects?.message}</p>
}
</ScrollArea>
</div>
);
};