diff --git a/admin/email-log.php b/admin/email-log.php
new file mode 100644
index 0000000..3968b86
--- /dev/null
+++ b/admin/email-log.php
@@ -0,0 +1,234 @@
+checkDeliveryStatus($messageId);
+ if (!empty($data)) {
+ $statusMap = ['queued'=>'sent','sent'=>'sent','delivered'=>'delivered','bounced'=>'bounced','failed'=>'failed'];
+ db()->update('email_log', [
+ 'status' => $statusMap[$data['status']] ?? 'unknown',
+ 'opened' => $data['opened'] ?? 0,
+ 'opened_at' => !empty($data['opened_at']) ? date('Y-m-d H:i:s', strtotime($data['opened_at'])) : null,
+ 'open_count' => $data['open_count'] ?? 0,
+ 'clicked' => $data['clicked'] ?? 0,
+ 'click_count' => $data['click_count'] ?? 0,
+ 'status_checked_at'=> date('Y-m-d H:i:s'),
+ ], 'id = :id', ['id' => $logId]);
+ }
+ }
+ header('Location: /admin/email-log.php' . (!empty($_POST['customer_filter']) ? '?customer=' . urlencode($_POST['customer_filter']) : ''));
+ exit;
+}
+
+// Filters
+$customerFilter = trim($_GET['customer'] ?? '');
+$statusFilter = trim($_GET['status'] ?? '');
+$search = trim($_GET['search'] ?? '');
+$page = max(1, (int)($_GET['page'] ?? 1));
+$perPage = 25;
+$offset = ($page - 1) * $perPage;
+
+$where = [];
+$params = [];
+
+if ($customerFilter) {
+ $where[] = 'l.customer_id = :customer_id';
+ $params['customer_id'] = $customerFilter;
+}
+if ($statusFilter) {
+ $where[] = 'l.status = :status';
+ $params['status'] = $statusFilter;
+}
+if ($search) {
+ $where[] = '(l.recipient_email LIKE :search OR l.subject LIKE :search)';
+ $params['search'] = '%' . $search . '%';
+}
+
+$whereClause = $where ? 'WHERE ' . implode(' AND ', $where) : '';
+
+$total = db()->fetch(
+ "SELECT COUNT(*) as cnt FROM email_log l $whereClause",
+ $params
+)['cnt'] ?? 0;
+
+$logs = db()->fetchAll(
+ "SELECT l.*, c.name as customer_name
+ FROM email_log l
+ LEFT JOIN customers c ON l.customer_id = c.customer_id
+ $whereClause
+ ORDER BY l.sent_at DESC
+ LIMIT $perPage OFFSET $offset",
+ $params
+);
+
+$totalPages = max(1, ceil($total / $perPage));
+
+// Status badge helper
+$statusBadge = [
+ 'sent' => ['bg'=>'#3B82F6','label'=>'Sent'],
+ 'delivered' => ['bg'=>'#10B981','label'=>'Delivered'],
+ 'bounced' => ['bg'=>'#EF4444','label'=>'Bounced'],
+ 'failed' => ['bg'=>'#EF4444','label'=>'Failed'],
+ 'unknown' => ['bg'=>'#9CA3AF','label'=>'Unknown'],
+];
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+ | Recipient |
+ Subject |
+ Preview |
+ Status |
+ Opened |
+ Clicked |
+ Sent At |
+ Actions |
+
+
+
+
+ | No emails found. |
+
+
+ |
+ = htmlspecialchars($log['recipient_email']) ?>
+
+
+
+ = htmlspecialchars($log['customer_name']) ?>
+
+
+
+ |
+
+ = htmlspecialchars($log['subject']) ?>
+
+ = htmlspecialchars($log['tags']) ?>
+
+ |
+
+ = htmlspecialchars($log['preview'] ?? '') ?>
+ |
+
+
+ = $badge['label'] ?>
+
+
+ = htmlspecialchars(substr($log['error_message'],0,60)) ?>
+
+ |
+
+
+
+ ✓ = $log['open_count'] > 1 ? '('.$log['open_count'].')' : '' ?>
+
+
+ —
+
+ |
+
+
+ ✓ = $log['click_count'] > 1 ? '('.$log['click_count'].')' : '' ?>
+
+ —
+
+ |
+
+ = date('M j, Y', strtotime($log['sent_at'])) ?>
+ = date('g:i a', strtotime($log['sent_at'])) ?>
+
+ checked = date('M j g:ia', strtotime($log['status_checked_at'])) ?>
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+
+
+ 1): ?>
+
+
+
+
+
diff --git a/admin/integrations.php b/admin/integrations.php
index bf3664c..1d7ade8 100644
--- a/admin/integrations.php
+++ b/admin/integrations.php
@@ -3,33 +3,31 @@
* Tom's Java Jive - Admin Integrations Settings
*/
-// Auth only — no HTML yet
require_once __DIR__ . '/../includes/auth.php';
AdminAuth::require();
-// Handle POST before any output
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$section = $_POST['section'] ?? '';
- if ($section === 'cybermail') {
- setSetting('cybermail_api_key', trim($_POST['cybermail_api_key'] ?? ''));
- setSetting('cybermail_from_email', trim($_POST['cybermail_from_email'] ?? ''));
- setSetting('cybermail_from_name', trim($_POST['cybermail_from_name'] ?? ''));
- setSetting('email_notifications_enabled', isset($_POST['email_notifications_enabled']) ? '1' : '0');
- setFlash('success', 'CyberMail settings saved.');
+ if ($section === 'email') {
+ setSetting('cybermail_api_key', trim($_POST['cybermail_api_key'] ?? ''));
+ setSetting('cybermail_from_email', trim($_POST['cybermail_from_email'] ?? ''));
+ setSetting('cybermail_from_name', trim($_POST['cybermail_from_name'] ?? ''));
+ setSetting('email_notifications_enabled', isset($_POST['email_notifications_enabled']) ? '1' : '0');
+ setFlash('success', 'Email settings saved.');
}
if ($section === 'twilio') {
- setSetting('twilio_account_sid', trim($_POST['twilio_account_sid'] ?? ''));
- setSetting('twilio_auth_token', trim($_POST['twilio_auth_token'] ?? ''));
- setSetting('twilio_phone_number', trim($_POST['twilio_phone_number'] ?? ''));
+ setSetting('twilio_account_sid', trim($_POST['twilio_account_sid'] ?? ''));
+ setSetting('twilio_auth_token', trim($_POST['twilio_auth_token'] ?? ''));
+ setSetting('twilio_phone_number', trim($_POST['twilio_phone_number'] ?? ''));
setSetting('sms_notifications_enabled', isset($_POST['sms_notifications_enabled']) ? '1' : '0');
setFlash('success', 'Twilio settings saved.');
}
if ($section === 'push') {
- setSetting('vapid_public_key', trim($_POST['vapid_public_key'] ?? ''));
- setSetting('vapid_private_key', trim($_POST['vapid_private_key'] ?? ''));
+ setSetting('vapid_public_key', trim($_POST['vapid_public_key'] ?? ''));
+ setSetting('vapid_private_key', trim($_POST['vapid_private_key'] ?? ''));
setSetting('push_notifications_enabled', isset($_POST['push_notifications_enabled']) ? '1' : '0');
setFlash('success', 'Push notification settings saved.');
}
@@ -43,23 +41,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
exit;
}
-// --- GET: render page ---
ob_start();
$pageTitle = 'Integrations';
$currentPage = 'integrations';
require_once __DIR__ . '/includes/header.php';
-// Load current settings
-$cm = [
- 'api_key' => getSetting('cybermail_api_key', ''),
- 'from_email' => getSetting('cybermail_from_email', 'noreply@tomsjavajive.com'),
- 'from_name' => getSetting('cybermail_from_name', "Tom's Java Jive"),
- 'enabled' => getSetting('email_notifications_enabled', '0'),
+$email = [
+ 'api_key' => getSetting('cybermail_api_key', ''),
+ 'from' => getSetting('cybermail_from_email', 'noreply@tomsjavajive.com'),
+ 'from_name' => getSetting('cybermail_from_name', "Tom's Java Jive"),
+ 'enabled' => getSetting('email_notifications_enabled', '0'),
];
$tw = [
- 'sid' => getSetting('twilio_account_sid', ''),
- 'token' => getSetting('twilio_auth_token', ''),
- 'phone' => getSetting('twilio_phone_number', ''),
+ 'sid' => getSetting('twilio_account_sid', ''),
+ 'token' => getSetting('twilio_auth_token', ''),
+ 'phone' => getSetting('twilio_phone_number', ''),
'enabled' => getSetting('sms_notifications_enabled', '0'),
];
$push = [
@@ -75,10 +71,10 @@ $loyaltyEnabled = getSetting('loyalty_enabled', '1') === '1';
.integration-header { display: flex; justify-content: space-between; align-items: center; padding: 1.25rem 1.5rem; border-bottom: 1px solid var(--admin-border); }
.integration-title { display: flex; align-items: center; gap: 1rem; }
.integration-icon { width: 48px; height: 48px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; }
-.integration-icon.cybermail { background: #0ea5e9; color: white; }
-.integration-icon.twilio { background: #F22F46; color: white; }
-.integration-icon.push { background: #8B5CF6; color: white; }
-.integration-icon.loyalty { background: #F59E0B; color: white; }
+.integration-icon.email { background: #0ea5e9; color: white; }
+.integration-icon.twilio { background: #F22F46; color: white; }
+.integration-icon.push { background: #8B5CF6; color: white; }
+.integration-icon.loyalty { background: #F59E0B; color: white; }
.integration-body { padding: 1.5rem; }
.status-badge { padding: .375rem .75rem; border-radius: 20px; font-size: .75rem; font-weight: 600; }
.status-badge.configured { background: rgba(16,185,129,.1); color: var(--admin-success); }
@@ -99,49 +95,51 @@ $loyaltyEnabled = getSetting('loyalty_enabled', '1') === '1';
= getFlash('success') ?>
-
+
@@ -296,7 +294,7 @@ $loyaltyEnabled = getSetting('loyalty_enabled', '1') === '1';