Web editor data integration reference.
This document describes the workflow for opening generated data (numbers, sentences, conjugation rules, etc.) directly in the Book Editor for preview, editing, and PDF export.
Previously, data generation would create PDFs directly without preview, leading to:
Generate → Preview in Editor → Edit → Export PDF
Instead of generating PDFs blindly, users can:
Data Generation Page → Session Storage → Book Editor → PDF Export
```text
1. **Data Generation**: User generates data (e.g., numbers 0-1000)
2. **Store Data**: Data is stored in `sessionStorage` before navigation
3. **Load in Editor**: Editor checks for incoming data on mount
4. **Convert Format**: Table data is converted to editor pages format
5. **Preview/Edit**: User can see and edit the content
6. **Export**: Export to PDF when ready
### Technical Implementation
#### 1. Data Storage Format
```typescript
interface EditorData {
type: 'numbers' | 'sentences' | 'conjugation' | 'verbs'
data: any[] // Table data array
columns: Array<{
key: string
label: string
width?: number
}>
metadata?: {
title?: string
range?: [number, number]
languagePair?: string
[key: string]: any
}
}
```text
#### 2. Session Storage Key
```typescript
const STORAGE_KEY = 'editorData'
```text
#### 3. Data Conversion Strategy
**Table Data → Editor Pages**
- **Cover Page**: Created from metadata (title, subtitle, author)
- **Content Pages**: Table data formatted as:
- Headers/table structure preserved
- Data rows formatted as readable content
- Proper pagination (content distributed across pages)
- Styling matches editor settings
## Implementation Guide
### Step 1: Add "Open in Editor" Button to Data Pages
#### Example: Numbers Page
```typescript
// src/app/data-generation/numbers/page.tsx
import { useRouter } from 'next/navigation'
import { BookOpen } from 'lucide-react'
const router = useRouter()
// Add button in export options section
<ThemeButton
onClick={() => {
// Store data for editor
const editorData = {
type: 'numbers',
data: dataTable,
columns: exportColumns,
metadata: {
title: `Kifuliiru Numbers ${numberRange[0]}-${numberRange[1]}`,
range: numberRange,
languagePair: 'Kifuliiru-English'
}
}
sessionStorage.setItem('editorData', JSON.stringify(editorData))
router.push('/book-editor')
}}
variant="primary"
size="md"
icon={<BookOpen size={16} />}
>
Open in Editor
</ThemeButton>
```text
#### Example: Sentences Page
```typescript
// src/app/sentences/page.tsx
<ThemeButton
onClick={() => {
const editorData = {
type: 'sentences',
data: filtered, // or entries
columns: [
{ key: 'kifuliiru', label: 'Kifuliiru' },
{ key: 'english', label: 'English' },
{ key: 'category', label: 'Category' }
],
metadata: {
title: 'Kifuliiru Sentences',
category: selectedCategory || 'All'
}
}
sessionStorage.setItem('editorData', JSON.stringify(editorData))
router.push('/book-editor')
}}
>
<BookOpen size={16} />
Open in Editor
</ThemeButton>
```text
#### Example: Conjugation Page
```typescript
// src/app/data-generation/conjugation/page.tsx
<ThemeButton
onClick={() => {
const editorData = {
type: 'conjugation',
data: dataTable,
columns: exportColumns,
metadata: {
title: 'Kifuliiru Verb Conjugation',
tense: selectedTense,
persons: selectedPersons
}
}
sessionStorage.setItem('editorData', JSON.stringify(editorData))
router.push('/book-editor')
}}
>
<BookOpen size={16} />
Open in Editor
</ThemeButton>
```text
### Step 2: Add Data Loading Logic to Book Editor
```typescript
// src/app/book-editor/page.tsx
import { useSearchParams } from 'next/navigation'
export default function BookEditorPage() {
const searchParams = useSearchParams()
// Check for incoming data on mount
useEffect(() => {
const editorDataStr = sessionStorage.getItem('editorData')
if (editorDataStr) {
try {
const editorData = JSON.parse(editorDataStr)
// Convert table data to editor pages
const { pages, settings } = convertDataToEditorPages(editorData)
// Load into editor
setPages(pages)
if (settings) {
setBookSettings(settings)
}
// Clean up
sessionStorage.removeItem('editorData')
// Show notification
showNotification(' Data loaded into editor!')
} catch (error) {
console.error('Error loading editor data:', error)
showNotification('Error loading data', 'error')
}
}
}, [])
// ... rest of component
}
```text
### Step 3: Create Data Converter Utility
```typescript
// src/lib/utils/editor-data-converter.ts
interface EditorData {
type: 'numbers' | 'sentences' | 'conjugation' | 'verbs'
data: any[]
columns: Array<{ key: string; label: string; width?: number }>
metadata?: Record<string, any>
}
interface ConvertedEditorPages {
pages: BookPage[]
settings?: BookSettings
}
export function convertDataToEditorPages(editorData: EditorData): ConvertedEditorPages {
const pages: BookPage[] = []
// Create cover page from metadata
const coverPage = createCoverPage(editorData.metadata)
if (coverPage) {
pages.push(coverPage)
}
// Convert table data to content pages
const contentPages = convertTableDataToPages(editorData.data, editorData.columns)
pages.push(...contentPages)
// Set default book settings
const settings: BookSettings = {
pageSize: '6x9',
fontFamily: 'Georgia, serif',
fontSize: '12pt',
lineHeight: '1.8',
showHeader: true,
showFooter: true,
headerText: editorData.metadata?.title || 'Kifuliiru Data',
pageNumberPosition: 'footer-right'
}
return { pages, settings }
}
function createCoverPage(metadata?: Record<string, any>): BookPage | null {
if (!metadata?.title) return null
return {
id: 'cover',
type: 'cover',
number: 0,
content: {
title: metadata.title,
subtitle: metadata.subtitle || metadata.languagePair || '',
author: metadata.author || 'Kifuliiru Lab'
}
}
}
function convertTableDataToPages(
data: any[],
columns: Array<{ key: string; label: string }>
): BookPage[] {
const pages: BookPage[] = []
// Create table header row
const headerRow = `<table style="width: 100%; border-collapse: collapse; margin-bottom: 20px;">
<thead>
<tr>
${columns.map(col => `<th style="border: 1px solid #ddd; padding: 8px; text-align: left; background: #f3f4f6;">${col.label}</th>`).join('')}
</tr>
</thead>
<tbody>
`
// Distribute data across pages
const itemsPerPage = 25 // Adjust based on content
let currentPageContent = headerRow
let pageNumber = 1
for (let i = 0; i < data.length; i++) {
const row = data[i]
const rowHtml = `
<tr>
${columns.map(col => `<td style="border: 1px solid #ddd; padding: 8px;">${row[col.key] || ''}</td>`).join('')}
</tr>
`
currentPageContent += rowHtml
// Create new page when reaching limit or last item
if ((i + 1) % itemsPerPage === 0 || i === data.length - 1) {
currentPageContent += `
</tbody>
</table>`
pages.push({
id: `page-${pageNumber}`,
type: 'content',
number: pageNumber,
content: currentPageContent
})
pageNumber++
currentPageContent = headerRow
}
}
return pages
}
// Format-specific converters
export function formatNumbersData(data: any[], columns: any[]): string {
// Custom formatting for numbers
let content = '<h1>Kifuliiru Numbers</h1>\n\n'
data.forEach(item => {
content += `<p><strong>${item.value}</strong> - ${item.kifuliiru} (${item.english})</p>\n`
})
return content
}
export function formatSentencesData(data: any[], columns: any[]): string {
// Custom formatting for sentences
let content = '<h1>Kifuliiru Sentences</h1>\n\n'
data.forEach(item => {
content += `<div style="margin-bottom: 20px;">
<p><strong>Kifuliiru:</strong> ${item.kifuliiru || ''}</p>
<p><strong>English:</strong> ${item.english || ''}</p>
${item.category ? `<p><em>Category: ${item.category}</em></p>` : ''}
</div>\n`
})
return content
}
```text
## Usage Guide
### For Users
1. **Generate Data**
- Navigate to a data generation page (Numbers, Sentences, Conjugation)
- Configure generation settings
- Click "Generate" to create data
2. **Open in Editor**
- Click the "Open in Editor" button
- Editor opens automatically with data loaded
- Cover page is created from metadata
- Content pages show formatted data
3. **Preview and Edit**
- Review content in the editor
- Edit text, formatting, or structure as needed
- Adjust book settings (fonts, page size, etc.)
- Add/remove pages if needed
4. **Export PDF**
- Click "Export" → "PDF"
- PDF is generated from the editor content
- Download when ready
### For Developers
#### Adding Support to New Data Types
1. **Add Button to Data Page**
```typescript
<ThemeButton onClick={openInEditor}>
<BookOpen /> Open in Editor
</ThemeButton>
```text
2. **Store Data Format**
```typescript
const editorData = {
type: 'yourDataType',
data: yourDataTable,
columns: yourColumns,
metadata: { title: 'Your Title' }
}
sessionStorage.setItem('editorData', JSON.stringify(editorData))
```text
3. **Add Converter Function** (if needed)
- Add custom formatting in `editor-data-converter.ts`
- Handle type-specific formatting
## Benefits
### For Users
- **Preview Before Export**: See exactly how content will look
- **Edit Capability**: Make changes before PDF generation
- **Better Control**: Adjust formatting, fonts, page size
- **Iterative Workflow**: Generate → Preview → Edit → Export
### For Developers
- **Single Source of Truth**: Editor becomes the preview system
- **Flexible**: Easy to add new data types
- **Reusable**: Same editor for all data types
- **Maintainable**: Centralized conversion logic
## Limitations & Considerations
### Current Limitations
1. **Large Datasets**: Very large tables may create many pages
- **Solution**: Paginate or use table view in editor
2. **Complex Formatting**: Some table structures may not convert perfectly
- **Solution**: Allow editing after loading
3. **Data Loss**: Data format may lose some structure
- **Solution**: Preserve original data in metadata
### Future Improvements
1. **Table Editor View**: Option to view as table in editor
2. **Bulk Edit**: Edit multiple rows at once
3. **Data Filtering**: Filter data before loading into editor
4. **Template Support**: Save editor layouts as templates
5. **Auto-Pagination**: Smart page breaking for tables
6. **Export Formats**: Export to Word, EPUB, etc.
## Troubleshooting
### Data Not Loading
- Check browser console for errors
- Verify `sessionStorage` is enabled
- Check data format matches expected structure
### Formatting Issues
- Editor may display differently than PDF
- Use editor preview as guide, not exact match
- Adjust settings in editor sidebar
### Performance Issues
- Large datasets may load slowly
- Consider pagination or chunking
- Limit initial data size if needed
## Technical Details
### Session Storage
- **Key**: `editorData`
- **Format**: JSON string
- **Lifetime**: Per browser session
- **Cleanup**: Removed after loading
### Data Conversion
- **Cover Page**: Created if metadata.title exists
- **Content Pages**: Created from table data
- **Pagination**: Automatic based on content size
- **Styling**: Basic HTML/CSS preserved
### Error Handling
- Invalid data format → Error notification
- Missing columns → Default formatting
- Empty data → Empty editor state
## Examples
### Example 1: Numbers 0-100
```typescript
{
type: 'numbers',
data: [
{ value: 0, kifuliiru: 'zero', english: 'zero' },
{ value: 1, kifuliiru: 'umu', english: 'one' },
// ... more numbers
],
columns: [
{ key: 'value', label: 'Value' },
{ key: 'kifuliiru', label: 'Kifuliiru' },
{ key: 'english', label: 'English' }
],
metadata: {
title: 'Kifuliiru Numbers 0-100',
range: [0, 100]
}
}
```text
### Example 2: Sentences
```typescript
{
type: 'sentences',
data: [
{
kifuliiru: 'Muha omuhi?',
english: 'How are you?',
category: 'greetings'
},
// ... more sentences
],
columns: [
{ key: 'kifuliiru', label: 'Kifuliiru' },
{ key: 'english', label: 'English' },
{ key: 'category', label: 'Category' }
],
metadata: {
title: 'Kifuliiru Sentences',
category: 'All Categories'
}
}
```text
## Conclusion
This workflow transforms the data generation process from:
- **Before**: Generate → PDF (no preview, no control)
- **After**: Generate → Preview in Editor → Edit → Export PDF
The editor becomes the central hub for content preview and editing, giving users full control over their final output.