fix: Presentation page responsive issue

This commit is contained in:
shiva raj badu 2026-03-27 14:26:32 +05:45
parent f112934662
commit 76841ae4ce
No known key found for this signature in database
12 changed files with 77 additions and 62 deletions

View file

@ -1,5 +1,5 @@
'use client'
import React from "react";
import React, { useEffect } from "react";
import { Card } from "@/components/ui/card";
import { DashboardApi } from "@/app/(presentation-generator)/services/api/dashboard";
@ -12,7 +12,7 @@ import {
import { useRouter } from "next/navigation";
import { toast } from "sonner";
import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoader";
import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoad";
import SlideScale from "@/app/(presentation-generator)/components/PresentationRender";
import MarkdownRenderer from "@/components/MarkDownRender";
@ -28,11 +28,52 @@ export const PresentationCard = ({
onDeleted?: (presentationId: string) => void;
}) => {
const router = useRouter();
useFontLoader(presentation.fonts || []);
const handlePreview = (e: React.MouseEvent) => {
e.preventDefault();
router.push(`/presentation?id=${id}&type=standard`);
};
useEffect(() => {
applyTheme(presentation.theme)
}, [])
const applyTheme = async (theme: any) => {
const element = document.getElementById(`dashboard-presentation-card-${id}`)
if (!element) return;
if (!theme || !theme.data || !theme.data.colors['graph_0']) { return; }
const cssVariables = {
'--primary-color': theme.data.colors['primary'],
'--background-color': theme.data.colors['background'],
'--card-color': theme.data.colors['card'],
'--stroke': theme.data.colors['stroke'],
'--primary-text': theme.data.colors['primary_text'],
'--background-text': theme.data.colors['background_text'],
'--graph-0': theme.data.colors['graph_0'],
'--graph-1': theme.data.colors['graph_1'],
'--graph-2': theme.data.colors['graph_2'],
'--graph-3': theme.data.colors['graph_3'],
'--graph-4': theme.data.colors['graph_4'],
'--graph-5': theme.data.colors['graph_5'],
'--graph-6': theme.data.colors['graph_6'],
'--graph-7': theme.data.colors['graph_7'],
'--graph-8': theme.data.colors['graph_8'],
'--graph-9': theme.data.colors['graph_9'],
}
Object.entries(cssVariables).forEach(([key, value]) => {
element.style.setProperty(key, value)
})
//
if (theme.data.fonts.textFont.url && theme.data.fonts.textFont.name) {
useFontLoader({ [theme.data.fonts.textFont.name]: theme.data.fonts.textFont.url })
}
// Apply fonts to preview container
element.style.setProperty('font-family', `"${theme.data.fonts.textFont.name}"`)
element.style.setProperty('--heading-font-family', `"${theme.data.fonts.textFont.name}"`)
element.style.setProperty('--body-font-family', `"${theme.data.fonts.textFont.name}"`)
}
const handleDelete = async (e: React.MouseEvent) => {
e.preventDefault();
@ -59,7 +100,9 @@ export const PresentationCard = ({
onClick={handlePreview}
className="bg-[#F8FBFB] font-syne shadow-none sm:shadow-none presentation-card rounded-[12px] p-0 group hover:shadow-md transition-all duration-500 slide-theme cursor-pointer overflow-hidden flex flex-col"
>
<div suppressHydrationWarning={true} className="flex flex-col flex-1 relative z-40">
<div
id={`dashboard-presentation-card-${id}`}
suppressHydrationWarning={true} className="flex flex-col flex-1 relative z-40">
{/* <p className=" text-xs font-syne absolute top-2 flex gap-1 capitalize items-center left-2 rounded-[100px] px-2.5 py-1 bg-[#3A3A3AF5] text-white font-semibold z-40 ">
{presentation.type}

View file

@ -12,7 +12,7 @@ interface EditableLayoutWrapperProps {
slideData: any;
isEditMode?: boolean;
properties?: any;
}
interface EditableElement {
@ -29,7 +29,7 @@ const EditableLayoutWrapper: React.FC<EditableLayoutWrapperProps> = ({
slideIndex,
slideData,
properties,
}) => {
const dispatch = useDispatch();
const containerRef = useRef<HTMLDivElement>(null);
@ -245,7 +245,7 @@ const EditableLayoutWrapper: React.FC<EditableLayoutWrapperProps> = ({
}
}
});
// Process SVG icons
svgElements.forEach((svg, index) => {
const svgEl = svg as SVGElement;

View file

@ -67,7 +67,7 @@ const ImageEditor = ({
(properties &&
properties[imageIdx] &&
properties[imageIdx].initialObjectFit) ||
"cover"
"cover"
);
// Refs
@ -104,7 +104,7 @@ const ImageEditor = ({
console.error("error in getting previous generated images", error);
setError(
error.message ||
"Failed to get previous generated images. Please try again."
"Failed to get previous generated images. Please try again."
);
}
};
@ -233,7 +233,7 @@ const ImageEditor = ({
trackEvent(MixpanelEvent.ImageEditor_UploadImage_API_Call);
const result = await ImagesApi.uploadImage(file);
setUploadedImageUrl(result.file_url || result.path);
} catch (err:any) {
} catch (err: any) {
setUploadError("Failed to upload image. Please try again.");
toast.error(err.message || "Failed to upload image. Please try again.");
console.log("Upload error:", err.message);
@ -247,7 +247,7 @@ const ImageEditor = ({
setUploadedImagesLoading(true);
const result = await ImagesApi.getUploadedImages();
setUploadedImages(result);
} catch (err:any) {
} catch (err: any) {
toast.error(err.message || "Failed to get uploaded images. Please try again.");
console.log("Get uploaded images error:", err.message);
} finally {
@ -265,7 +265,7 @@ const ImageEditor = ({
const result = await ImagesApi.deleteImage(image_id);
setUploadedImages(uploadedImages.filter((image) => image.id !== image_id));
toast.success(result.message || "Image deleted successfully");
} catch (err:any) {
} catch (err: any) {
toast.error(err.message || "Failed to delete image. Please try again.");
}
};
@ -480,12 +480,12 @@ const ImageEditor = ({
}
className="cursor-pointer group aspect-[4/3] rounded-lg overflow-hidden relative border border-gray-200"
>
<Trash className="absolute group-hover:opacity-100 opacity-0 transition-opacity z-10 w-4 h-4 top-2 right-2 text-red-500" onClick={(e) =>{
<Trash className="absolute group-hover:opacity-100 opacity-0 transition-opacity z-10 w-4 h-4 top-2 right-2 text-red-500" onClick={(e) => {
e.stopPropagation();
handleDeleteImage(image.id)
}}/>
}} />
<img
src={image.file_url || image.path}
src={image.file_url || image.path}
alt="Uploaded preview"
className="w-full h-full object-cover group-hover:scale-105 transition-transform"
/>
@ -496,7 +496,7 @@ const ImageEditor = ({
</span>
</div>
</div>
</div>
))
)}
@ -578,7 +578,7 @@ const ImageEditor = ({
variant="outline"
className={cn(
objectFit === "cover" &&
"bg-blue-50 border-blue-500"
"bg-blue-50 border-blue-500"
)}
onClick={() => handleFitChange("cover")}
>
@ -588,7 +588,7 @@ const ImageEditor = ({
variant="outline"
className={cn(
objectFit === "contain" &&
"bg-blue-50 border-blue-500"
"bg-blue-50 border-blue-500"
)}
onClick={() => handleFitChange("contain")}
>
@ -607,7 +607,7 @@ const ImageEditor = ({
</div>
}
{/* Focus Point */}
{}
{ }
</div>
</TabsContent>
</Tabs>

View file

@ -6,7 +6,7 @@ import { V1ContentRender } from '../../(presentation-generator)/components/V1Con
const BASE_WIDTH = 1280;
const BASE_HEIGHT = 720;
const SlideScale = ({ slide }: { slide: any }) => {
const SlideScale = ({ slide, theme }: { slide: any, theme?: any }) => {
const containerRef = useRef<HTMLDivElement | null>(null);
const [containerWidth, setContainerWidth] = useState<number>(0);
@ -62,12 +62,12 @@ const SlideScale = ({ slide }: { slide: any }) => {
} as React.CSSProperties}
>
<div
{/* <div
className="absolute inset-0 bg-transparent z-30 w-full h-full select-none"
aria-hidden="true"
/>
<V1ContentRender slide={slide} isEditMode={true} />
/> */}
<V1ContentRender slide={slide} isEditMode={true} theme={theme} />
</div>

View file

@ -1,16 +0,0 @@
export const useFontLoader = ( fonts:string[]) => {
const injectFonts = (fontUrls: string[]) => {
fontUrls.forEach((fontUrl) => {
if (!fontUrl) return;
let newFontUrl = fontUrl.includes('fonts.googleapis') ? fontUrl : `https://localhost:5000${fontUrl}`;
const existingStyle = document.querySelector(`style[data-font-url="${newFontUrl}"]`);
if (existingStyle) return;
const style = document.createElement("style");
style.setAttribute("data-font-url", newFontUrl);
style.textContent = `@import url('${newFontUrl}');`;
document.head.appendChild(style);
});
};
injectFonts(fonts);
};

View file

@ -57,18 +57,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
</span>
</div>
)}
{/* <div className="flex items-center justify-between">
<h5 className="text-lg font-medium">
Presentation Outline
</h5>
{isStreaming && (
<div className="flex items-center text-sm text-blue-600">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600 mr-2"></div>
Generating outlines...
</div>
)}
</div> */}
{/* Skeleton loading state */}
{isLoading && (
<div className="space-y-4 bg-white">
{[...Array(6)].map((_, index) => (
@ -93,7 +82,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
{/* Outlines content */}
{outlines && outlines.length > 0 && (
<div className="bg-[#F9F8F8] p-7 relative z-20 rounded-[20px]">
<div className="bg-[#F9F8F8] p-7 relative z-20 rounded-[20px] min-h-[calc(100vh-200px)]">
<DndContext
sensors={sensors}
collisionDetection={closestCenter}

View file

@ -246,7 +246,7 @@ const PresentationHeader = ({
return (
<>
<div className="py-7 sticky top-0 bg-white z-50 mb-[17px] font-syne flex justify-between items-center">
<h2 className="text-lg text-[#101323] font-unbounded "><MarkdownRenderer content={presentationData?.title || "Presentation"} className="mb-0 w-[600px] truncate text-sm text-[#101323] " /></h2>
<h2 className="text-lg text-[#101323] font-unbounded "><MarkdownRenderer content={presentationData?.title || "Presentation"} className="mb-0 max-w-[600px] overflow-ellipsis line-clamp-1 text-sm text-[#101323] " /></h2>
<div className="flex items-center gap-2.5">
{isPresentationSaving && <div className="flex items-center gap-2">

View file

@ -132,7 +132,7 @@ const SidePanel = ({
<Separator orientation="horizontal" className="my-6 " />
<div
className={`
fixed xl:relative h-full z-50 xl:z-auto
relative bg-[#F6F6F9] h-full z-50 xl:z-auto
transition-all duration-300 ease-in-out
`}
>
@ -141,14 +141,14 @@ const SidePanel = ({
className="w-full h-[calc(100vh-120px)] hide-scrollbar overflow-hidden slide-theme "
>
<p className="text-xl font-normal pb-3.5 text-[#000000]">Slides</p>
<p className="text-xl font-normal pb-3.5 text-[#000000]">Slides ({presentationData?.slides?.length})</p>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<div className=" overflow-y-auto hide-scrollbar h-[calc(100%-140px)] space-y-3.5">
<div className=" overflow-y-auto w-full hide-scrollbar h-[calc(100%-140px)] space-y-3.5">
{isStreaming ? (
presentationData &&
presentationData?.slides.map((slide: any, index: number) => (

View file

@ -21,6 +21,7 @@ import { trackEvent, MixpanelEvent } from "@/utils/mixpanel";
import { addToHistory } from "@/store/slices/undoRedoSlice";
import { V1ContentRender } from "../../components/V1ContentRender";
import NewSlide from "./NewSlide";
import SlideScale from "../../components/PresentationRender";
interface SlideContentProps {
slide: any;
@ -145,7 +146,8 @@ const SlideContent = ({ slide, index, presentationId }: SlideContentProps) => {
data-group={slide.layout_group}
className={` w-full group font-syne `}
>
<V1ContentRender slide={slide} isEditMode={true} theme={null} />
{/* <V1ContentRender slide={slide} isEditMode={true} theme={null} /> */}
<SlideScale slide={slide} theme={presentationData?.theme || null} />
{!showNewSlideSelection && (
<div className="group-hover:opacity-100 hidden md:block opacity-0 transition-opacity my-4 duration-300">
<ToolTip content="Add new slide below">

View file

@ -13,8 +13,6 @@ interface SortableSlideProps {
const SCALE = 0.125;
export function SortableSlide({ slide, index, selectedSlide, onSlideClick }: SortableSlideProps) {
const searchParams = useSearchParams();
const type = searchParams.get("type") as 'standard' | 'smart';
const lastClickTime = useRef(0);
const {
attributes,

View file

@ -12,7 +12,7 @@ import {
import { useRouter } from "next/navigation";
import { toast } from "sonner";
import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoader";
import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoad";
import SlideScale from "@/app/(presentation-generator)/components/PresentationRender";
import MarkdownRenderer from "@/components/MarkDownRender";
@ -28,7 +28,7 @@ export const PresentationCard = ({
onDeleted?: (presentationId: string) => void;
}) => {
const router = useRouter();
useFontLoader(presentation.fonts || []);
const handlePreview = (e: React.MouseEvent) => {
e.preventDefault();
router.push(`/presentation?id=${id}&type=standard`);

View file

@ -5,7 +5,6 @@ import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { ArrowLeft, Home, Loader2, Trash2 } from "lucide-react";
import { useFontLoader } from "../../hooks/useFontLoader";
import { MixpanelEvent, trackEvent } from "@/utils/mixpanel";
import TemplateService from "../../services/api/template";
import Header from "../../(dashboard)/dashboard/components/Header";