maked fully responsive for all devices

This commit is contained in:
Saimon8420 2025-02-12 13:24:33 +06:00
parent 1ed96799a0
commit 0f1d2c2ea9
10 changed files with 95 additions and 20 deletions

54
src/ErrorBoundary.jsx Normal file
View 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;

View file

@ -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>
)
}

View file

@ -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}

View file

@ -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 (

View file

@ -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

View file

@ -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>
)}

View file

@ -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>

View file

@ -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 (

View file

@ -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"] });

View file

@ -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>