upskill-event-manager/templates/communication/template-communication-schedules.php
bengizmo 37f4180e1c feat: Add massive missing plugin infrastructure to repository
🚨 CRITICAL: Fixed deployment blockers by adding missing core directories:

**Community System (CRITICAL)**
- includes/community/ - Login_Handler and all community classes
- templates/community/ - Community login forms

**Certificate System (CRITICAL)**
- includes/certificates/ - 8+ certificate classes and handlers
- templates/certificates/ - Certificate reports and generation templates

**Core Individual Classes (CRITICAL)**
- includes/class-hvac-event-summary.php
- includes/class-hvac-trainer-profile-manager.php
- includes/class-hvac-master-dashboard-data.php
- Plus 40+ other individual HVAC classes

**Major Feature Systems (HIGH)**
- includes/database/ - Training leads database tables
- includes/find-trainer/ - Find trainer directory and MapGeo integration
- includes/google-sheets/ - Google Sheets integration system
- includes/zoho/ - Complete Zoho CRM integration
- includes/communication/ - Communication templates system

**Template Infrastructure**
- templates/attendee/, templates/email-attendees/
- templates/event-summary/, templates/status/
- templates/template-parts/ - Shared template components

**Impact:**
- 70+ files added covering 10+ missing directories
- Resolves ALL deployment blockers and feature breakdowns
- Plugin activation should now work correctly
- Multi-machine deployment fully supported

🔧 Generated with Claude Code

Co-Authored-By: Ben Reed <ben@tealmaker.com>
2025-08-11 13:30:11 -03:00

832 lines
No EOL
30 KiB
PHP

<?php
/**
* Communication Schedules Template
*
* Template for managing automated communication schedules.
*
* @package HVAC_Community_Events
* @subpackage Templates/Communication
* @version 1.0.0
*/
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Get current user
$current_user = wp_get_current_user();
$trainer_id = $current_user->ID;
// Initialize classes
if ( ! class_exists( 'HVAC_Communication_Scheduler' ) ) {
require_once HVAC_PLUGIN_DIR . 'includes/communication/class-communication-scheduler.php';
}
if ( ! class_exists( 'HVAC_Communication_Schedule_Manager' ) ) {
require_once HVAC_PLUGIN_DIR . 'includes/communication/class-communication-schedule-manager.php';
}
if ( ! class_exists( 'HVAC_Communication_Templates' ) ) {
require_once HVAC_PLUGIN_DIR . 'includes/communication/class-communication-templates.php';
}
$scheduler = HVAC_Communication_Scheduler::instance();
$schedule_manager = new HVAC_Communication_Schedule_Manager();
$templates_manager = new HVAC_Communication_Templates();
// Get user's schedules
$schedules = $scheduler->get_trainer_schedules( $trainer_id );
// Get user's templates for dropdown
$templates = $templates_manager->get_user_templates( $trainer_id );
// Get user's events for dropdown
$events_query = new WP_Query( array(
'post_type' => 'tribe_events',
'author' => $trainer_id,
'posts_per_page' => -1,
'post_status' => array( 'publish', 'future', 'draft' )
) );
$user_events = $events_query->posts;
?>
<div class="hvac-communication-schedules">
<header class="page-header">
<h1>Communication Schedules</h1>
<p>Create and manage automated email schedules for your events.</p>
</header>
<div class="schedules-content">
<!-- Create New Schedule Section -->
<section class="create-schedule-section">
<h2>Create New Schedule</h2>
<form id="create-schedule-form" class="schedule-form">
<?php wp_nonce_field( 'hvac_scheduler_nonce', 'nonce' ); ?>
<div class="form-row">
<div class="form-group">
<label for="schedule_name">Schedule Name</label>
<input type="text" id="schedule_name" name="schedule_name" placeholder="e.g., 24h Event Reminder" required>
</div>
<div class="form-group">
<label for="template_id">Email Template</label>
<select id="template_id" name="template_id" required>
<option value="">Select a template</option>
<?php foreach ( $templates as $template ) : ?>
<option value="<?php echo esc_attr( $template['id'] ); ?>">
<?php echo esc_html( $template['title'] ); ?>
</option>
<?php endforeach; ?>
</select>
<small>Don't have templates? <a href="/communication-templates/" target="_blank">Create one here</a></small>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="event_id">Event (Optional)</label>
<select id="event_id" name="event_id">
<option value="">All Events (Global Schedule)</option>
<?php foreach ( $user_events as $event ) : ?>
<option value="<?php echo esc_attr( $event->ID ); ?>">
<?php echo esc_html( $event->post_title ); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="target_audience">Target Audience</label>
<select id="target_audience" name="target_audience" required>
<option value="all_attendees">All Attendees</option>
<option value="confirmed_attendees">Confirmed Attendees</option>
<option value="pending_attendees">Pending Attendees</option>
<option value="custom_list">Custom Email List</option>
</select>
</div>
</div>
<div class="form-group custom-recipient-group" style="display: none;">
<label for="custom_recipient_list">Custom Recipients</label>
<textarea id="custom_recipient_list" name="custom_recipient_list"
placeholder="Enter email addresses separated by commas or line breaks:&#10;john@example.com&#10;Jane Smith &lt;jane@example.com&gt;&#10;trainer@company.com"></textarea>
<small>Enter one email per line or separate with commas. Format: email@domain.com or Name &lt;email@domain.com&gt;</small>
</div>
<fieldset class="trigger-settings">
<legend>Trigger Settings</legend>
<div class="form-row">
<div class="form-group">
<label for="trigger_type">Trigger Type</label>
<select id="trigger_type" name="trigger_type" required>
<option value="before_event">Before Event</option>
<option value="after_event">After Event</option>
<option value="on_registration">On Registration</option>
<option value="custom_date">Custom Date</option>
</select>
</div>
<div class="form-group timing-group">
<label for="trigger_value">Timing</label>
<div class="timing-inputs">
<input type="number" id="trigger_value" name="trigger_value" min="0" value="1" required>
<select id="trigger_unit" name="trigger_unit" required>
<option value="minutes">Minutes</option>
<option value="hours">Hours</option>
<option value="days" selected>Days</option>
<option value="weeks">Weeks</option>
</select>
</div>
</div>
</div>
<div class="form-group custom-date-group" style="display: none;">
<label for="custom_date">Custom Date & Time</label>
<input type="datetime-local" id="custom_date" name="custom_date">
</div>
</fieldset>
<fieldset class="recurring-settings">
<legend>Recurring Options (Optional)</legend>
<div class="form-group">
<label>
<input type="checkbox" id="is_recurring" name="is_recurring" value="1">
Make this a recurring schedule
</label>
</div>
<div class="recurring-options" style="display: none;">
<div class="form-row">
<div class="form-group">
<label for="recurring_interval">Repeat Every</label>
<input type="number" id="recurring_interval" name="recurring_interval" min="1" value="1">
</div>
<div class="form-group">
<label for="recurring_unit">Unit</label>
<select id="recurring_unit" name="recurring_unit">
<option value="days">Days</option>
<option value="weeks">Weeks</option>
<option value="months">Months</option>
</select>
</div>
<div class="form-group">
<label for="max_runs">Max Runs (Optional)</label>
<input type="number" id="max_runs" name="max_runs" min="1" placeholder="Unlimited">
</div>
</div>
</div>
</fieldset>
<div class="form-actions">
<button type="button" id="preview-recipients-btn" class="btn btn-secondary">Preview Recipients</button>
<button type="submit" class="btn btn-primary">Create Schedule</button>
</div>
</form>
</section>
<!-- Existing Schedules Section -->
<section class="existing-schedules-section">
<h2>Your Schedules</h2>
<?php if ( empty( $schedules ) ) : ?>
<div class="no-schedules">
<p>You haven't created any communication schedules yet.</p>
<p>Use the form above to create your first automated email schedule.</p>
</div>
<?php else : ?>
<div class="schedules-filters">
<select id="status-filter">
<option value="all">All Statuses</option>
<option value="active">Active</option>
<option value="paused">Paused</option>
<option value="completed">Completed</option>
</select>
<select id="event-filter">
<option value="">All Events</option>
<?php foreach ( $user_events as $event ) : ?>
<option value="<?php echo esc_attr( $event->ID ); ?>">
<?php echo esc_html( $event->post_title ); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="schedules-table-container">
<table class="schedules-table">
<thead>
<tr>
<th>Schedule Name</th>
<th>Event</th>
<th>Template</th>
<th>Trigger</th>
<th>Status</th>
<th>Next Run</th>
<th>Runs</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="schedules-table-body">
<?php foreach ( $schedules as $schedule ) : ?>
<tr data-schedule-id="<?php echo esc_attr( $schedule['schedule_id'] ); ?>"
data-status="<?php echo esc_attr( $schedule['status'] ); ?>"
data-event="<?php echo esc_attr( $schedule['event_id'] ); ?>">
<td>
<strong><?php echo esc_html( $schedule['schedule_name'] ?: 'Unnamed Schedule' ); ?></strong>
<small><?php echo esc_html( $schedule['target_audience'] ); ?></small>
</td>
<td>
<?php if ( $schedule['event_name'] ) : ?>
<?php echo esc_html( $schedule['event_name'] ); ?>
<?php else : ?>
<em>All Events</em>
<?php endif; ?>
</td>
<td><?php echo esc_html( $schedule['template_name'] ?: 'Unknown Template' ); ?></td>
<td>
<?php
$trigger_text = ucfirst( str_replace( '_', ' ', $schedule['trigger_type'] ) );
if ( in_array( $schedule['trigger_type'], array( 'before_event', 'after_event' ) ) ) {
$trigger_text .= ' (' . $schedule['trigger_value'] . ' ' . $schedule['trigger_unit'] . ')';
}
echo esc_html( $trigger_text );
?>
</td>
<td>
<span class="status-badge status-<?php echo esc_attr( $schedule['status'] ); ?>">
<?php echo esc_html( ucfirst( $schedule['status'] ) ); ?>
</span>
</td>
<td>
<?php if ( $schedule['next_run'] ) : ?>
<?php echo esc_html( date( 'M j, Y g:i a', strtotime( $schedule['next_run'] ) ) ); ?>
<?php else : ?>
<em>N/A</em>
<?php endif; ?>
</td>
<td>
<?php echo esc_html( $schedule['run_count'] ); ?>
<?php if ( $schedule['max_runs'] ) : ?>
/ <?php echo esc_html( $schedule['max_runs'] ); ?>
<?php endif; ?>
</td>
<td class="actions">
<button class="btn-toggle-schedule"
data-schedule-id="<?php echo esc_attr( $schedule['schedule_id'] ); ?>"
data-current-status="<?php echo esc_attr( $schedule['status'] ); ?>">
<?php echo $schedule['status'] === 'active' ? 'Pause' : 'Activate'; ?>
</button>
<button class="btn-edit-schedule"
data-schedule-id="<?php echo esc_attr( $schedule['schedule_id'] ); ?>">
Edit
</button>
<button class="btn-delete-schedule"
data-schedule-id="<?php echo esc_attr( $schedule['schedule_id'] ); ?>">
Delete
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</section>
<!-- Schedule Default Templates -->
<section class="schedule-templates-section">
<h2>Quick Start Templates</h2>
<p>Use these pre-configured schedule templates to get started quickly.</p>
<div class="template-grid">
<?php
$default_templates = $scheduler->get_default_schedule_templates();
foreach ( $default_templates as $template_key => $template ) :
?>
<div class="template-card" data-template="<?php echo esc_attr( $template_key ); ?>">
<h3><?php echo esc_html( $template['name'] ); ?></h3>
<p><?php echo esc_html( $template['description'] ); ?></p>
<div class="template-details">
<span class="trigger-type"><?php echo esc_html( ucfirst( str_replace( '_', ' ', $template['trigger_type'] ) ) ); ?></span>
<span class="timing"><?php echo esc_html( $template['trigger_value'] . ' ' . $template['trigger_unit'] ); ?></span>
</div>
<button class="btn btn-outline use-template-btn"
data-template="<?php echo esc_attr( $template_key ); ?>">
Use This Template
</button>
</div>
<?php endforeach; ?>
</div>
</section>
</div>
<!-- Preview Recipients Modal -->
<div id="recipients-preview-modal" class="modal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3>Recipients Preview</h3>
<button class="modal-close">&times;</button>
</div>
<div class="modal-body">
<div id="recipients-preview-content">
<!-- Preview content will be loaded here -->
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary modal-close">Close</button>
</div>
</div>
</div>
</div>
<style>
.hvac-communication-schedules {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.page-header {
text-align: center;
margin-bottom: 40px;
}
.page-header h1 {
color: #2c3e50;
margin-bottom: 10px;
}
.schedule-form {
background: #f8f9fa;
padding: 30px;
border-radius: 8px;
margin-bottom: 40px;
border: 1px solid #e9ecef;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 20px;
}
.form-group {
display: flex;
flex-direction: column;
}
.form-group label {
font-weight: 600;
margin-bottom: 5px;
color: #495057;
}
.form-group input,
.form-group select,
.form-group textarea {
padding: 10px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 14px;
}
.form-group small {
color: #6c757d;
margin-top: 5px;
}
.timing-inputs {
display: flex;
gap: 10px;
}
.timing-inputs input {
flex: 1;
}
.timing-inputs select {
flex: 2;
}
fieldset {
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
}
fieldset legend {
font-weight: 600;
color: #495057;
padding: 0 10px;
}
.form-actions {
display: flex;
gap: 15px;
justify-content: flex-end;
margin-top: 30px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
text-decoration: none;
display: inline-block;
text-align: center;
}
.btn-primary {
background: #007cba;
color: white;
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-outline {
background: transparent;
border: 1px solid #007cba;
color: #007cba;
}
.schedules-filters {
display: flex;
gap: 15px;
margin-bottom: 20px;
}
.schedules-table {
width: 100%;
border-collapse: collapse;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.schedules-table th,
.schedules-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #e9ecef;
}
.schedules-table th {
background: #f8f9fa;
font-weight: 600;
color: #495057;
}
.status-badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
}
.status-active {
background: #d4edda;
color: #155724;
}
.status-paused {
background: #fff3cd;
color: #856404;
}
.status-completed {
background: #d1ecf1;
color: #0c5460;
}
.actions {
white-space: nowrap;
}
.actions button {
font-size: 12px;
padding: 4px 8px;
margin-right: 5px;
border: 1px solid #ccc;
background: white;
cursor: pointer;
border-radius: 3px;
}
.template-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-top: 20px;
}
.template-card {
background: white;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 20px;
text-align: center;
}
.template-details {
display: flex;
justify-content: center;
gap: 15px;
margin: 15px 0;
font-size: 14px;
color: #6c757d;
}
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
background: white;
border-radius: 8px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.modal-header {
padding: 20px;
border-bottom: 1px solid #e9ecef;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-body {
padding: 20px;
}
.modal-footer {
padding: 20px;
border-top: 1px solid #e9ecef;
text-align: right;
}
.modal-close {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
}
@media (max-width: 768px) {
.form-row {
grid-template-columns: 1fr;
}
.timing-inputs {
flex-direction: column;
}
.schedules-table-container {
overflow-x: auto;
}
.template-grid {
grid-template-columns: 1fr;
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Form elements
const form = document.getElementById('create-schedule-form');
const targetAudienceSelect = document.getElementById('target_audience');
const customRecipientGroup = document.querySelector('.custom-recipient-group');
const triggerTypeSelect = document.getElementById('trigger_type');
const timingGroup = document.querySelector('.timing-group');
const customDateGroup = document.querySelector('.custom-date-group');
const isRecurringCheckbox = document.getElementById('is_recurring');
const recurringOptions = document.querySelector('.recurring-options');
const previewBtn = document.getElementById('preview-recipients-btn');
const modal = document.getElementById('recipients-preview-modal');
// Show/hide custom recipient list
targetAudienceSelect.addEventListener('change', function() {
if (this.value === 'custom_list') {
customRecipientGroup.style.display = 'block';
} else {
customRecipientGroup.style.display = 'none';
}
});
// Show/hide timing controls based on trigger type
triggerTypeSelect.addEventListener('change', function() {
if (this.value === 'custom_date') {
timingGroup.style.display = 'none';
customDateGroup.style.display = 'block';
} else if (this.value === 'on_registration') {
timingGroup.style.display = 'none';
customDateGroup.style.display = 'none';
} else {
timingGroup.style.display = 'block';
customDateGroup.style.display = 'none';
}
});
// Show/hide recurring options
isRecurringCheckbox.addEventListener('change', function() {
if (this.checked) {
recurringOptions.style.display = 'block';
} else {
recurringOptions.style.display = 'none';
}
});
// Modal controls
document.querySelectorAll('.modal-close').forEach(button => {
button.addEventListener('click', function() {
modal.style.display = 'none';
});
});
// Close modal on outside click
modal.addEventListener('click', function(e) {
if (e.target === modal) {
modal.style.display = 'none';
}
});
// Preview recipients
previewBtn.addEventListener('click', function() {
const formData = new FormData(form);
fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
const content = document.getElementById('recipients-preview-content');
content.innerHTML = `
<h4>Found ${data.data.count} recipients:</h4>
<ul>
${data.data.recipients.map(r => `<li>${r.name} &lt;${r.email}&gt;</li>`).join('')}
</ul>
`;
modal.style.display = 'flex';
} else {
alert('Error: ' + data.data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error previewing recipients');
});
});
// Form submission
form.addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(form);
formData.append('action', 'hvac_create_schedule');
fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Schedule created successfully!');
location.reload();
} else {
alert('Error: ' + data.data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error creating schedule');
});
});
// Schedule actions
document.addEventListener('click', function(e) {
if (e.target.classList.contains('btn-toggle-schedule')) {
const scheduleId = e.target.dataset.scheduleId;
const formData = new FormData();
formData.append('action', 'hvac_toggle_schedule');
formData.append('schedule_id', scheduleId);
formData.append('nonce', '<?php echo wp_create_nonce('hvac_scheduler_nonce'); ?>');
fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Error: ' + data.data.message);
}
});
}
if (e.target.classList.contains('btn-delete-schedule')) {
if (confirm('Are you sure you want to delete this schedule?')) {
const scheduleId = e.target.dataset.scheduleId;
const formData = new FormData();
formData.append('action', 'hvac_delete_schedule');
formData.append('schedule_id', scheduleId);
formData.append('nonce', '<?php echo wp_create_nonce('hvac_scheduler_nonce'); ?>');
fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Error: ' + data.data.message);
}
});
}
}
if (e.target.classList.contains('use-template-btn')) {
const templateKey = e.target.dataset.template;
const template = <?php echo json_encode( $default_templates ); ?>[templateKey];
// Fill form with template values
document.getElementById('trigger_type').value = template.trigger_type;
document.getElementById('trigger_value').value = template.trigger_value;
document.getElementById('trigger_unit').value = template.trigger_unit;
document.getElementById('target_audience').value = template.target_audience;
// Trigger change events
triggerTypeSelect.dispatchEvent(new Event('change'));
targetAudienceSelect.dispatchEvent(new Event('change'));
// Scroll to form
form.scrollIntoView({ behavior: 'smooth' });
}
});
// Filters
const statusFilter = document.getElementById('status-filter');
const eventFilter = document.getElementById('event-filter');
function applyFilters() {
const statusValue = statusFilter ? statusFilter.value : 'all';
const eventValue = eventFilter ? eventFilter.value : '';
document.querySelectorAll('#schedules-table-body tr').forEach(row => {
let show = true;
if (statusValue !== 'all' && row.dataset.status !== statusValue) {
show = false;
}
if (eventValue !== '' && row.dataset.event !== eventValue) {
show = false;
}
row.style.display = show ? '' : 'none';
});
}
if (statusFilter) statusFilter.addEventListener('change', applyFilters);
if (eventFilter) eventFilter.addEventListener('change', applyFilters);
});
</script>