maked fully responsive for all devices
This commit is contained in:
parent
1ed96799a0
commit
0f1d2c2ea9
10 changed files with 95 additions and 20 deletions
54
src/ErrorBoundary.jsx
Normal file
54
src/ErrorBoundary.jsx
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { AlertCircle, RefreshCw } from "lucide-react";
|
||||
import { Component } from "react";
|
||||
|
||||
class GlobalErrorBoundary extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hasError: false, error: null };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error) {
|
||||
return { hasError: true, error };
|
||||
}
|
||||
|
||||
componentDidCatch(error, errorInfo) {
|
||||
console.error("Global Error Caught:", error, errorInfo);
|
||||
// You can log this to an external service like Sentry or LogRocket
|
||||
}
|
||||
|
||||
handleRetry = () => {
|
||||
this.setState({ hasError: false, error: null });
|
||||
window.location.reload(); // Reload the app
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-screen bg-background text-foreground p-6">
|
||||
<div className="w-full max-w-md">
|
||||
<div className="bg-card text-card-foreground rounded-lg shadow-lg p-8">
|
||||
<div className="flex items-center justify-center mb-6">
|
||||
<AlertCircle className="w-16 h-16 text-destructive" />
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-primary-text text-center mb-4">Oops! Something went wrong</h1>
|
||||
<p className="text-muted-foreground text-center mb-6">
|
||||
{this.state.error?.message || "An unexpected error occurred."}
|
||||
</p>
|
||||
<button
|
||||
onClick={this.handleRetry}
|
||||
className="w-full flex items-center justify-center px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors"
|
||||
>
|
||||
<RefreshCw className="w-5 h-5 mr-2" />
|
||||
Try Again
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export default GlobalErrorBoundary;
|
||||
29
src/Home.jsx
29
src/Home.jsx
|
|
@ -18,7 +18,7 @@ import { useToast } from './hooks/use-toast';
|
|||
import useProject from './hooks/useProject';
|
||||
|
||||
export const Home = () => {
|
||||
const { activeObject } = useContext(ActiveObjectContext);
|
||||
const { activeObject, setActiveObject } = useContext(ActiveObjectContext);
|
||||
const { canvas, selectedPanel } = useContext(CanvasContext);
|
||||
const { user, setUser } = useContext(AuthContext);
|
||||
const params = useParams();
|
||||
|
|
@ -62,11 +62,11 @@ export const Home = () => {
|
|||
if (token && !isLoading && data?.status === 201) {
|
||||
navigate("/");
|
||||
}
|
||||
if (projectData?.status === 404 || projectData?.status === 500) {
|
||||
if (projectData?.status === 500 && id) {
|
||||
navigate("/");
|
||||
toast({ variant: "destructive", title: projectData?.status, description: "No project found" });
|
||||
}
|
||||
if (projectData?.status === 200 & !projectLoading && projectData?.data?.object && canvas && selectedPanel === "project" && id) {
|
||||
if (projectData && projectData?.status === 200 && !projectLoading && projectData?.data?.object && canvas && (selectedPanel === "project" || selectedPanel === "") && id) {
|
||||
const isEmpty = (obj) => Object.values(obj).length === 0;
|
||||
if (!isEmpty(projectData?.data?.object)) {
|
||||
canvas.loadFromJSON(projectData?.data?.object);
|
||||
|
|
@ -75,8 +75,18 @@ export const Home = () => {
|
|||
}
|
||||
}, [navigate, isLoading, data, projectData, projectLoading, canvas, selectedPanel, id, toast]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectData && canvas) {
|
||||
canvas.clear();
|
||||
canvas.renderAll();
|
||||
canvas.setBackgroundColor("#ffffff", canvas.renderAll.bind(canvas));
|
||||
|
||||
setActiveObject(null);
|
||||
}
|
||||
}, [projectData, canvas, setActiveObject])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='relative flex flex-col'>
|
||||
{
|
||||
isLoading &&
|
||||
<div className='flex justify-center items-center h-screen'>
|
||||
|
|
@ -91,20 +101,20 @@ export const Home = () => {
|
|||
|
||||
{
|
||||
activeObject &&
|
||||
<div className="absolute left-[90px] right-[90px] z-[9999] rounded-md p-1 h-fit bg-white border-t border-gray-200 shadow-md my-1 w-[80%] mx-auto">
|
||||
<div className="absolute left-0 xl:left-[90px] lg:left-[90px] md:left-[90px] sm:left-[80px] right-0 xl:right-[90px] lg:right-[90px] md:right-[90px] sm:right-[80px] z-[9999] rounded-md p-1 h-fit bg-white border-t border-gray-200 shadow-md my-1 w-[80%] mx-auto">
|
||||
<TopBar />
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className="absolute z-[999] right-0 bottom-0 flex justify-center items-center h-20 bg-white border-t border-gray-200 shadow-md w-fit">
|
||||
<div className="absolute z-[999] right-0 xl:bottom-0 lg:bottom-0 md:bottom-0 sm:bottom-0 bottom-10 flex justify-center items-center h-20 bg-white border-t border-gray-200 shadow-md w-fit">
|
||||
<ActionButtons />
|
||||
</div>
|
||||
|
||||
<div className="h-full mr-1">
|
||||
<div className="h-full mr-1 hidden xl:block lg:block md:block sm:block">
|
||||
<Sidebar />
|
||||
</div>
|
||||
|
||||
<div className="absolute ml-20 z-[999] top-[15%]">
|
||||
<div className="absolute ml-0 xl:ml-20 lg:ml-20 md:ml-20 z-[999] top-[15%]">
|
||||
<EditorPanel />
|
||||
</div>
|
||||
|
||||
|
|
@ -118,6 +128,9 @@ export const Home = () => {
|
|||
</>
|
||||
}
|
||||
</div>
|
||||
<div className='h-full z-[999] block xl:hidden lg:hidden md:hidden sm:hidden'>
|
||||
<Sidebar />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ export default function Canvas() {
|
|||
>
|
||||
<AspectRatio
|
||||
ratio={getRatioValue(canvasRatio)}
|
||||
className="rounded-lg border-0 border-primary/10 transition-all duration-300 ease-in-out"
|
||||
className="rounded-lg border-0 border-primary/10 transition-all duration-300 ease-in-out cursor-pointer"
|
||||
>
|
||||
<div
|
||||
ref={canvasRef}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const sidebarItems = [
|
|||
export function Sidebar() {
|
||||
const { selectedPanel, setSelectedPanel } = useContext(CanvasContext);
|
||||
return (
|
||||
<div className="w-20 border-r bg-background flex flex-col items-center justify-center py-4 gap-6 md:pt-16 xl:pt-0 h-full">
|
||||
<div className="w-full xl:w-20 lg:w-20 md:w-20 sm:w-20 border-r bg-background flex xl:flex-col lg:flex-col md:flex-col sm:flex-col items-center justify-center py-4 gap-6 md:pt-16 xl:pt-0 h-full overflow-x-scroll">
|
||||
{sidebarItems.map((item) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const Unauthenticated = () => {
|
|||
Sorry, you need to be authenticated to access this page. Please log in to continue.
|
||||
</p>
|
||||
<a
|
||||
href="https://dashboard.planpostai.com/login"
|
||||
href="https://dashboard.planpostai.com"
|
||||
className="inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background bg-primary text-primary-foreground hover:bg-primary/90 h-10 py-2 px-4"
|
||||
>
|
||||
Go Back to Dashboard
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ const EditorPanel = () => {
|
|||
return (
|
||||
<>
|
||||
{selectedPanel !== "" && (
|
||||
<div className="w-80 h-[450px] bg-background rounded-xl shadow-lg mx-4 my-auto overflow-y-scroll scrollbar-hide">
|
||||
<div className="w-screen xl:w-80 lg:w-80 md:w-80 sm:w-80 h-[450px] bg-background rounded-xl shadow-lg mx-0 xl:mx-4 lg:mx-4 md:mx-4 sm:mx-4 my-auto overflow-y-scroll scrollbar-hide">
|
||||
{renderPanel()}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,15 @@ import { Loader, Trash, X } from 'lucide-react';
|
|||
import { ScrollArea } from '../ui/scroll-area';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { getProjects } from '@/api/projectApi';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import useProject from '@/hooks/useProject';
|
||||
|
||||
export const ProjectPanel = () => {
|
||||
const { setSelectedPanel } = useContext(CanvasContext);
|
||||
|
||||
const params = useParams();
|
||||
const { id } = params;
|
||||
|
||||
const navigate = useNavigate();
|
||||
const queryClient = useQueryClient(); // Initialize query client
|
||||
|
||||
|
|
@ -55,7 +58,7 @@ export const ProjectPanel = () => {
|
|||
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"
|
||||
className={`rounded-md flex p-1 flex-col gap-1 bg-red-50 hover:bg-red-100 cursor-pointer transition-all ${project?.id === id ? "border-2 border-red-200" : ""} `}
|
||||
onClick={() => handleNavigate(project.id)}
|
||||
>
|
||||
<p className="font-bold text-sm truncate">{project?.name}</p>
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@ import { Input } from './ui/input';
|
|||
import { Label } from './ui/label';
|
||||
import { Save, Trash2 } from 'lucide-react';
|
||||
import { Textarea } from './ui/textarea';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { Button } from './ui/button';
|
||||
import { Separator } from './ui/separator';
|
||||
import { deleteImage, uploadImage } from '@/api/uploadApi';
|
||||
import useProject from '@/hooks/useProject';
|
||||
import { captureCanvas } from '@/lib/captureCanvas';
|
||||
import useCanvasCapture from '@/hooks/useCanvasCapture';
|
||||
|
|
@ -90,7 +88,7 @@ const SaveCanvas = () => {
|
|||
}
|
||||
|
||||
const handleDeleteProject = () => {
|
||||
projectDelete();
|
||||
projectDelete(id);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const useProject = () => {
|
|||
// Fetch project data
|
||||
const { data: projectData, isLoading } = useQuery({
|
||||
queryKey: ["project", id],
|
||||
queryFn: async () => await getProjectById(id),
|
||||
queryFn: () => getProjectById(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
|
||||
|
|
@ -47,21 +47,25 @@ const useProject = () => {
|
|||
},
|
||||
onSuccess: (data) => {
|
||||
if (data?.status === 200) {
|
||||
|
||||
if (selectedPanel === "canvas") {
|
||||
toast({
|
||||
title: data?.status,
|
||||
description: data?.message,
|
||||
})
|
||||
setSelectedPanel("");
|
||||
queryClient.invalidateQueries({ queryKey: ["project", id] });
|
||||
queryClient.invalidateQueries({ queryKey: ["projects"] });
|
||||
if (canvas) {
|
||||
canvas.clear();
|
||||
canvas.renderAll();
|
||||
canvas.setBackgroundColor("#ffffff", canvas.renderAll.bind(canvas));
|
||||
setActiveObject(null);
|
||||
navigate("/");
|
||||
setSelectedPanel("");
|
||||
}
|
||||
setActiveObject(null);
|
||||
navigate("/");
|
||||
setSelectedPanel("");
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: ["project", id] });
|
||||
queryClient.invalidateQueries({ queryKey: ["projects"] });
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import OpenContextProvider from "./components/Context/openContext/OpenContextPro
|
|||
import AuthContextProvider from "./components/Context/authContext/AuthProvider";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import GlobalErrorBoundary from "./ErrorBoundary";
|
||||
|
||||
// Create a client
|
||||
const queryClient = new QueryClient();
|
||||
|
|
@ -21,7 +22,9 @@ createRoot(document.getElementById("root")).render(
|
|||
<OpenContextProvider>
|
||||
<BrowserRouter>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<GlobalErrorBoundary>
|
||||
<App />
|
||||
</GlobalErrorBoundary>
|
||||
</QueryClientProvider>
|
||||
</BrowserRouter>
|
||||
</OpenContextProvider>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue