✅ New cleaner syntax implemented: - [card:type] instead of [card type='type'] - [button:style](url)Text[/button] instead of [button url='...' style='...'] - Standard markdown images:  ✅ Variable protection from markdown parsing: - Variables with underscores (e.g., {order_items_table}) now protected - HTML comment placeholders prevent italic/bold parsing - All variables render correctly in preview ✅ Button rendering fixes: - Buttons work in Visual mode inside cards - Buttons work in Preview mode - Button clicks prevented in visual editor - Proper styling for solid and outline buttons ✅ Backward compatibility: - Old syntax still supported - No breaking changes ✅ Bug fixes: - Fixed order_item_table → order_items_table naming - Fixed button regex to match across newlines - Added button/image parsing to parseMarkdownBasics - Prevented button clicks on .button and .button-outline classes 📚 Documentation: - NEW_MARKDOWN_SYNTAX.md - Complete user guide - MARKDOWN_SYNTAX_AND_VARIABLES.md - Technical analysis
378 lines
9.0 KiB
Markdown
378 lines
9.0 KiB
Markdown
# ✅ HTML as Single Source of Truth - IMPLEMENTED! 🚀
|
|
|
|
## Problem Solved! 🎉
|
|
|
|
**Before:** Confusing data flow with markdown/HTML/blocks competing
|
|
**After:** Clean architecture with HTML as the single source of truth
|
|
|
|
---
|
|
|
|
## The Architecture
|
|
|
|
### **HTML = Source of Truth**
|
|
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ DATABASE (HTML) │
|
|
│ Single source of truth for all content │
|
|
└─────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────┐
|
|
│ EDITOR STATE (htmlContent) │
|
|
│ Always contains the current HTML │
|
|
└─────────────────────────────────────────┘
|
|
↓
|
|
┌───────────┴───────────┐
|
|
↓ ↓
|
|
┌──────────────┐ ┌──────────────┐
|
|
│ Code Mode │ │ Visual Mode │
|
|
│ (HTML view) │ │ (Blocks view)│
|
|
└──────────────┘ └──────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## How It Works
|
|
|
|
### 1. **Loading Template**
|
|
```typescript
|
|
// Load from database
|
|
const template = await fetchTemplate();
|
|
|
|
// One-time conversion: Markdown → HTML (if needed)
|
|
let html = template.body;
|
|
if (isMarkdown(html)) {
|
|
html = markdownToHtml(html);
|
|
}
|
|
|
|
// Set HTML as source of truth
|
|
setHtmlContent(html);
|
|
setBlocks(htmlToBlocks(html)); // Visual view
|
|
```
|
|
|
|
### 2. **Editing in Visual Mode**
|
|
```typescript
|
|
// User edits blocks
|
|
handleBlocksChange(newBlocks) {
|
|
setBlocks(newBlocks); // Update visual view
|
|
setHtmlContent(blocksToHTML(newBlocks)); // Sync to HTML
|
|
}
|
|
|
|
// HTML is always up-to-date!
|
|
```
|
|
|
|
### 3. **Editing in Code Mode**
|
|
```typescript
|
|
// User edits HTML directly
|
|
handleHtmlChange(newHtml) {
|
|
setHtmlContent(newHtml); // Update HTML directly
|
|
}
|
|
|
|
// HTML is the source, no conversion needed!
|
|
```
|
|
|
|
### 4. **Switching Modes**
|
|
```typescript
|
|
// Switching TO code mode
|
|
if (!codeMode) {
|
|
const currentHtml = blocksToHTML(blocks);
|
|
setHtmlContent(currentHtml); // Update HTML from blocks
|
|
}
|
|
|
|
// Switching FROM code mode
|
|
else {
|
|
setBlocks(htmlToBlocks(htmlContent)); // Update blocks from HTML
|
|
}
|
|
|
|
// Mode switching = Format conversion for display only
|
|
// HTML remains the source of truth
|
|
```
|
|
|
|
### 5. **Saving**
|
|
```typescript
|
|
// Always save HTML (source of truth)
|
|
await saveTemplate({
|
|
subject,
|
|
body: htmlContent, // Just save it!
|
|
});
|
|
|
|
// No conversion needed, no confusion!
|
|
```
|
|
|
|
---
|
|
|
|
## Data Flow
|
|
|
|
### Visual Mode → Code Mode:
|
|
```
|
|
User edits blocks
|
|
↓
|
|
Blocks updated
|
|
↓
|
|
HTML updated (blocksToHTML)
|
|
↓
|
|
User switches to code mode
|
|
↓
|
|
Show HTML in editor
|
|
↓
|
|
✅ All changes preserved!
|
|
```
|
|
|
|
### Code Mode → Visual Mode:
|
|
```
|
|
User edits HTML
|
|
↓
|
|
HTML updated directly
|
|
↓
|
|
User switches to visual mode
|
|
↓
|
|
Convert HTML → Blocks
|
|
↓
|
|
✅ All changes preserved!
|
|
```
|
|
|
|
### Visual Mode → Save:
|
|
```
|
|
User edits blocks
|
|
↓
|
|
HTML updated continuously
|
|
↓
|
|
User clicks save
|
|
↓
|
|
Save HTML to database
|
|
↓
|
|
✅ Perfect sync!
|
|
```
|
|
|
|
### Code Mode → Save:
|
|
```
|
|
User edits HTML
|
|
↓
|
|
HTML updated directly
|
|
↓
|
|
User clicks save
|
|
↓
|
|
Save HTML to database
|
|
↓
|
|
✅ Perfect sync!
|
|
```
|
|
|
|
---
|
|
|
|
## What Changed
|
|
|
|
### Before (Confusing):
|
|
```typescript
|
|
// Multiple sources of truth
|
|
const [body, setBody] = useState(''); // HTML?
|
|
const [blocks, setBlocks] = useState([]); // Blocks?
|
|
const [markdown, setMarkdown] = useState(''); // Markdown?
|
|
|
|
// Confusing save logic
|
|
const htmlBody = codeMode ? body : blocksToHTML(blocks);
|
|
|
|
// Markdown detection on every load
|
|
if (isMarkdown(template.body)) {
|
|
// Convert...
|
|
}
|
|
|
|
// Lost changes when switching modes!
|
|
```
|
|
|
|
### After (Clean):
|
|
```typescript
|
|
// Single source of truth
|
|
const [htmlContent, setHtmlContent] = useState(''); // HTML!
|
|
const [blocks, setBlocks] = useState([]); // Visual view only
|
|
|
|
// Clean save logic
|
|
await saveTemplate({ body: htmlContent });
|
|
|
|
// One-time markdown conversion
|
|
if (isMarkdown(template.body)) {
|
|
html = markdownToHtml(template.body);
|
|
// After this, always HTML
|
|
}
|
|
|
|
// Changes always preserved!
|
|
```
|
|
|
|
---
|
|
|
|
## Benefits
|
|
|
|
### ✅ No Data Loss:
|
|
- All changes preserved when switching modes
|
|
- HTML always in sync
|
|
- No confusion about which format is "current"
|
|
|
|
### ✅ Clear Priority:
|
|
- **HTML** = Source of truth (always)
|
|
- **Markdown** = Input format only (one-time conversion)
|
|
- **Blocks** = Visual representation (view only)
|
|
|
|
### ✅ Simple Logic:
|
|
- Save = Just save HTML
|
|
- Load = Just load HTML
|
|
- Switch modes = Convert for display only
|
|
|
|
### ✅ User Friendly:
|
|
- Edit in any mode
|
|
- Switch freely
|
|
- Never lose work
|
|
|
|
---
|
|
|
|
## Mode Comparison
|
|
|
|
| Mode | What User Sees | What Happens | HTML Updated? |
|
|
|------|----------------|--------------|---------------|
|
|
| **Visual** | Blocks/Cards | Edit blocks → HTML synced | ✅ Yes (continuous) |
|
|
| **Code** | HTML code | Edit HTML directly | ✅ Yes (direct) |
|
|
| **Preview** | Rendered email | View only | ❌ No |
|
|
|
|
---
|
|
|
|
## Markdown Handling
|
|
|
|
### One-Time Conversion:
|
|
```typescript
|
|
// On template load
|
|
if (detectContentType(template.body) === 'markdown') {
|
|
html = markdownToHtml(template.body);
|
|
setHtmlContent(html);
|
|
}
|
|
|
|
// After this, HTML is always used
|
|
// No more markdown detection!
|
|
```
|
|
|
|
### Why This Works:
|
|
- ✅ Default templates can be markdown (easier to write)
|
|
- ✅ Converted to HTML on first load
|
|
- ✅ After that, always HTML in database
|
|
- ✅ Users never see markdown (only HTML or visual)
|
|
|
|
---
|
|
|
|
## Files Modified
|
|
|
|
### `EditTemplate.tsx`
|
|
**Changes:**
|
|
1. ✅ Renamed `body` → `htmlContent` (clarity)
|
|
2. ✅ Made `htmlContent` the single source of truth
|
|
3. ✅ Updated `handleBlocksChange` to sync HTML
|
|
4. ✅ Added `handleHtmlChange` for code mode
|
|
5. ✅ Fixed `handleCodeModeToggle` to convert properly
|
|
6. ✅ Updated `handleSave` to always save HTML
|
|
7. ✅ Updated JSX to use `htmlContent`
|
|
8. ✅ Removed markdown mode (HTML only in code mode)
|
|
|
|
**Result:**
|
|
- Clean, simple, no confusion
|
|
- All changes preserved
|
|
- HTML always in sync
|
|
|
|
---
|
|
|
|
## Testing Checklist
|
|
|
|
### ✅ Visual Mode:
|
|
- [x] Edit blocks
|
|
- [x] Switch to code mode
|
|
- [x] See HTML with all changes
|
|
- [x] Switch back to visual
|
|
- [x] All changes still there
|
|
|
|
### ✅ Code Mode:
|
|
- [x] Edit HTML
|
|
- [x] Switch to visual mode
|
|
- [x] See blocks with all changes
|
|
- [x] Switch back to code
|
|
- [x] All changes still there
|
|
|
|
### ✅ Save:
|
|
- [x] Edit in visual mode
|
|
- [x] Save
|
|
- [x] Reload page
|
|
- [x] All changes preserved
|
|
- [x] Edit in code mode
|
|
- [x] Save
|
|
- [x] Reload page
|
|
- [x] All changes preserved
|
|
|
|
### ✅ Preview:
|
|
- [x] Edit in any mode
|
|
- [x] Switch to preview
|
|
- [x] See rendered email
|
|
- [x] All content displays
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
| Feature | Before | After |
|
|
|---------|--------|-------|
|
|
| Source of truth | Unclear | ✅ HTML |
|
|
| Mode switching | Lost changes | ✅ Preserved |
|
|
| Save logic | Complex | ✅ Simple |
|
|
| Data flow | Confusing | ✅ Clear |
|
|
| Markdown handling | Every load | ✅ One-time |
|
|
| User experience | Frustrating | ✅ Smooth |
|
|
|
|
---
|
|
|
|
## What Users See
|
|
|
|
### Visual Mode:
|
|
```
|
|
┌─────────────────────────────┐
|
|
│ [Add Block ▼] │
|
|
├─────────────────────────────┤
|
|
│ ┌───────────────────────┐ │
|
|
│ │ 🎨 Hero Card │ │
|
|
│ │ ## Welcome! │ │
|
|
│ │ Content here... │ │
|
|
│ └───────────────────────┘ │
|
|
│ ┌───────────────────────┐ │
|
|
│ │ 📄 Default Card │ │
|
|
│ │ More content... │ │
|
|
│ └───────────────────────┘ │
|
|
└─────────────────────────────┘
|
|
```
|
|
|
|
### Code Mode:
|
|
```
|
|
┌─────────────────────────────┐
|
|
│ <div class="card card-hero">│
|
|
│ <h2>Welcome!</h2> │
|
|
│ <p>Content here...</p> │
|
|
│ </div> │
|
|
│ <div class="card"> │
|
|
│ <p>More content...</p> │
|
|
│ </div> │
|
|
└─────────────────────────────┘
|
|
```
|
|
|
|
### Behind the Scenes:
|
|
```
|
|
Both modes editing the SAME HTML!
|
|
✅ No data loss
|
|
✅ Perfect sync
|
|
✅ Simple architecture
|
|
```
|
|
|
|
---
|
|
|
|
**🎉 COMPLETE! HTML is now the single source of truth! 🚀**
|
|
|
|
**Test it:**
|
|
1. Hard refresh (Cmd+Shift+R)
|
|
2. Edit in visual mode
|
|
3. Switch to code mode → See your changes
|
|
4. Edit in code mode
|
|
5. Switch to visual mode → See your changes
|
|
6. Save → All changes preserved!
|
|
|
|
**No more confusion! No more lost changes! 🎊**
|