loreal-global-kickoff/EmailTemplates.php
DJP 95020fad44 Add Title/Creative Execution columns and HTML email templates
CSV Transformation Fixes:
- Title now includes language code: "{OriginalTitle}_{ISO}" (e.g., "Syndication_en-GB")
- Added "Creative Execution" column with original global title
- Both columns properly populated for all 16 regional CSVs

Email Template System:
- Created EmailTemplates.php with professional HTML templates
- Based on Ferrero automation email styling
- Templates for all workflows:
  * Asset Submission Success/Failed
  * Global to Local Started/Complete/Failed
  * Box Upload Success
- L'Oréal brand colors (Yellow #FFC407, Black #000000, Green for success)
- Responsive design with proper HTML structure
- Clean, professional layout with color-coded status boxes

Email Service Enhancements:
- Added sendTemplate() method for templated emails
- SMTP now supports HTML multipart emails (text + HTML)
- Mailgun API support for HTML
- Proper MIME boundaries and headers
- Extract subject from template HTML

Notification Updates:
- upload-to-box.php: Uses templates with full data (campaign, business unit, file count)
- submit.php: Logs all asset submissions
- All emails sent as professional HTML with fallback text

Template Features:
- Color-coded headers (green=success, red=error, yellow=warning)
- Info boxes with campaign details
- Data tables for multiple items
- Action required sections
- Footer with branding

All notifications now send beautiful, branded HTML emails to users!

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 11:00:10 -05:00

283 lines
9.3 KiB
PHP

<?php
/**
* Email Templates
* HTML email templates for L'Oréal OMG Assistant notifications
* Based on Ferrero automation email styling
*/
class EmailTemplates {
/**
* Get base template wrapper
*/
private static function wrapTemplate($title, $content, $color = '#FFC407') {
return <<<HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f5f5f5; }
.container { max-width: 900px; margin: 0 auto; background-color: white; }
.header { background-color: {$color}; color: #000000; padding: 30px 20px; text-align: center; }
.header h1 { margin: 0; font-size: 28px; font-weight: bold; }
.content { padding: 30px; }
.footer { background-color: #f8f9fa; padding: 20px; text-align: center; color: #666; font-size: 12px; }
.info-box { background-color: #f9f9f9; border-left: 4px solid {$color}; padding: 15px; margin: 20px 0; }
.info-box p { margin: 5px 0; }
.success-box { background-color: #d4edda; border-left: 4px solid #28a745; padding: 15px; margin: 20px 0; }
.error-box { background-color: #ffebee; border-left: 4px solid #d32f2f; padding: 15px; margin: 20px 0; }
.warning-box { background-color: #fff3cd; border-left: 4px solid #ff9800; padding: 15px; margin: 20px 0; }
.data-table { width: 100%; border-collapse: collapse; margin: 15px 0; }
.data-table th { background-color: #000000; color: {$color}; padding: 12px; text-align: left; }
.data-table td { padding: 10px; border-bottom: 1px solid #eee; }
code { background-color: #f5f5f5; padding: 2px 6px; border-radius: 3px; font-family: 'Courier New', monospace; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>{$title}</h1>
</div>
<div class="content">
{$content}
</div>
<div class="footer">
<p><strong>L'Oréal OMG Assistant Global</strong></p>
<p>Automated notification - Do not reply to this email</p>
</div>
</div>
</body>
</html>
HTML;
}
/**
* Master Asset Submission - Success
*/
public static function assetSubmissionSuccess($data) {
$content = <<<HTML
<div class="success-box">
<p><strong>✅ Master Asset Submission Complete</strong></p>
</div>
<div class="info-box">
<p><strong>Box ID:</strong> <code>{$data['box_id']}</code></p>
<p><strong>Campaign Number:</strong> {$data['campaign_number']}</p>
<p><strong>Folder:</strong> {$data['folder_name']}</p>
<p><strong>Total Assets:</strong> {$data['asset_count']} items</p>
</div>
<h3>Campaign Dates:</h3>
<div class="info-box">
<p><strong>Supply Date:</strong> {$data['supply_date']}</p>
<p><strong>Live Date:</strong> {$data['live_date']}</p>
<p><strong>End Date:</strong> {$data['end_date']}</p>
</div>
<div class="success-box">
<p><strong>✓ Status:</strong> Asset metadata successfully submitted to Make.com workflow.</p>
<p>Your assets are now being processed in the OMG system.</p>
</div>
HTML;
return self::wrapTemplate('✅ Asset Submission Complete', $content, '#28a745');
}
/**
* Master Asset Submission - Failed
*/
public static function assetSubmissionFailed($data) {
$content = <<<HTML
<div class="error-box">
<p><strong>❌ Asset Submission Failed</strong></p>
</div>
<div class="info-box">
<p><strong>Box ID:</strong> <code>{$data['box_id']}</code></p>
<p><strong>Campaign Number:</strong> {$data['campaign_number']}</p>
</div>
<h3>Error Details:</h3>
<div class="error-box">
<p style="color: #d32f2f;"><strong>Error:</strong> {$data['error']}</p>
</div>
<div class="warning-box">
<p><strong>📌 Action Required:</strong> Please review the error and try again.</p>
<p>If the issue persists, contact the system administrator.</p>
</div>
HTML;
return self::wrapTemplate('❌ Asset Submission Failed', $content, '#d32f2f');
}
/**
* Global to Local - Processing Started
*/
public static function globalToLocalStarted($data) {
$content = <<<HTML
<div class="info-box">
<p><strong>📄 File:</strong> <code>{$data['filename']}</code></p>
<p><strong>Campaign Number:</strong> {$data['campaign_number']}</p>
</div>
<h3>Processing Steps:</h3>
<div class="info-box">
<p>✓ File uploaded and validated</p>
<p>🔄 Extracting campaign information...</p>
<p>🔄 Calling OMG API for business unit...</p>
<p>🔄 Transforming data for 16 regional markets...</p>
</div>
<div class="warning-box">
<p><strong>⏳ Please wait:</strong> You will receive another email when processing is complete.</p>
</div>
HTML;
return self::wrapTemplate('🔄 CSV Processing Started', $content);
}
/**
* Global to Local - Processing Complete
*/
public static function globalToLocalComplete($data) {
$content = <<<HTML
<div class="success-box">
<p><strong>✅ CSV Transformation Complete</strong></p>
</div>
<div class="info-box">
<p><strong>Campaign Number:</strong> {$data['campaign_number']}</p>
<p><strong>Business Unit:</strong> {$data['business_unit']}</p>
<p><strong>Input Rows:</strong> {$data['input_rows']}</p>
<p><strong>Output Files Created:</strong> {$data['file_count']}</p>
<p><strong>Total Output Rows:</strong> {$data['total_rows']}</p>
</div>
<h3>Files Created (16 Regional CSVs):</h3>
<table class="data-table">
<tr>
<th>ISO Code</th>
<th>Country</th>
<th>Rows</th>
</tr>
HTML;
// Add sample rows (first 5 files)
$isoSample = ['en-GB', 'es-ES', 'pt-PT', 'en-IE', 'fr-CH'];
foreach ($isoSample as $iso) {
$parts = explode('-', $iso);
$country = $parts[1] ?? '';
$content .= "<tr><td>{$iso}</td><td>{$country}</td><td>{$data['input_rows']}</td></tr>";
}
$content .= <<<HTML
<tr><td colspan="3" style="text-align: center; font-style: italic; color: #999;">... and 11 more files</td></tr>
</table>
<div class="success-box">
<p><strong>✓ Complete:</strong> All {$data['file_count']} CSV files have been uploaded to Box.</p>
<p>Files should appear in OMG within 5 minutes.</p>
</div>
<div class="info-box">
<p><strong>Box Folder:</strong> <a href="{$data['folder_url']}">{$data['folder_url']}</a></p>
</div>
HTML;
return self::wrapTemplate('✅ Global to Local Complete', $content, '#28a745');
}
/**
* Global to Local - Processing Failed
*/
public static function globalToLocalFailed($data) {
$content = <<<HTML
<div class="error-box">
<p><strong>❌ CSV Processing Failed</strong></p>
</div>
<div class="info-box">
<p><strong>File:</strong> <code>{$data['filename']}</code></p>
{$data['campaign_number'] ? "<p><strong>Campaign Number:</strong> {$data['campaign_number']}</p>" : ''}
</div>
<h3>Error Details:</h3>
<div class="error-box">
<p style="color: #d32f2f;"><strong>Stage:</strong> {$data['stage']}</p>
<p style="color: #d32f2f;"><strong>Error:</strong> {$data['error']}</p>
</div>
<div class="warning-box">
<p><strong>📌 Action Required:</strong></p>
<ul>
<li>Review the error details above</li>
<li>Check your CSV file format and data</li>
<li>Verify campaign number is in the filename</li>
<li>Try uploading again after corrections</li>
</ul>
</div>
HTML;
return self::wrapTemplate('❌ CSV Processing Failed', $content, '#d32f2f');
}
/**
* Box Upload - Success
*/
public static function boxUploadSuccess($data) {
$content = <<<HTML
<div class="success-box">
<p><strong>✅ Files Uploaded to Box Successfully</strong></p>
</div>
<div class="info-box">
<p><strong>Files Uploaded:</strong> {$data['file_count']}</p>
<p><strong>Campaign Number:</strong> {$data['campaign_number']}</p>
<p><strong>Business Unit:</strong> {$data['business_unit']}</p>
</div>
<h3>Upload Summary:</h3>
<div class="success-box">
<p>All {$data['file_count']} regional CSV files have been successfully uploaded to the Box output folder.</p>
<p>These files are now available in the OMG system for further processing.</p>
</div>
<div class="info-box">
<p><strong>Box Folder:</strong> <a href="{$data['folder_url']}">View Files in Box</a></p>
</div>
HTML;
return self::wrapTemplate('✅ Box Upload Complete', $content, '#28a745');
}
/**
* Get template by name
*/
public static function getTemplate($templateName, $data) {
switch ($templateName) {
case 'asset_submission_success':
return self::assetSubmissionSuccess($data);
case 'asset_submission_failed':
return self::assetSubmissionFailed($data);
case 'global_to_local_started':
return self::globalToLocalStarted($data);
case 'global_to_local_complete':
return self::globalToLocalComplete($data);
case 'global_to_local_failed':
return self::globalToLocalFailed($data);
case 'box_upload_success':
return self::boxUploadSuccess($data);
default:
// Fallback simple template
return self::wrapTemplate('Notification', '<p>' . json_encode($data) . '</p>');
}
}
}