From 4ce9f225a6d0eb6cb08cfa1a29032f2b182916a5 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Wed, 20 Aug 2025 17:32:06 -0500 Subject: [PATCH] Implement slide deletion and duplication functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add complete slide deletion with smart confirmation messages - Implement slide duplication that copies layout, content, and notes - Handle edge cases for navigation after deletion/duplication - Add proper slide order management and renumbering - Include comprehensive error handling and user feedback - Support deleting last slide, only slide, and middle slides - Navigate intelligently after operations (to duplicated slide, adjusted position after deletion) - Add improved confirmation dialogs with context-aware messaging - Integrate with existing presentation storage and state management - Replace placeholder TODO implementations with full functionality Features completed: ✅ User can delete slides from presentation ✅ User gets confirmation before slide deletion ✅ Slide order adjusts automatically ✅ User can duplicate existing slides copying layout and content ✅ Smart navigation maintains user context after operations ✅ Robust error handling with proper user feedback 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- USERFLOWS.md | 7 ++ .../presentations/PresentationEditor.tsx | 93 +++++++++++++++++-- 2 files changed, 90 insertions(+), 10 deletions(-) diff --git a/USERFLOWS.md b/USERFLOWS.md index 3418882..85a9106 100644 --- a/USERFLOWS.md +++ b/USERFLOWS.md @@ -35,6 +35,7 @@ - [x] User can add presentation notes to slide - [x] User can see miniature preview of slide live while editing - [x] User can save slide (auto-saves presentation) +- [x] User can duplicate an existing slide copying it's layout and content. ### Edit Existing Slide - [x] User can click on existing slide to edit @@ -43,6 +44,7 @@ - [x] User can exit slide editing mode without saving changes in an obvious way - [x] User can edit presentation notes - [x] Changes auto-save to presentation +- [ ] User can edit slide content without preview if desired by clicking inside content slot areas ### Remove Slide - [ ] User can delete slides from presentation @@ -53,3 +55,8 @@ - [ ] User can preview individual slides - [ ] User can view slides in presentation mode - [ ] User can navigate between slides in preview + +### Slide Order Management +- [ ] User can reorder slides via drag-and-drop +- [ ] User can see slide order visually in editor +- [ ] Slide order automatically saves when changed diff --git a/src/components/presentations/PresentationEditor.tsx b/src/components/presentations/PresentationEditor.tsx index b949646..9c5fbb9 100644 --- a/src/components/presentations/PresentationEditor.tsx +++ b/src/components/presentations/PresentationEditor.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import { useParams, useNavigate, Link } from 'react-router-dom'; import type { Presentation } from '../../types/presentation'; import type { Theme } from '../../types/theme'; -import { getPresentationById } from '../../utils/presentationStorage'; +import { getPresentationById, updatePresentation } from '../../utils/presentationStorage'; import { getTheme } from '../../themes'; import './PresentationEditor.css'; @@ -88,16 +88,47 @@ export const PresentationEditor: React.FC = () => { const duplicateSlide = async (slideIndex: number) => { if (!presentation) return; + const slideToDuplicate = presentation.slides[slideIndex]; + if (!slideToDuplicate) return; + try { setSaving(true); + setError(null); - // TODO: Implement slide duplication - console.log('Duplicate slide functionality to be implemented'); - alert('Slide duplication will be implemented next.'); + // Create a duplicate slide with new ID + const duplicatedSlide = { + ...slideToDuplicate, + id: `slide_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, + order: slideIndex + 1 // Insert right after the original + }; + + // Create updated presentation with the duplicated slide + const updatedPresentation = { ...presentation }; + const newSlides = [...presentation.slides]; + + // Insert the duplicated slide after the original + newSlides.splice(slideIndex + 1, 0, duplicatedSlide); + + // Update slide order for all slides after the insertion point + newSlides.forEach((slide, index) => { + slide.order = index; + }); + + updatedPresentation.slides = newSlides; + + // Save the updated presentation + await updatePresentation(updatedPresentation); + + // Update local state + setPresentation(updatedPresentation); + + // Navigate to the duplicated slide + const newSlideNumber = slideIndex + 2; // +2 because we inserted after and slide numbers are 1-based + navigate(`/presentations/${presentationId}/edit/slides/${newSlideNumber}`); } catch (err) { console.error('Error duplicating slide:', err); - alert('Failed to duplicate slide'); + setError(err instanceof Error ? err.message : 'Failed to duplicate slide'); } finally { setSaving(false); } @@ -106,20 +137,62 @@ export const PresentationEditor: React.FC = () => { const deleteSlide = async (slideIndex: number) => { if (!presentation) return; - if (!confirm('Are you sure you want to delete this slide?')) { + const slideToDelete = presentation.slides[slideIndex]; + if (!slideToDelete) return; + + const slideNumber = slideIndex + 1; + const totalSlides = presentation.slides.length; + let confirmMessage = `Are you sure you want to delete slide ${slideNumber}?`; + + if (totalSlides === 1) { + confirmMessage = `Are you sure you want to delete the only slide in this presentation? The presentation will be empty after deletion.`; + } else { + confirmMessage += ` This will remove the slide and renumber all subsequent slides. This action cannot be undone.`; + } + + if (!confirm(confirmMessage)) { return; } try { setSaving(true); + setError(null); - // TODO: Implement slide deletion - console.log('Delete slide functionality to be implemented'); - alert('Slide deletion will be implemented next.'); + // Create updated presentation with the slide removed + const updatedPresentation = { ...presentation }; + updatedPresentation.slides = presentation.slides.filter((_, index) => index !== slideIndex); + + // Update slide order for remaining slides + updatedPresentation.slides.forEach((slide, index) => { + slide.order = index; + }); + + // Save the updated presentation + await updatePresentation(updatedPresentation); + + // Update local state + setPresentation(updatedPresentation); + + // Handle navigation after deletion + const totalSlides = updatedPresentation.slides.length; + if (totalSlides === 0) { + // No slides left, stay on editor main view + navigate(`/presentations/${presentationId}/edit`); + } else { + // Navigate to appropriate slide + let newSlideIndex = slideIndex; + if (slideIndex >= totalSlides) { + // If we deleted the last slide, go to the new last slide + newSlideIndex = totalSlides - 1; + } + // Navigate to the adjusted slide number + const slideNumber = newSlideIndex + 1; + navigate(`/presentations/${presentationId}/edit/slides/${slideNumber}`); + } } catch (err) { console.error('Error deleting slide:', err); - alert('Failed to delete slide'); + setError(err instanceof Error ? err.message : 'Failed to delete slide'); } finally { setSaving(false); }