slideshare/src/utils/presentationStorage.ts
Michael Mainguy 9b0b16969f feat: implement complete presentation management system with aspect ratio support
## Major Features Added:

### Presentation Management
- Complete CRUD operations for presentations (create, read, update, delete)
- IndexedDB storage for offline presentation data
- Comprehensive presentation list view with metadata
- Navigation integration with header menu

### Slide Management
- Full slide editor with layout selection and content editing
- Live preview with theme styling applied
- Speaker notes functionality
- Enhanced layout previews with realistic sample content
- Themed layout selection with proper CSS inheritance

### Aspect Ratio System
- Support for 3 common presentation formats: 16:9, 4:3, 16:10
- Global CSS system baked into theme engine
- Visual aspect ratio selection in presentation creation
- Responsive scaling for different viewing contexts
- Print-optimized styling with proper dimensions

### User Experience Improvements
- Enhanced sample content generation for realistic previews
- Improved navigation with presentation management
- Better form styling and user interaction
- Comprehensive error handling and loading states
- Mobile-responsive design throughout

### Technical Infrastructure
- Complete TypeScript type system for presentations
- Modular component architecture
- CSS aspect ratio classes for theme consistency
- Enhanced template rendering with live updates
- Robust storage utilities with proper error handling

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-20 16:34:00 -05:00

214 lines
6.1 KiB
TypeScript

import type { Presentation, PresentationMetadata, CreatePresentationRequest } from '../types/presentation';
const DB_NAME = 'SlideshareDB';
const DB_VERSION = 1;
const PRESENTATIONS_STORE = 'presentations';
/**
* Initialize IndexedDB database
*/
export const initializeDB = (): Promise<IDBDatabase> => {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onerror = () => {
reject(new Error('Failed to open IndexedDB database'));
};
request.onsuccess = () => {
resolve(request.result);
};
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
// Create presentations object store
if (!db.objectStoreNames.contains(PRESENTATIONS_STORE)) {
const store = db.createObjectStore(PRESENTATIONS_STORE, { keyPath: 'metadata.id' });
// Create indexes for efficient querying
store.createIndex('name', 'metadata.name', { unique: false });
store.createIndex('theme', 'metadata.theme', { unique: false });
store.createIndex('createdAt', 'metadata.createdAt', { unique: false });
store.createIndex('updatedAt', 'metadata.updatedAt', { unique: false });
}
};
});
};
/**
* Generate a unique ID for presentations
*/
const generatePresentationId = (): string => {
return `presentation_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
};
/**
* Create a new presentation
*/
export const createPresentation = async (request: CreatePresentationRequest): Promise<Presentation> => {
const db = await initializeDB();
const now = new Date().toISOString();
const presentation: Presentation = {
metadata: {
id: generatePresentationId(),
name: request.name,
description: request.description,
theme: request.theme,
aspectRatio: request.aspectRatio,
createdAt: now,
updatedAt: now
},
slides: []
};
return new Promise((resolve, reject) => {
const transaction = db.transaction([PRESENTATIONS_STORE], 'readwrite');
const store = transaction.objectStore(PRESENTATIONS_STORE);
const request = store.add(presentation);
request.onerror = () => {
reject(new Error('Failed to create presentation'));
};
request.onsuccess = () => {
resolve(presentation);
};
transaction.onerror = () => {
reject(new Error('Transaction failed while creating presentation'));
};
});
};
/**
* Get all presentations
*/
export const getAllPresentations = async (): Promise<Presentation[]> => {
const db = await initializeDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([PRESENTATIONS_STORE], 'readonly');
const store = transaction.objectStore(PRESENTATIONS_STORE);
const request = store.getAll();
request.onerror = () => {
reject(new Error('Failed to retrieve presentations'));
};
request.onsuccess = () => {
// Sort by creation date (newest first)
const presentations = request.result.sort((a: Presentation, b: Presentation) =>
new Date(b.metadata.createdAt).getTime() - new Date(a.metadata.createdAt).getTime()
);
resolve(presentations);
};
});
};
/**
* Get a presentation by ID
*/
export const getPresentationById = async (id: string): Promise<Presentation | null> => {
const db = await initializeDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([PRESENTATIONS_STORE], 'readonly');
const store = transaction.objectStore(PRESENTATIONS_STORE);
const request = store.get(id);
request.onerror = () => {
reject(new Error('Failed to retrieve presentation'));
};
request.onsuccess = () => {
resolve(request.result || null);
};
});
};
/**
* Update a presentation
*/
export const updatePresentation = async (presentation: Presentation): Promise<Presentation> => {
const db = await initializeDB();
// Update the updatedAt timestamp
presentation.metadata.updatedAt = new Date().toISOString();
return new Promise((resolve, reject) => {
const transaction = db.transaction([PRESENTATIONS_STORE], 'readwrite');
const store = transaction.objectStore(PRESENTATIONS_STORE);
const request = store.put(presentation);
request.onerror = () => {
reject(new Error('Failed to update presentation'));
};
request.onsuccess = () => {
resolve(presentation);
};
});
};
/**
* Delete a presentation
*/
export const deletePresentation = async (id: string): Promise<void> => {
const db = await initializeDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([PRESENTATIONS_STORE], 'readwrite');
const store = transaction.objectStore(PRESENTATIONS_STORE);
const request = store.delete(id);
request.onerror = () => {
reject(new Error('Failed to delete presentation'));
};
request.onsuccess = () => {
resolve();
};
});
};
/**
* Get presentations by theme
*/
export const getPresentationsByTheme = async (theme: string): Promise<Presentation[]> => {
const db = await initializeDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([PRESENTATIONS_STORE], 'readonly');
const store = transaction.objectStore(PRESENTATIONS_STORE);
const index = store.index('theme');
const request = index.getAll(theme);
request.onerror = () => {
reject(new Error('Failed to retrieve presentations by theme'));
};
request.onsuccess = () => {
// Sort by creation date (newest first)
const presentations = request.result.sort((a: Presentation, b: Presentation) =>
new Date(b.metadata.createdAt).getTime() - new Date(a.metadata.createdAt).getTime()
);
resolve(presentations);
};
});
};
/**
* Get presentation metadata only (for listing views)
*/
export const getPresentationMetadata = async (): Promise<PresentationMetadata[]> => {
const presentations = await getAllPresentations();
return presentations.map(p => p.metadata);
};