Implement slide deletion and duplication functionality
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
79060f0945
commit
4ce9f225a6
@ -35,6 +35,7 @@
|
|||||||
- [x] User can add presentation notes to slide
|
- [x] User can add presentation notes to slide
|
||||||
- [x] User can see miniature preview of slide live while editing
|
- [x] User can see miniature preview of slide live while editing
|
||||||
- [x] User can save slide (auto-saves presentation)
|
- [x] User can save slide (auto-saves presentation)
|
||||||
|
- [x] User can duplicate an existing slide copying it's layout and content.
|
||||||
|
|
||||||
### Edit Existing Slide
|
### Edit Existing Slide
|
||||||
- [x] User can click on existing slide to edit
|
- [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 exit slide editing mode without saving changes in an obvious way
|
||||||
- [x] User can edit presentation notes
|
- [x] User can edit presentation notes
|
||||||
- [x] Changes auto-save to presentation
|
- [x] Changes auto-save to presentation
|
||||||
|
- [ ] User can edit slide content without preview if desired by clicking inside content slot areas
|
||||||
|
|
||||||
### Remove Slide
|
### Remove Slide
|
||||||
- [ ] User can delete slides from presentation
|
- [ ] User can delete slides from presentation
|
||||||
@ -53,3 +55,8 @@
|
|||||||
- [ ] User can preview individual slides
|
- [ ] User can preview individual slides
|
||||||
- [ ] User can view slides in presentation mode
|
- [ ] User can view slides in presentation mode
|
||||||
- [ ] User can navigate between slides in preview
|
- [ ] 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
|
||||||
|
@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { useParams, useNavigate, Link } from 'react-router-dom';
|
import { useParams, useNavigate, Link } from 'react-router-dom';
|
||||||
import type { Presentation } from '../../types/presentation';
|
import type { Presentation } from '../../types/presentation';
|
||||||
import type { Theme } from '../../types/theme';
|
import type { Theme } from '../../types/theme';
|
||||||
import { getPresentationById } from '../../utils/presentationStorage';
|
import { getPresentationById, updatePresentation } from '../../utils/presentationStorage';
|
||||||
import { getTheme } from '../../themes';
|
import { getTheme } from '../../themes';
|
||||||
import './PresentationEditor.css';
|
import './PresentationEditor.css';
|
||||||
|
|
||||||
@ -88,16 +88,47 @@ export const PresentationEditor: React.FC = () => {
|
|||||||
const duplicateSlide = async (slideIndex: number) => {
|
const duplicateSlide = async (slideIndex: number) => {
|
||||||
if (!presentation) return;
|
if (!presentation) return;
|
||||||
|
|
||||||
|
const slideToDuplicate = presentation.slides[slideIndex];
|
||||||
|
if (!slideToDuplicate) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
// TODO: Implement slide duplication
|
// Create a duplicate slide with new ID
|
||||||
console.log('Duplicate slide functionality to be implemented');
|
const duplicatedSlide = {
|
||||||
alert('Slide duplication will be implemented next.');
|
...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) {
|
} catch (err) {
|
||||||
console.error('Error duplicating slide:', err);
|
console.error('Error duplicating slide:', err);
|
||||||
alert('Failed to duplicate slide');
|
setError(err instanceof Error ? err.message : 'Failed to duplicate slide');
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
@ -106,20 +137,62 @@ export const PresentationEditor: React.FC = () => {
|
|||||||
const deleteSlide = async (slideIndex: number) => {
|
const deleteSlide = async (slideIndex: number) => {
|
||||||
if (!presentation) return;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
// TODO: Implement slide deletion
|
// Create updated presentation with the slide removed
|
||||||
console.log('Delete slide functionality to be implemented');
|
const updatedPresentation = { ...presentation };
|
||||||
alert('Slide deletion will be implemented next.');
|
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) {
|
} catch (err) {
|
||||||
console.error('Error deleting slide:', err);
|
console.error('Error deleting slide:', err);
|
||||||
alert('Failed to delete slide');
|
setError(err instanceof Error ? err.message : 'Failed to delete slide');
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user