feat(journals): implement centralized export templates and bulk interop

- Added 'ae_journals_export_templates.ts' with Markdown, HTML, and JSON support
- Refactored 'ae_comp__modal_journal_export.svelte' to use the new template system
- Optimized bulk export with automated template selection based on Journal type
- Integrated 'Import Entries' action directly into the Journal menu
- Updated project documentation to reflect portability features and implementation status
This commit is contained in:
Scott Idem
2026-01-14 12:55:15 -05:00
parent b80cbb7c2b
commit 56fa003382
6 changed files with 258 additions and 79 deletions

View File

@@ -0,0 +1,149 @@
/**
* @file ae_journals_export_templates.ts
* @description Templates for formatting journal entries during export.
* @author One Sky IT
*/
import { ae_util } from '$lib/ae_utils/ae_utils';
import type { ae_JournalEntry } from '$lib/types/ae_types';
export interface ExportTemplate {
id: string;
name: string;
description: string;
extension: string;
formatter: (entries: ae_JournalEntry[]) => string;
}
/**
* Standard Markdown Template
*/
export const template_standard_markdown: ExportTemplate = {
id: 'standard_markdown',
name: 'Standard Markdown',
description: 'Basic Markdown with title, date, and content.',
extension: 'md',
formatter: (entries) => {
return entries.map(entry => {
const dateStr = ae_util.iso_datetime_formatter(entry.created_on, 'datetime_12_long');
const title = entry.name || dateStr;
const header = `# ${title}\n*${dateStr}*\n\n`;
return `${header}${entry.content || ''}\n\n---\n`;
}).join('\n');
}
};
/**
* Personal Log Template
* Formats entries as a continuous daily log.
*/
export const template_personal_log: ExportTemplate = {
id: 'personal_log',
name: 'Personal Log',
description: 'Formatted as a daily log with ## Date headers.',
extension: 'md',
formatter: (entries) => {
// Sort entries by date ascending for a chronological log
const sorted = [...entries].sort((a, b) =>
(a.created_on || '').localeCompare(b.created_on || '')
);
return sorted.map(entry => {
const dateStr = ae_util.iso_datetime_formatter(entry.created_on, 'date_iso');
const title = entry.name || '';
const header = `## ${dateStr}${title ? ' - ' + title : ''}\n\n`;
return `${header}${entry.content || ''}\n\n`;
}).join('\n');
}
};
/**
* Amazon Vine Template
* Specific formatting for product reviews.
*/
export const template_amazon_vine: ExportTemplate = {
id: 'amazon_vine',
name: 'Amazon Vine',
description: 'Optimized for product reviews.',
extension: 'md',
formatter: (entries) => {
return entries.map(entry => {
const dateStr = ae_util.iso_datetime_formatter(entry.created_on, 'date_iso');
// Try to find a product name in the title or content
const productName = entry.name || 'Unknown Product';
// Look for product link in content_json or tags if available,
// otherwise just output content
let output = `## ${productName}\n`;
output += `*Date: ${dateStr}*\n\n`;
output += `${entry.content || ''}\n\n`;
output += `---`;
return output;
}).join('\n\n');
}
};
/**
* Standard HTML Template
*/
export const template_standard_html: ExportTemplate = {
id: 'standard_html',
name: 'Standard HTML',
description: 'Semantic HTML5 articles.',
extension: 'html',
formatter: (entries) => {
const body = entries.map(entry => {
const dateStr = ae_util.iso_datetime_formatter(entry.created_on, 'datetime_12_long');
const title = entry.name || dateStr;
return `
<article class="journal-entry" id="entry-${entry.journal_entry_id}">
<header>
<h1>${title}</h1>
<time datetime="${entry.created_on}">${dateStr}</time>
</header>
<div class="content">
${entry.content_md_html || ''}
</div>
<hr/>
</article>`;
}).join('\n');
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Journal Export</title>
<style>
body { font-family: sans-serif; max-width: 800px; margin: 2rem auto; padding: 0 1rem; line-height: 1.6; }
article { margin-bottom: 3rem; }
header { border-bottom: 1px solid #eee; margin-bottom: 1rem; }
h1 { margin-bottom: 0.2rem; }
time { color: #666; font-size: 0.9rem; }
hr { border: 0; border-top: 1px solid #eee; margin: 2rem 0; }
</style>
</head>
<body>
${body}
</body>
</html>`;
}
};
/**
* Standard JSON Template
*/
export const template_standard_json: ExportTemplate = {
id: 'standard_json',
name: 'Raw JSON',
description: 'Full database dump of selected entries.',
extension: 'json',
formatter: (entries) => JSON.stringify(entries, null, 2)
};
export const EXPORT_TEMPLATES = {
standard_markdown: template_standard_markdown,
personal_log: template_personal_log,
amazon_vine: template_amazon_vine,
standard_html: template_standard_html,
standard_json: template_standard_json
};