From 9ce7ed23ee17ade52628e17e4ea5c56bfb079230 Mon Sep 17 00:00:00 2001 From: shiva raj badu Date: Fri, 25 Jul 2025 23:58:15 +0545 Subject: [PATCH] feat(Nextjs): enchance editableLayoutWrapper --- .../components/EditableLayoutWrapper.tsx | 68 +++++++++++- .../components/ImageEditor.tsx | 102 +++++++++++++----- .../ExampleSlideLayoutStructure.tsx | 2 +- .../general/BulletIconsOnlySlideLayout.tsx | 8 +- .../modern/1IntroSlideLayout.tsx | 4 +- .../modern/2AboutCompanySlideLayout.tsx | 17 +-- .../modern/3ProblemSlideLayout.tsx | 14 +-- .../modern/5ProductOverviewSlideLayout.tsx | 64 ++++++----- .../modern/6MarketSizeSlideLayout.tsx | 26 ++--- .../modern/z10TeamSlideLayout.tsx | 12 ++- 10 files changed, 221 insertions(+), 96 deletions(-) diff --git a/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx b/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx index 1c5ddc74..fe46d952 100644 --- a/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx @@ -194,15 +194,15 @@ const EditableLayoutWrapper: React.FC = ({ // Add hover effects without changing layout htmlImg.style.cursor = 'pointer'; - htmlImg.style.transition = 'filter 0.2s, transform 0.2s'; + htmlImg.style.transition = 'opacity 0.2s, transform 0.2s'; const mouseEnterHandler = () => { - htmlImg.style.filter = 'brightness(0.9)'; + htmlImg.style.opacity = '0.8'; }; const mouseLeaveHandler = () => { - htmlImg.style.filter = 'brightness(1)'; + htmlImg.style.opacity = '1'; }; @@ -216,7 +216,7 @@ const EditableLayoutWrapper: React.FC = ({ htmlImg.removeEventListener('mouseleave', mouseLeaveHandler); htmlImg.style.cursor = ''; htmlImg.style.transition = ''; - htmlImg.style.filter = ''; + htmlImg.style.opacity = ''; htmlImg.style.transform = ''; htmlImg.removeAttribute('data-editable-processed'); }; @@ -326,6 +326,18 @@ const EditableLayoutWrapper: React.FC = ({ } }; + const handleFocusPointClick = (propertiesData: any) => { + console.log('activeEditor', activeEditor); + const id = activeEditor?.id; + const editableId = document.querySelector(`[data-editable-id="${id}"]`); + console.log('editableId', editableId); + if (editableId) { + const editableElement = editableId as HTMLImageElement; + editableElement.style.objectPosition = `${propertiesData.initialFocusPoint.x}px ${propertiesData.initialFocusPoint.y}px`; + editableElement.style.objectFit = propertiesData.initialObjectFit; + } + + }; return (
@@ -341,6 +353,7 @@ const EditableLayoutWrapper: React.FC = ({ properties={null} onClose={handleEditorClose} onImageChange={handleImageChange} + onFocusPointClick={handleFocusPointClick} > )} @@ -359,4 +372,49 @@ const EditableLayoutWrapper: React.FC = ({ ); }; -export default EditableLayoutWrapper; \ No newline at end of file +export default EditableLayoutWrapper; + + + + +const setNestedImageValue = (obj: any, path: string, url: string, promptText?: string) => { + const keys = path.split(/[.\[\]]+/).filter(Boolean); + let current = obj; + + // Navigate to the parent object + for (let i = 0; i < keys.length - 1; i++) { + const key = keys[i]; + if (isNaN(Number(key))) { + if (!current[key]) { + current[key] = {}; + } + current = current[key]; + } else { + const index = Number(key); + if (!current[index]) { + current[index] = {}; + } + current = current[index]; + } + } + + // Set the image properties + const finalKey = keys[keys.length - 1]; + const target = isNaN(Number(finalKey)) ? current[finalKey] : current[Number(finalKey)]; + + // Preserve existing properties if the target already exists + const updatedValue = { + ...(target && typeof target === 'object' ? target : {}), + __image_url__: url, + __image_prompt__: promptText || (target?.__image_prompt__) || '' + }; + + if (isNaN(Number(finalKey))) { + current[finalKey] = updatedValue; + } else { + current[Number(finalKey)] = updatedValue; + } + + // Add debugging + console.log('Redux: Updated slide image at path:', path, 'with URL:', url); +}; \ No newline at end of file diff --git a/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx b/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx index 83cc9906..604d0a9b 100644 --- a/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx @@ -27,7 +27,7 @@ interface ImageEditorProps { properties?: null | any; onClose?: () => void; onImageChange?: (newImageUrl: string, prompt?: string) => void; - + onFocusPointClick?: (propertiesData: any) => void; } const ImageEditor = ({ @@ -36,6 +36,7 @@ const ImageEditor = ({ promptContent, properties, onClose, + onFocusPointClick, onImageChange, }: ImageEditorProps) => { @@ -69,9 +70,6 @@ const ImageEditor = ({ // Refs const imageRef = useRef(null); - const imageContainerRef = useRef(null); - const toolbarRef = useRef(null); - const popoverContentRef = useRef(null); useEffect(() => { setPreviewImages(initialImage); @@ -105,28 +103,7 @@ const ImageEditor = ({ } } - // Close toolbar when clicking outside - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if ( - imageContainerRef.current && - !imageContainerRef.current.contains(event.target as Node) && - toolbarRef.current && - !toolbarRef.current.contains(event.target as Node) && - !popoverContentRef.current - ) { - if (isFocusPointMode) { - saveImageProperties(objectFit, focusPoint); - } - setIsFocusPointMode(false); - } - }; - document.addEventListener("mousedown", handleClickOutside); - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }, [isFocusPointMode, focusPoint, objectFit]); /** * Handles image selection and calls the parent callback @@ -200,6 +177,7 @@ const ImageEditor = ({ initialFocusPoint: focusPoint, }; // TODO: Save to Redux store if needed + onFocusPointClick?.(propertiesData); }; /** @@ -298,6 +276,7 @@ const ImageEditor = ({ Upload + {/* Edit */} {/* Generate Tab */} @@ -455,6 +434,79 @@ const ImageEditor = ({ )}
+ +
+

Current Image

+
{ + + if (isFocusPointMode) { + handleFocusPointClick(e); + } else { + + } + + }} + + className="aspect-[4/3] group rounded-lg overflow-hidden relative border border-gray-200"> +

Click to Change Focus Point

+ {previewImages && { + + setIsFocusPointMode(true); + + } + } src={previewImages} style={{ objectFit: objectFit, objectPosition: `${focusPoint.x}% ${focusPoint.y}%`, }} alt={`Preview`} className="w-full h-full " />} + {isFocusPointMode &&
+
+

+ Click anywhere to set focus point +

+ +
+ +
+
+
+
+
+
+
+
} +
+ {/* Edit Image */} + {/* Object Fit */} + { +
+

Object Fit

+
+ + + +
+
+ + } + {/* Focus Point */} + { + + } +
+
diff --git a/servers/nextjs/presentation-layouts/ExampleSlideLayoutStructure.tsx b/servers/nextjs/presentation-layouts/ExampleSlideLayoutStructure.tsx index ce92d946..391c67d5 100644 --- a/servers/nextjs/presentation-layouts/ExampleSlideLayoutStructure.tsx +++ b/servers/nextjs/presentation-layouts/ExampleSlideLayoutStructure.tsx @@ -26,7 +26,7 @@ const SlideComponent = ({ data }: { data: Partial }) => { // Validate each data field before rendering using && operator or optional chaining. // These layouts are exported as PDF and PPTX so they must be optimized for both formats. // Content must properly fit in the container, specify min and max constraints in the schema. - // You can check out ExampleComponent.tsx for more details. + // You can check out ExampleSlideLayout.tsx for more details. }; export default SlideComponent; \ No newline at end of file diff --git a/servers/nextjs/presentation-layouts/general/BulletIconsOnlySlideLayout.tsx b/servers/nextjs/presentation-layouts/general/BulletIconsOnlySlideLayout.tsx index 486910ef..f4375d7a 100644 --- a/servers/nextjs/presentation-layouts/general/BulletIconsOnlySlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/general/BulletIconsOnlySlideLayout.tsx @@ -29,7 +29,7 @@ const bulletIconsOnlySlideSchema = z.object({ title: 'Custom Software', subtitle: 'We create tailored software to optimize processes and boost efficiency.', icon: { - __icon_url__: 'https://cdn.jsdelivr.net/npm/lucide@latest/dist/esm/icons/code.js', + __icon_url__: '/static/icons/placeholder.png', __icon_query__: 'code software development' } }, @@ -37,7 +37,7 @@ const bulletIconsOnlySlideSchema = z.object({ title: 'Digital Consulting', subtitle: 'Our consultants guide organizations in leveraging the latest technologies.', icon: { - __icon_url__: 'https://cdn.jsdelivr.net/npm/lucide@latest/dist/esm/icons/users.js', + __icon_url__: '/static/icons/placeholder.png', __icon_query__: 'users consulting team' } }, @@ -45,7 +45,7 @@ const bulletIconsOnlySlideSchema = z.object({ title: 'Support Services', subtitle: 'We provide ongoing support to help businesses adapt and maintain performance.', icon: { - __icon_url__: 'https://cdn.jsdelivr.net/npm/lucide@latest/dist/esm/icons/headphones.js', + __icon_url__: '/static/icons/placeholder.png', __icon_query__: 'headphones support service' } }, @@ -53,7 +53,7 @@ const bulletIconsOnlySlideSchema = z.object({ title: 'Scalable Marketing', subtitle: 'Our data-driven strategies help businesses expand their reach and engagement.', icon: { - __icon_url__: 'https://cdn.jsdelivr.net/npm/lucide@latest/dist/esm/icons/trending-up.js', + __icon_url__: '/static/icons/placeholder.png', __icon_query__: 'trending up marketing growth' } } diff --git a/servers/nextjs/presentation-layouts/modern/1IntroSlideLayout.tsx b/servers/nextjs/presentation-layouts/modern/1IntroSlideLayout.tsx index ef9eaee8..3e9e06da 100644 --- a/servers/nextjs/presentation-layouts/modern/1IntroSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/modern/1IntroSlideLayout.tsx @@ -60,8 +60,8 @@ const IntroPitchDeckSlide: React.FC = ({ > {/* Top Header */}
- {slideData?.companyName} - {slideData?.date} +

{slideData?.companyName}

+

{slideData?.date}

{/* Main Title */} diff --git a/servers/nextjs/presentation-layouts/modern/2AboutCompanySlideLayout.tsx b/servers/nextjs/presentation-layouts/modern/2AboutCompanySlideLayout.tsx index 372d5424..07800c2e 100644 --- a/servers/nextjs/presentation-layouts/modern/2AboutCompanySlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/modern/2AboutCompanySlideLayout.tsx @@ -122,14 +122,17 @@ const AboutCompanySlideLayout: React.FC = ({ {/* Right side - Content */}
-

- {slideData?.title || "About Our Company"} -

+ {slideData?.title && ( +

+ {slideData?.title} +

+ )} -
- {slideData?.content || - "In the presentation session, the background/introduction can be filled with information that is arranged systematically and effectively with respect to an interesting topic to be used as material for discussion at the opening of the presentation session. The introduction can provide a general overview for those who are listening to your presentation so that the key words on the topic of discussion are emphasized during this background/introductory presentation session."} -
+ {slideData?.content && ( +
+ {slideData?.content} +
+ )}
diff --git a/servers/nextjs/presentation-layouts/modern/3ProblemSlideLayout.tsx b/servers/nextjs/presentation-layouts/modern/3ProblemSlideLayout.tsx index 6054eb44..1a6a7a6b 100644 --- a/servers/nextjs/presentation-layouts/modern/3ProblemSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/modern/3ProblemSlideLayout.tsx @@ -150,12 +150,14 @@ const ProblemStatementSlideLayout: React.FC< className="flex items-start gap-5 bg-white bg-opacity-5 rounded-lg p-5" >
- {category.icon?.__icon_query__} + {category.icon?.__icon_url__ && ( + {category.icon?.__icon_query__} + )}

diff --git a/servers/nextjs/presentation-layouts/modern/5ProductOverviewSlideLayout.tsx b/servers/nextjs/presentation-layouts/modern/5ProductOverviewSlideLayout.tsx index b27b8035..6ee21923 100644 --- a/servers/nextjs/presentation-layouts/modern/5ProductOverviewSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/modern/5ProductOverviewSlideLayout.tsx @@ -162,13 +162,15 @@ const ProductOverviewSlideLayout: React.FC = ({ className="rounded-b-md overflow-hidden" style={{ height: `${IMAGE_SECTION_HEIGHT + 28}px` }} > - { + {products[0].image.__image_url__ && ( + { + )}

)} @@ -199,13 +201,15 @@ const ProductOverviewSlideLayout: React.FC = ({ className="rounded-b-md overflow-hidden" style={{ height: `${IMAGE_SECTION_HEIGHT + 28}px` }} > - { + {products[2].image.__image_url__ && ( + { + )} )} @@ -225,13 +229,15 @@ const ProductOverviewSlideLayout: React.FC = ({ className="rounded-t-md overflow-hidden" style={{ height: `${IMAGE_SECTION_HEIGHT + 28}px` }} > - { + {products[1].image.__image_url__ && ( + { + )} {/* Bottom Section - Blue background with text */}
= ({ className="rounded-t-md overflow-hidden" style={{ height: `${IMAGE_SECTION_HEIGHT + 28}px` }} > - { + {products[3].image.__image_url__ && ( + { + )}
= ({ {slideData?.title || "Market Size"}
- Market World Map with Points + {slideData?.mapImage?.__image_url__ && ( + Market World Map with Points + )}
-

- {slideData?.description || - "Market size is the total amount of all sales and customers that can be seen directly by stakeholders. This technique is usually calculated at the end of the year, the market size can be used by companies to determine the potential of their market and business in the future."} -

+ {slideData?.description && ( +

+ {slideData?.description} +

+ )}
diff --git a/servers/nextjs/presentation-layouts/modern/z10TeamSlideLayout.tsx b/servers/nextjs/presentation-layouts/modern/z10TeamSlideLayout.tsx index 9f7ff177..65135ac7 100644 --- a/servers/nextjs/presentation-layouts/modern/z10TeamSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/modern/z10TeamSlideLayout.tsx @@ -145,11 +145,13 @@ const ModernTeamSlideLayout: React.FC = ({ > {/* Photo */}
- {member.image.__image_prompt__ + {member.image.__image_url__ && ( + {member.image.__image_prompt__ + )}
{/* Name */}