chore: add template folder

This commit is contained in:
Jesús Pérex 2025-07-07 23:07:55 +01:00
parent 4d6fe85f53
commit d33aeef1af
Signed by: jesus
GPG Key ID: 9F243E355E0BC939
11 changed files with 1738 additions and 0 deletions

163
templates/blog-post.html Normal file
View File

@ -0,0 +1,163 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}} - {{site_name | default(value="My Blog")}}</title>
<meta name="description" content="{{description | default(value=content | excerpt(length=160))}}">
{% if author %}
<meta name="author" content="{{author}}">
{% endif %}
{% if tags %}
<meta name="keywords" content="{{tags | join(sep=', ')}}">
{% endif %}
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f9f9f9;
}
.container {
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #2c3e50;
margin-bottom: 10px;
font-size: 2.5em;
line-height: 1.2;
}
.meta {
color: #7f8c8d;
margin-bottom: 30px;
font-size: 0.9em;
}
.meta span {
margin-right: 20px;
}
.content {
font-size: 1.1em;
line-height: 1.8;
margin-bottom: 30px;
}
.tags {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.tag {
display: inline-block;
background: #3498db;
color: white;
padding: 5px 12px;
margin-right: 10px;
margin-bottom: 10px;
border-radius: 20px;
font-size: 0.85em;
text-decoration: none;
}
.tag:hover {
background: #2980b9;
}
.reading-time {
color: #95a5a6;
font-style: italic;
}
.share-buttons {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.share-button {
display: inline-block;
padding: 10px 20px;
margin-right: 10px;
margin-bottom: 10px;
background: #34495e;
color: white;
text-decoration: none;
border-radius: 5px;
font-size: 0.9em;
}
.share-button:hover {
background: #2c3e50;
}
.back-link {
margin-top: 40px;
text-align: center;
}
.back-link a {
color: #3498db;
text-decoration: none;
font-weight: 500;
}
.back-link a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<article>
<header>
<h1>{{title}}</h1>
<div class="meta">
{% if author %}
<span>By {{author}}</span>
{% endif %}
{% if published_date %}
<span>Published on {{published_date | date_format(format="%B %d, %Y")}}</span>
{% endif %}
{% if reading_time %}
<span class="reading-time">{{reading_time}} min read</span>
{% endif %}
</div>
</header>
<div class="content">
{% if featured_image %}
<img src="{{featured_image}}" alt="{{title}}" style="width: 100%; height: auto; margin-bottom: 20px; border-radius: 5px;">
{% endif %}
{{content | markdown | safe}}
</div>
{% if tags %}
<div class="tags">
<strong>Tags:</strong>
{% for tag in tags %}
<a href="/tag/{{tag | slug}}" class="tag">{{tag}}</a>
{% endfor %}
</div>
{% endif %}
{% if enable_sharing %}
<div class="share-buttons">
<strong>Share this post:</strong><br>
<a href="https://twitter.com/intent/tweet?text={{title | urlencode}}&url={{page_url | urlencode}}" class="share-button" target="_blank">Twitter</a>
<a href="https://www.facebook.com/sharer/sharer.php?u={{page_url | urlencode}}" class="share-button" target="_blank">Facebook</a>
<a href="https://www.linkedin.com/sharing/share-offsite/?url={{page_url | urlencode}}" class="share-button" target="_blank">LinkedIn</a>
<a href="mailto:?subject={{title | urlencode}}&body={{page_url | urlencode}}" class="share-button">Email</a>
</div>
{% endif %}
</article>
<div class="back-link">
<a href="{{back_url | default(value='/')}}">&larr; Back to {{back_text | default(value='Home')}}</a>
</div>
</div>
{% if analytics_id %}
<script>
// Add analytics tracking here if needed
console.log('Analytics ID: {{analytics_id}}');
</script>
{% endif %}
</body>
</html>

296
templates/email/README.md Normal file
View File

@ -0,0 +1,296 @@
# Email Templates Structure
This directory contains internationalized email templates for the Rustelo framework. Templates are organized by language and support both HTML and text formats.
## Directory Structure
```
templates/email/
├── en_/ # English templates (default language)
│ ├── html/ # HTML email templates
│ │ ├── contact.hbs # Contact form submission
│ │ ├── notification.hbs # General notifications
│ │ └── welcome.hbs # Welcome email (example)
│ └── text/ # Plain text email templates
│ ├── contact.hbs # Contact form submission
│ ├── notification.hbs # General notifications
│ └── welcome.hbs # Welcome email (example)
├── es_/ # Spanish templates
│ ├── html/
│ └── text/
├── fr_/ # French templates (example)
│ ├── html/
│ └── text/
└── README.md # This file
```
## Language Naming Convention
- Language directories must end with an underscore (`_`)
- Use ISO 639-1 language codes (e.g., `en`, `es`, `fr`, `de`)
- Examples: `en_`, `es_`, `fr_`, `de_`, `it_`, `pt_`, `ru_`, `ja_`, `ko_`, `zh_`
## Template Naming Convention
Templates are automatically registered with the naming pattern: `{language}_{template_name}_{format}`
Examples:
- `en_contact_html` - English contact form HTML template
- `es_contact_text` - Spanish contact form text template
- `fr_notification_html` - French notification HTML template
## Language Fallback
The email system provides automatic language fallback:
1. **Requested Language**: First tries the specific language (e.g., `es_contact_html`)
2. **Default Language**: Falls back to English (`en_contact_html`)
3. **Legacy Support**: Falls back to templates without language prefix (`contact_html`)
## Creating New Templates
### 1. Create Language Directory
```bash
mkdir -p templates/email/fr_/html
mkdir -p templates/email/fr_/text
```
### 2. Create Template Files
Create both HTML and text versions for each template:
```bash
# HTML version
touch templates/email/fr_/html/contact.hbs
touch templates/email/fr_/html/notification.hbs
# Text version
touch templates/email/fr_/text/contact.hbs
touch templates/email/fr_/text/notification.hbs
```
### 3. Template Content Structure
Each template should use Handlebars syntax and include appropriate variables:
**Contact Form Variables:**
- `{{name}}` - Sender's name
- `{{email}}` - Sender's email
- `{{subject}}` - Message subject
- `{{message}}` - Message content
- `{{form_type}}` - Type of form (contact, support, etc.)
- `{{submitted_at}}` - Submission timestamp
- `{{ip_address}}` - Sender's IP address (optional)
- `{{user_agent}}` - Sender's browser info (optional)
- `{{fields}}` - Additional form fields (optional)
**Notification Variables:**
- `{{title}}` - Notification title
- `{{message}}` - Notification message
- `{{content}}` - Additional content (optional)
- `{{type}}` - Notification type (info, warning, success, danger)
- `{{action_url}}` - Call-to-action URL (optional)
- `{{action_text}}` - Call-to-action text (optional)
- `{{timestamp}}` - Notification timestamp
## Available Handlebars Helpers
The email system provides several custom helpers:
### Date Formatting
```handlebars
{{date_format submitted_at "%B %d, %Y at %I:%M %p UTC"}}
{{date_format submitted_at "%d de %B de %Y a las %H:%M UTC"}}
```
### Text Manipulation
```handlebars
{{capitalize form_type}} <!-- "contact" → "Contact" -->
{{truncate user_agent 100}} <!-- Limit text to 100 characters -->
{{default action_text "Click Here"}} <!-- Use default if empty -->
{{url_encode email}} <!-- URL encode text -->
```
### Conditional Content
```handlebars
{{#if fields}}
Additional fields are present
{{/if}}
{{#each fields}}
{{@key}}: {{this}}
{{/each}}
```
## Language Detection
The email system automatically detects the preferred language from:
1. **User Profile**: User's saved language preference (if authenticated)
2. **Request Headers**: `Accept-Language` HTTP header
3. **Default Fallback**: English (`en`)
### Supported Languages
The system currently supports these language codes:
- `en` - English
- `es` - Spanish
- `fr` - French
- `de` - German
- `it` - Italian
- `pt` - Portuguese
- `ru` - Russian
- `ja` - Japanese
- `ko` - Korean
- `zh` - Chinese
## Using Templates in Code
### Server-side Usage
```rust
// Send with automatic language detection
email_service.send_contact_form(
"John Doe",
"john@example.com",
"Question",
"Message content",
"admin@app.com"
).await?;
// Send with specific language
email_service.send_contact_form_with_language(
"Juan Pérez",
"juan@example.com",
"Pregunta",
"Contenido del mensaje",
"admin@app.com",
"es" // Spanish
).await?;
// Send templated email
email_service.send_templated_email_with_language(
"user@example.com",
"Welcome!",
"welcome",
template_data,
"fr" // French
).await?;
```
### Client-side Language
The contact and support form components automatically detect language from browser headers:
```rust
// Language is automatically detected from Accept-Language header
<ContactForm
recipient="contact@yourapp.com"
title="Contact Us"
/>
```
## Template Best Practices
### 1. Consistency
- Use consistent styling across all language versions
- Maintain the same structure and layout
- Keep variable names identical across languages
### 2. Cultural Considerations
- Adapt date formats for different regions
- Consider right-to-left languages if needed
- Use appropriate salutations and closings
- Respect cultural communication norms
### 3. Content Guidelines
- Keep translations accurate and natural
- Use professional tone appropriate for business communication
- Include all necessary legal disclaimers
- Test with native speakers when possible
### 4. HTML Best Practices
- Use semantic HTML structure
- Include proper `lang` attribute in HTML templates
- Ensure responsive design for mobile devices
- Use accessible color contrasts
- Include fallback fonts for different character sets
### 5. Text Templates
- Keep plain text versions readable and well-formatted
- Use appropriate line breaks and spacing
- Include all important information from HTML version
- Test rendering in various email clients
## Testing Templates
### 1. Development Testing
Use the console provider to see rendered templates:
```toml
[email]
provider = "console"
```
### 2. Language Testing
Test specific languages by setting Accept-Language header:
```bash
curl -H "Accept-Language: es-ES,es;q=0.9" \
-X POST /api/email/contact \
-d '{"name":"Test","email":"test@example.com","subject":"Test","message":"Test"}'
```
### 3. Template Validation
- Verify all variables are properly escaped
- Check for proper fallback behavior
- Test with various data inputs
- Validate HTML structure and accessibility
## Migration from Legacy Templates
If you have existing templates without language prefixes:
1. **Backup existing templates**
2. **Create language directories** (`en_/html/`, `en_/text/`)
3. **Move templates** to appropriate language directories
4. **Update template names** to remove language prefixes
5. **Test thoroughly** to ensure proper fallback behavior
## Configuration
Email template directory is configured in your application config:
```toml
[email]
# Template directory (relative to root_path)
template_dir = "templates/email"
```
Or via environment variable:
```bash
EMAIL_TEMPLATE_DIR=/path/to/templates/email
```
## Troubleshooting
### Template Not Found Errors
1. Check directory structure and naming convention
2. Verify template files exist and are readable
3. Check log output for specific template name being requested
4. Ensure language fallback is working properly
### Language Detection Issues
1. Verify Accept-Language header format
2. Check supported language list
3. Test with explicit language parameter
4. Review language detection logs
### Rendering Issues
1. Validate Handlebars syntax
2. Check for missing variables
3. Test with sample data
4. Verify helper function usage
For more detailed troubleshooting, see the main email system documentation.

View File

@ -0,0 +1,217 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact Form Submission</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #f8f9fa;
}
.container {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
margin: 0;
font-size: 28px;
font-weight: 600;
}
.header p {
margin: 10px 0 0 0;
opacity: 0.9;
font-size: 16px;
}
.content {
padding: 30px;
}
.field {
margin-bottom: 20px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 6px;
border-left: 4px solid #667eea;
}
.field-label {
font-weight: 600;
color: #495057;
font-size: 14px;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
}
.field-value {
color: #212529;
font-size: 16px;
line-height: 1.5;
}
.message-field {
background-color: #fff;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 20px;
margin-top: 10px;
}
.message-field .field-value {
white-space: pre-wrap;
word-wrap: break-word;
}
.metadata {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #dee2e6;
background-color: #f8f9fa;
padding: 20px;
border-radius: 6px;
}
.metadata h3 {
margin: 0 0 15px 0;
color: #495057;
font-size: 16px;
font-weight: 600;
}
.metadata-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 14px;
}
.metadata-label {
color: #6c757d;
font-weight: 500;
}
.metadata-value {
color: #495057;
}
.footer {
background-color: #f8f9fa;
padding: 20px;
text-align: center;
border-top: 1px solid #dee2e6;
}
.footer p {
margin: 0;
color: #6c757d;
font-size: 14px;
}
.highlight {
background-color: #fff3cd;
border: 1px solid #ffeaa7;
padding: 15px;
border-radius: 6px;
margin-bottom: 20px;
}
.highlight strong {
color: #856404;
}
@media (max-width: 600px) {
body {
padding: 10px;
}
.header, .content {
padding: 20px;
}
.metadata-item {
flex-direction: column;
margin-bottom: 12px;
}
.metadata-label {
margin-bottom: 4px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📧 New Contact Form Submission</h1>
<p>You have received a new message from your website</p>
</div>
<div class="content">
<div class="highlight">
<strong>Reply directly to this email</strong> to respond to the sender, or use the contact information below.
</div>
<div class="field">
<div class="field-label">Name</div>
<div class="field-value">{{name}}</div>
</div>
<div class="field">
<div class="field-label">Email Address</div>
<div class="field-value">
<a href="mailto:{{email}}" style="color: #667eea; text-decoration: none;">{{email}}</a>
</div>
</div>
<div class="field">
<div class="field-label">Subject</div>
<div class="field-value">{{subject}}</div>
</div>
<div class="field">
<div class="field-label">Message</div>
<div class="message-field">
<div class="field-value">{{message}}</div>
</div>
</div>
{{#if fields}}
<div class="field">
<div class="field-label">Additional Information</div>
<div style="margin-top: 10px;">
{{#each fields}}
<div style="margin-bottom: 10px; padding: 10px; background-color: #fff; border-radius: 4px; border-left: 3px solid #28a745;">
<strong>{{@key}}:</strong> {{this}}
</div>
{{/each}}
</div>
</div>
{{/if}}
<div class="metadata">
<h3>Submission Details</h3>
<div class="metadata-item">
<span class="metadata-label">Submitted:</span>
<span class="metadata-value">{{date_format submitted_at "%B %d, %Y at %I:%M %p UTC"}}</span>
</div>
<div class="metadata-item">
<span class="metadata-label">Form Type:</span>
<span class="metadata-value">{{capitalize form_type}}</span>
</div>
{{#if ip_address}}
<div class="metadata-item">
<span class="metadata-label">IP Address:</span>
<span class="metadata-value">{{ip_address}}</span>
</div>
{{/if}}
{{#if user_agent}}
<div class="metadata-item">
<span class="metadata-label">User Agent:</span>
<span class="metadata-value">{{truncate user_agent 100}}</span>
</div>
{{/if}}
</div>
</div>
<div class="footer">
<p>This email was generated automatically from your website contact form.</p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,181 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #f8f9fa;
}
.container {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
margin: 0;
font-size: 28px;
font-weight: 600;
}
.header .icon {
font-size: 48px;
margin-bottom: 15px;
}
.content {
padding: 30px;
}
.message {
background-color: #f8f9fa;
border-left: 4px solid #007bff;
padding: 20px;
margin-bottom: 20px;
border-radius: 0 6px 6px 0;
}
.message p {
margin: 0;
font-size: 16px;
line-height: 1.6;
}
.additional-content {
background-color: #fff;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 20px;
margin-top: 20px;
}
.additional-content h3 {
margin: 0 0 15px 0;
color: #495057;
font-size: 18px;
font-weight: 600;
}
.additional-content .content-body {
color: #212529;
line-height: 1.6;
}
.footer {
background-color: #f8f9fa;
padding: 20px;
text-align: center;
border-top: 1px solid #dee2e6;
}
.footer p {
margin: 0;
color: #6c757d;
font-size: 14px;
}
.timestamp {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #dee2e6;
text-align: center;
color: #6c757d;
font-size: 14px;
}
.alert {
padding: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 6px;
}
.alert-info {
color: #0c5460;
background-color: #d1ecf1;
border-color: #bee5eb;
}
.alert-warning {
color: #856404;
background-color: #fff3cd;
border-color: #ffeaa7;
}
.alert-success {
color: #155724;
background-color: #d4edda;
border-color: #c3e6cb;
}
.alert-danger {
color: #721c24;
background-color: #f8d7da;
border-color: #f5c6cb;
}
@media (max-width: 600px) {
body {
padding: 10px;
}
.header, .content {
padding: 20px;
}
.header h1 {
font-size: 24px;
}
.header .icon {
font-size: 36px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="icon">🔔</div>
<h1>{{title}}</h1>
</div>
<div class="content">
{{#if type}}
<div class="alert alert-{{type}}">
<strong>{{capitalize type}}:</strong> This is a {{type}} notification.
</div>
{{/if}}
<div class="message">
<p>{{message}}</p>
</div>
{{#if content}}
<div class="additional-content">
<h3>Additional Information</h3>
<div class="content-body">
{{{content}}}
</div>
</div>
{{/if}}
{{#if action_url}}
<div style="text-align: center; margin: 30px 0;">
<a href="{{action_url}}" style="display: inline-block; padding: 12px 24px; background-color: #007bff; color: white; text-decoration: none; border-radius: 6px; font-weight: 600;">
{{default action_text "Take Action"}}
</a>
</div>
{{/if}}
<div class="timestamp">
<p>Sent on {{date_format (default timestamp "now") "%B %d, %Y at %I:%M %p UTC"}}</p>
</div>
</div>
<div class="footer">
<p>This is an automated notification. Please do not reply to this email.</p>
{{#if unsubscribe_url}}
<p style="margin-top: 10px;">
<a href="{{unsubscribe_url}}" style="color: #6c757d; font-size: 12px;">Unsubscribe from notifications</a>
</p>
{{/if}}
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,32 @@
=== CONTACT FORM SUBMISSION ===
You have received a new message from your website contact form.
CONTACT DETAILS:
Name: {{name}}
Email: {{email}}
Subject: {{subject}}
MESSAGE:
{{message}}
{{#if fields}}
ADDITIONAL INFORMATION:
{{#each fields}}
{{@key}}: {{this}}
{{/each}}
{{/if}}
SUBMISSION DETAILS:
Submitted: {{date_format submitted_at "%B %d, %Y at %I:%M %p UTC"}}
Form Type: {{capitalize form_type}}
{{#if ip_address}}
IP Address: {{ip_address}}
{{/if}}
{{#if user_agent}}
User Agent: {{truncate user_agent 100}}
{{/if}}
---
This email was generated automatically from your website contact form.
To reply to the sender, use their email address: {{email}}

View File

@ -0,0 +1,29 @@
=== NOTIFICATION ===
{{title}}
{{#if type}}
[{{uppercase type}}] This is a {{type}} notification.
{{/if}}
{{message}}
{{#if content}}
ADDITIONAL INFORMATION:
{{content}}
{{/if}}
{{#if action_url}}
ACTION REQUIRED:
{{default action_text "Take Action"}}: {{action_url}}
{{/if}}
---
Sent on {{date_format (default timestamp "now") "%B %d, %Y at %I:%M %p UTC"}}
This is an automated notification. Please do not reply to this email.
{{#if unsubscribe_url}}
To unsubscribe from notifications, visit: {{unsubscribe_url}}
{{/if}}

View File

@ -0,0 +1,217 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Formulario de Contacto Recibido</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #f8f9fa;
}
.container {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
margin: 0;
font-size: 28px;
font-weight: 600;
}
.header p {
margin: 10px 0 0 0;
opacity: 0.9;
font-size: 16px;
}
.content {
padding: 30px;
}
.field {
margin-bottom: 20px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 6px;
border-left: 4px solid #667eea;
}
.field-label {
font-weight: 600;
color: #495057;
font-size: 14px;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
}
.field-value {
color: #212529;
font-size: 16px;
line-height: 1.5;
}
.message-field {
background-color: #fff;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 20px;
margin-top: 10px;
}
.message-field .field-value {
white-space: pre-wrap;
word-wrap: break-word;
}
.metadata {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #dee2e6;
background-color: #f8f9fa;
padding: 20px;
border-radius: 6px;
}
.metadata h3 {
margin: 0 0 15px 0;
color: #495057;
font-size: 16px;
font-weight: 600;
}
.metadata-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 14px;
}
.metadata-label {
color: #6c757d;
font-weight: 500;
}
.metadata-value {
color: #495057;
}
.footer {
background-color: #f8f9fa;
padding: 20px;
text-align: center;
border-top: 1px solid #dee2e6;
}
.footer p {
margin: 0;
color: #6c757d;
font-size: 14px;
}
.highlight {
background-color: #fff3cd;
border: 1px solid #ffeaa7;
padding: 15px;
border-radius: 6px;
margin-bottom: 20px;
}
.highlight strong {
color: #856404;
}
@media (max-width: 600px) {
body {
padding: 10px;
}
.header, .content {
padding: 20px;
}
.metadata-item {
flex-direction: column;
margin-bottom: 12px;
}
.metadata-label {
margin-bottom: 4px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📧 Nuevo Formulario de Contacto</h1>
<p>Has recibido un nuevo mensaje desde tu sitio web</p>
</div>
<div class="content">
<div class="highlight">
<strong>Responde directamente a este correo</strong> para contestar al remitente, o usa la información de contacto a continuación.
</div>
<div class="field">
<div class="field-label">Nombre</div>
<div class="field-value">{{name}}</div>
</div>
<div class="field">
<div class="field-label">Dirección de Correo</div>
<div class="field-value">
<a href="mailto:{{email}}" style="color: #667eea; text-decoration: none;">{{email}}</a>
</div>
</div>
<div class="field">
<div class="field-label">Asunto</div>
<div class="field-value">{{subject}}</div>
</div>
<div class="field">
<div class="field-label">Mensaje</div>
<div class="message-field">
<div class="field-value">{{message}}</div>
</div>
</div>
{{#if fields}}
<div class="field">
<div class="field-label">Información Adicional</div>
<div style="margin-top: 10px;">
{{#each fields}}
<div style="margin-bottom: 10px; padding: 10px; background-color: #fff; border-radius: 4px; border-left: 3px solid #28a745;">
<strong>{{@key}}:</strong> {{this}}
</div>
{{/each}}
</div>
</div>
{{/if}}
<div class="metadata">
<h3>Detalles del Envío</h3>
<div class="metadata-item">
<span class="metadata-label">Enviado:</span>
<span class="metadata-value">{{date_format submitted_at "%d de %B de %Y a las %H:%M UTC"}}</span>
</div>
<div class="metadata-item">
<span class="metadata-label">Tipo de Formulario:</span>
<span class="metadata-value">{{capitalize form_type}}</span>
</div>
{{#if ip_address}}
<div class="metadata-item">
<span class="metadata-label">Dirección IP:</span>
<span class="metadata-value">{{ip_address}}</span>
</div>
{{/if}}
{{#if user_agent}}
<div class="metadata-item">
<span class="metadata-label">Navegador:</span>
<span class="metadata-value">{{truncate user_agent 100}}</span>
</div>
{{/if}}
</div>
</div>
<div class="footer">
<p>Este correo fue generado automáticamente desde el formulario de contacto de tu sitio web.</p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,181 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #f8f9fa;
}
.container {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
margin: 0;
font-size: 28px;
font-weight: 600;
}
.header .icon {
font-size: 48px;
margin-bottom: 15px;
}
.content {
padding: 30px;
}
.message {
background-color: #f8f9fa;
border-left: 4px solid #007bff;
padding: 20px;
margin-bottom: 20px;
border-radius: 0 6px 6px 0;
}
.message p {
margin: 0;
font-size: 16px;
line-height: 1.6;
}
.additional-content {
background-color: #fff;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 20px;
margin-top: 20px;
}
.additional-content h3 {
margin: 0 0 15px 0;
color: #495057;
font-size: 18px;
font-weight: 600;
}
.additional-content .content-body {
color: #212529;
line-height: 1.6;
}
.footer {
background-color: #f8f9fa;
padding: 20px;
text-align: center;
border-top: 1px solid #dee2e6;
}
.footer p {
margin: 0;
color: #6c757d;
font-size: 14px;
}
.timestamp {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #dee2e6;
text-align: center;
color: #6c757d;
font-size: 14px;
}
.alert {
padding: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 6px;
}
.alert-info {
color: #0c5460;
background-color: #d1ecf1;
border-color: #bee5eb;
}
.alert-warning {
color: #856404;
background-color: #fff3cd;
border-color: #ffeaa7;
}
.alert-success {
color: #155724;
background-color: #d4edda;
border-color: #c3e6cb;
}
.alert-danger {
color: #721c24;
background-color: #f8d7da;
border-color: #f5c6cb;
}
@media (max-width: 600px) {
body {
padding: 10px;
}
.header, .content {
padding: 20px;
}
.header h1 {
font-size: 24px;
}
.header .icon {
font-size: 36px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="icon">🔔</div>
<h1>{{title}}</h1>
</div>
<div class="content">
{{#if type}}
<div class="alert alert-{{type}}">
<strong>{{capitalize type}}:</strong> Esta es una notificación {{type}}.
</div>
{{/if}}
<div class="message">
<p>{{message}}</p>
</div>
{{#if content}}
<div class="additional-content">
<h3>Información Adicional</h3>
<div class="content-body">
{{{content}}}
</div>
</div>
{{/if}}
{{#if action_url}}
<div style="text-align: center; margin: 30px 0;">
<a href="{{action_url}}" style="display: inline-block; padding: 12px 24px; background-color: #007bff; color: white; text-decoration: none; border-radius: 6px; font-weight: 600;">
{{default action_text "Tomar Acción"}}
</a>
</div>
{{/if}}
<div class="timestamp">
<p>Enviado el {{date_format (default timestamp "now") "%d de %B de %Y a las %H:%M UTC"}}</p>
</div>
</div>
<div class="footer">
<p>Esta es una notificación automática. Por favor no respondas a este correo.</p>
{{#if unsubscribe_url}}
<p style="margin-top: 10px;">
<a href="{{unsubscribe_url}}" style="color: #6c757d; font-size: 12px;">Cancelar suscripción de notificaciones</a>
</p>
{{/if}}
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,32 @@
=== FORMULARIO DE CONTACTO ===
Has recibido un nuevo mensaje desde el formulario de contacto de tu sitio web.
DETALLES DEL CONTACTO:
Nombre: {{name}}
Correo: {{email}}
Asunto: {{subject}}
MENSAJE:
{{message}}
{{#if fields}}
INFORMACIÓN ADICIONAL:
{{#each fields}}
{{@key}}: {{this}}
{{/each}}
{{/if}}
DETALLES DEL ENVÍO:
Enviado: {{date_format submitted_at "%d de %B de %Y a las %H:%M UTC"}}
Tipo de Formulario: {{capitalize form_type}}
{{#if ip_address}}
Dirección IP: {{ip_address}}
{{/if}}
{{#if user_agent}}
Navegador: {{truncate user_agent 100}}
{{/if}}
---
Este correo fue generado automáticamente desde el formulario de contacto de tu sitio web.
Para responder al remitente, usa su dirección de correo: {{email}}

View File

@ -0,0 +1,29 @@
=== NOTIFICACIÓN ===
{{title}}
{{#if type}}
[{{uppercase type}}] Esta es una notificación {{type}}.
{{/if}}
{{message}}
{{#if content}}
INFORMACIÓN ADICIONAL:
{{content}}
{{/if}}
{{#if action_url}}
ACCIÓN REQUERIDA:
{{default action_text "Tomar Acción"}}: {{action_url}}
{{/if}}
---
Enviado el {{date_format (default timestamp "now") "%d de %B de %Y a las %H:%M UTC"}}
Esta es una notificación automática. Por favor no respondas a este correo.
{{#if unsubscribe_url}}
Para cancelar la suscripción de notificaciones, visita: {{unsubscribe_url}}
{{/if}}

361
templates/page.html Normal file
View File

@ -0,0 +1,361 @@
<!DOCTYPE html>
<html lang="{{lang | default(value='en')}}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}} - {{site_name | default(value="My Website")}}</title>
<meta name="description" content="{{description | default(value=content | excerpt(length=160))}}">
{% if author %}
<meta name="author" content="{{author}}">
{% endif %}
{% if keywords %}
<meta name="keywords" content="{{keywords | join(sep=', ')}}">
{% endif %}
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
color: #333;
background-color: #ffffff;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem 0;
text-align: center;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 0.5rem;
font-weight: 700;
}
.header .subtitle {
font-size: 1.2rem;
opacity: 0.9;
font-weight: 300;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.main-content {
display: grid;
grid-template-columns: 1fr 300px;
gap: 40px;
margin: 40px auto;
max-width: 1200px;
padding: 0 20px;
}
.content {
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
min-height: 400px;
}
.content h2 {
color: #2c3e50;
margin-bottom: 20px;
font-size: 1.8rem;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.content h3 {
color: #34495e;
margin: 30px 0 15px 0;
font-size: 1.4rem;
}
.content p {
margin-bottom: 20px;
font-size: 1.1rem;
line-height: 1.8;
}
.content ul, .content ol {
margin-bottom: 20px;
padding-left: 30px;
}
.content li {
margin-bottom: 8px;
font-size: 1.1rem;
}
.content blockquote {
border-left: 4px solid #3498db;
padding-left: 20px;
margin: 20px 0;
font-style: italic;
color: #555;
}
.content code {
background: #f8f9fa;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.9em;
}
.content pre {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
overflow-x: auto;
margin: 20px 0;
}
.content pre code {
background: none;
padding: 0;
}
.sidebar {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
height: fit-content;
position: sticky;
top: 20px;
}
.sidebar h3 {
color: #2c3e50;
margin-bottom: 20px;
font-size: 1.3rem;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.sidebar ul {
list-style: none;
}
.sidebar li {
margin-bottom: 10px;
}
.sidebar a {
color: #3498db;
text-decoration: none;
font-weight: 500;
}
.sidebar a:hover {
text-decoration: underline;
}
.meta-info {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-bottom: 20px;
font-size: 0.9rem;
color: #666;
}
.meta-info strong {
color: #333;
}
.breadcrumbs {
background: #f8f9fa;
padding: 15px 0;
border-bottom: 1px solid #eee;
}
.breadcrumbs a {
color: #3498db;
text-decoration: none;
margin-right: 10px;
}
.breadcrumbs a:hover {
text-decoration: underline;
}
.breadcrumbs span {
color: #666;
margin-right: 10px;
}
.footer {
background: #2c3e50;
color: white;
text-align: center;
padding: 40px 0;
margin-top: 60px;
}
.footer p {
margin-bottom: 10px;
opacity: 0.8;
}
.footer a {
color: #3498db;
text-decoration: none;
}
.footer a:hover {
text-decoration: underline;
}
.cta-section {
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
color: white;
padding: 40px;
border-radius: 8px;
text-align: center;
margin: 40px 0;
}
.cta-section h3 {
font-size: 1.8rem;
margin-bottom: 15px;
}
.cta-section p {
font-size: 1.1rem;
margin-bottom: 25px;
opacity: 0.9;
}
.cta-button {
display: inline-block;
background: white;
color: #3498db;
padding: 15px 30px;
border-radius: 5px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
}
.cta-button:hover {
background: #f8f9fa;
transform: translateY(-2px);
}
@media (max-width: 768px) {
.main-content {
grid-template-columns: 1fr;
gap: 20px;
margin: 20px auto;
padding: 0 15px;
}
.content, .sidebar {
padding: 20px;
}
.header h1 {
font-size: 2rem;
}
.header .subtitle {
font-size: 1rem;
}
}
</style>
</head>
<body>
{% if breadcrumbs %}
<div class="breadcrumbs">
<div class="container">
{% for crumb in breadcrumbs %}
{% if not loop.last %}
<a href="{{crumb.url}}">{{crumb.title}}</a>
<span>&gt;</span>
{% else %}
<span>{{crumb.title}}</span>
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
<header class="header">
<div class="container">
<h1>{{title}}</h1>
{% if subtitle %}
<p class="subtitle">{{subtitle}}</p>
{% endif %}
</div>
</header>
<div class="main-content">
<main class="content">
{% if show_meta %}
<div class="meta-info">
{% if last_updated %}
<strong>Last Updated:</strong> {{last_updated | date_format(format="%B %d, %Y")}}
{% endif %}
{% if author %}
<br><strong>Author:</strong> {{author}}
{% endif %}
{% if reading_time %}
<br><strong>Reading Time:</strong> {{reading_time}} minutes
{% endif %}
</div>
{% endif %}
{% if featured_image %}
<img src="{{featured_image}}" alt="{{title}}" style="width: 100%; height: auto; margin-bottom: 30px; border-radius: 5px;">
{% endif %}
{{content | markdown | safe}}
{% if cta_enabled %}
<div class="cta-section">
<h3>{{cta_title | default(value="Ready to Get Started?")}}</h3>
<p>{{cta_description | default(value="Take the next step and explore what we have to offer.")}}</p>
<a href="{{cta_url | default(value='/contact')}}" class="cta-button">{{cta_button_text | default(value="Get Started")}}</a>
</div>
{% endif %}
</main>
<aside class="sidebar">
{% if toc_enabled and toc %}
<h3>Table of Contents</h3>
<ul>
{% for item in toc %}
<li><a href="#{{item.anchor}}">{{item.title}}</a></li>
{% endfor %}
</ul>
{% endif %}
{% if sidebar_links %}
<h3>{{sidebar_title | default(value="Quick Links")}}</h3>
<ul>
{% for link in sidebar_links %}
<li><a href="{{link.url}}">{{link.title}}</a></li>
{% endfor %}
</ul>
{% endif %}
{% if contact_info %}
<h3>Contact Information</h3>
<div class="meta-info">
{% if contact_info.email %}
<strong>Email:</strong> <a href="mailto:{{contact_info.email}}">{{contact_info.email}}</a><br>
{% endif %}
{% if contact_info.phone %}
<strong>Phone:</strong> {{contact_info.phone}}<br>
{% endif %}
{% if contact_info.address %}
<strong>Address:</strong> {{contact_info.address}}
{% endif %}
</div>
{% endif %}
{% if related_pages %}
<h3>Related Pages</h3>
<ul>
{% for page in related_pages %}
<li><a href="{{page.url}}">{{page.title}}</a></li>
{% endfor %}
</ul>
{% endif %}
</aside>
</div>
<footer class="footer">
<div class="container">
<p>&copy; {{current_year | default(value="2024")}} {{site_name | default(value="My Website")}}. All rights reserved.</p>
{% if footer_links %}
<p>
{% for link in footer_links %}
<a href="{{link.url}}">{{link.title}}</a>
{% if not loop.last %} | {% endif %}
{% endfor %}
</p>
{% endif %}
</div>
</footer>
{% if analytics_id %}
<script>
// Add analytics tracking here if needed
console.log('Analytics ID: {{analytics_id}}');
</script>
{% endif %}
</body>
</html>