2025-07-17 08:11:23 +02:00
|
|
|
|
<?php
|
|
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) { exit; }
|
2025-07-21 13:57:16 +02:00
|
|
|
|
global $wpdb;
|
|
|
|
|
|
|
|
|
|
|
|
// Get page and status filter
|
|
|
|
|
|
$current_page = max(1, intval($_GET['paged'] ?? 1));
|
|
|
|
|
|
$status_filter = sanitize_text_field($_GET['status_filter'] ?? '');
|
|
|
|
|
|
|
|
|
|
|
|
// Load Feed Manager for operations
|
|
|
|
|
|
require_once MIRAVIA_CLASSES_PATH . 'class.feed-manager.php';
|
|
|
|
|
|
$feedManager = new MiraviaFeedManager();
|
2025-07-17 08:11:23 +02:00
|
|
|
|
|
2025-07-21 13:57:16 +02:00
|
|
|
|
// Get jobs data
|
|
|
|
|
|
$limit = 20;
|
|
|
|
|
|
$offset = ($current_page - 1) * $limit;
|
|
|
|
|
|
$jobs = $feedManager->getJobs($limit, $offset, $status_filter ?: null);
|
|
|
|
|
|
$total_jobs = $feedManager->getJobCount($status_filter ?: null);
|
|
|
|
|
|
$total_pages = ceil($total_jobs / $limit);
|
|
|
|
|
|
|
|
|
|
|
|
// Get status counts for filter tabs
|
|
|
|
|
|
$status_counts = [
|
|
|
|
|
|
'all' => $feedManager->getJobCount(),
|
|
|
|
|
|
'PENDING' => $feedManager->getJobCount('PENDING'),
|
|
|
|
|
|
'CREATING_DOCUMENT' => $feedManager->getJobCount('CREATING_DOCUMENT'),
|
|
|
|
|
|
'SUBMITTED' => $feedManager->getJobCount('SUBMITTED'),
|
|
|
|
|
|
'PROCESSING' => $feedManager->getJobCount('PROCESSING'),
|
|
|
|
|
|
'COMPLETED' => $feedManager->getJobCount('COMPLETED'),
|
|
|
|
|
|
'FAILED' => $feedManager->getJobCount('FAILED')
|
|
|
|
|
|
];
|
2025-07-17 08:11:23 +02:00
|
|
|
|
|
|
|
|
|
|
?>
|
|
|
|
|
|
<div class="wrap">
|
2025-07-21 13:57:16 +02:00
|
|
|
|
<h2>Feed Jobs Queue</h2>
|
|
|
|
|
|
<p class="description">Monitor and manage Feed API job submissions. Jobs are processed asynchronously by Miravia's Feed API.</p>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Status Filter Tabs -->
|
|
|
|
|
|
<div class="nav-tab-wrapper">
|
|
|
|
|
|
<a href="?page=miravia_settings&subpage=jobs" class="nav-tab <?php echo empty($status_filter) ? 'nav-tab-active' : ''; ?>">
|
|
|
|
|
|
All (<?php echo $status_counts['all']; ?>)
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<a href="?page=miravia_settings&subpage=jobs&status_filter=PENDING" class="nav-tab <?php echo $status_filter === 'PENDING' ? 'nav-tab-active' : ''; ?>">
|
|
|
|
|
|
Pending (<?php echo $status_counts['PENDING']; ?>)
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<a href="?page=miravia_settings&subpage=jobs&status_filter=SUBMITTED" class="nav-tab <?php echo $status_filter === 'SUBMITTED' ? 'nav-tab-active' : ''; ?>">
|
|
|
|
|
|
Submitted (<?php echo $status_counts['SUBMITTED']; ?>)
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<a href="?page=miravia_settings&subpage=jobs&status_filter=PROCESSING" class="nav-tab <?php echo $status_filter === 'PROCESSING' ? 'nav-tab-active' : ''; ?>">
|
|
|
|
|
|
Processing (<?php echo $status_counts['PROCESSING']; ?>)
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<a href="?page=miravia_settings&subpage=jobs&status_filter=COMPLETED" class="nav-tab <?php echo $status_filter === 'COMPLETED' ? 'nav-tab-active' : ''; ?>">
|
|
|
|
|
|
Completed (<?php echo $status_counts['COMPLETED']; ?>)
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<a href="?page=miravia_settings&subpage=jobs&status_filter=FAILED" class="nav-tab <?php echo $status_filter === 'FAILED' ? 'nav-tab-active' : ''; ?>">
|
|
|
|
|
|
Failed (<?php echo $status_counts['FAILED']; ?>)
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Jobs Table -->
|
|
|
|
|
|
<table class="wp-list-table widefat fixed striped">
|
|
|
|
|
|
<thead>
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<th>Job ID</th>
|
|
|
|
|
|
<th>Feed ID</th>
|
|
|
|
|
|
<th>Type</th>
|
|
|
|
|
|
<th>Status</th>
|
|
|
|
|
|
<th>Products</th>
|
|
|
|
|
|
<th>Created</th>
|
|
|
|
|
|
<th>Processing Time</th>
|
|
|
|
|
|
<th>Actions</th>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</thead>
|
|
|
|
|
|
<tbody>
|
|
|
|
|
|
<?php if (empty($jobs)): ?>
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td colspan="8" style="text-align: center; padding: 40px;">
|
|
|
|
|
|
<p><strong>No feed jobs found.</strong></p>
|
|
|
|
|
|
<p>Submit products using the Feed API from the Products page to see jobs here.</p>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
<?php else: ?>
|
|
|
|
|
|
<?php foreach ($jobs as $job): ?>
|
|
|
|
|
|
<tr data-job-id="<?php echo $job->id; ?>">
|
|
|
|
|
|
<td><strong>#<?php echo $job->id; ?></strong></td>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
<?php if ($job->feed_id): ?>
|
|
|
|
|
|
<code><?php echo esc_html(substr($job->feed_id, 0, 12)) . '...'; ?></code>
|
|
|
|
|
|
<?php else: ?>
|
|
|
|
|
|
<span class="description">—</span>
|
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td><?php echo esc_html($job->feed_type); ?></td>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
<?php
|
|
|
|
|
|
$status_class = '';
|
|
|
|
|
|
switch ($job->status) {
|
|
|
|
|
|
case 'COMPLETED':
|
|
|
|
|
|
$status_class = 'status-completed';
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'FAILED':
|
|
|
|
|
|
$status_class = 'status-failed';
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'PROCESSING':
|
|
|
|
|
|
case 'SUBMITTED':
|
|
|
|
|
|
$status_class = 'status-processing';
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
$status_class = 'status-pending';
|
|
|
|
|
|
}
|
|
|
|
|
|
?>
|
|
|
|
|
|
<span class="job-status <?php echo $status_class; ?>" id="status-<?php echo $job->id; ?>">
|
|
|
|
|
|
<?php echo esc_html($job->status); ?>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td><?php echo intval($job->product_count); ?></td>
|
|
|
|
|
|
<td><?php echo date('M j, Y H:i', strtotime($job->created)); ?></td>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
<?php if ($job->processing_start_time && $job->processing_end_time): ?>
|
|
|
|
|
|
<?php
|
|
|
|
|
|
$start = new DateTime($job->processing_start_time);
|
|
|
|
|
|
$end = new DateTime($job->processing_end_time);
|
|
|
|
|
|
$duration = $start->diff($end);
|
|
|
|
|
|
echo $duration->format('%H:%I:%S');
|
|
|
|
|
|
?>
|
|
|
|
|
|
<?php elseif ($job->processing_start_time): ?>
|
|
|
|
|
|
<span class="description">In progress...</span>
|
|
|
|
|
|
<?php else: ?>
|
|
|
|
|
|
<span class="description">—</span>
|
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
<?php if ($job->feed_id && in_array($job->status, ['SUBMITTED', 'PROCESSING'])): ?>
|
|
|
|
|
|
<button type="button" class="button update-status-btn" data-job-id="<?php echo $job->id; ?>">
|
|
|
|
|
|
Update Status
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
|
|
|
|
<?php if ($job->status === 'FAILED'): ?>
|
|
|
|
|
|
<button type="button" class="button resubmit-job-btn" data-job-id="<?php echo $job->id; ?>">
|
|
|
|
|
|
Resubmit
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
|
|
|
|
<?php if ($job->error_message): ?>
|
|
|
|
|
|
<button type="button" class="button view-error-btn" data-error="<?php echo esc_attr($job->error_message); ?>">
|
|
|
|
|
|
View Error
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
|
|
|
|
<?php if ($job->result_data): ?>
|
|
|
|
|
|
<button type="button" class="button view-results-btn" data-results="<?php echo esc_attr($job->result_data); ?>">
|
|
|
|
|
|
View Results
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Pagination -->
|
|
|
|
|
|
<?php if ($total_pages > 1): ?>
|
|
|
|
|
|
<div class="tablenav bottom">
|
|
|
|
|
|
<div class="tablenav-pages">
|
|
|
|
|
|
<span class="displaying-num"><?php echo $total_jobs; ?> items</span>
|
|
|
|
|
|
<span class="pagination-links">
|
|
|
|
|
|
<?php if ($current_page > 1): ?>
|
|
|
|
|
|
<a class="first-page button" href="?page=miravia_settings&subpage=jobs<?php echo $status_filter ? '&status_filter=' . $status_filter : ''; ?>&paged=1">
|
|
|
|
|
|
‹‹
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<a class="prev-page button" href="?page=miravia_settings&subpage=jobs<?php echo $status_filter ? '&status_filter=' . $status_filter : ''; ?>&paged=<?php echo $current_page - 1; ?>">
|
|
|
|
|
|
‹
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
|
|
|
|
<span class="paging-input">
|
|
|
|
|
|
<span class="tablenav-paging-text">
|
|
|
|
|
|
<?php echo $current_page; ?> of <span class="total-pages"><?php echo $total_pages; ?></span>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
|
|
|
|
<?php if ($current_page < $total_pages): ?>
|
|
|
|
|
|
<a class="next-page button" href="?page=miravia_settings&subpage=jobs<?php echo $status_filter ? '&status_filter=' . $status_filter : ''; ?>&paged=<?php echo $current_page + 1; ?>">
|
|
|
|
|
|
›
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<a class="last-page button" href="?page=miravia_settings&subpage=jobs<?php echo $status_filter ? '&status_filter=' . $status_filter : ''; ?>&paged=<?php echo $total_pages; ?>">
|
|
|
|
|
|
››
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Error/Results Modal -->
|
|
|
|
|
|
<div id="job-modal" style="display: none;">
|
|
|
|
|
|
<div id="job-modal-content">
|
|
|
|
|
|
<span id="job-modal-close">×</span>
|
|
|
|
|
|
<h3 id="job-modal-title">Details</h3>
|
|
|
|
|
|
<div id="job-modal-body"></div>
|
|
|
|
|
|
</div>
|
2025-07-17 08:11:23 +02:00
|
|
|
|
</div>
|
2025-07-21 13:57:16 +02:00
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
|
.job-status {
|
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-completed {
|
|
|
|
|
|
background: #d4edda;
|
|
|
|
|
|
color: #155724;
|
|
|
|
|
|
border: 1px solid #c3e6cb;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-failed {
|
|
|
|
|
|
background: #f8d7da;
|
|
|
|
|
|
color: #721c24;
|
|
|
|
|
|
border: 1px solid #f5c6cb;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-processing {
|
|
|
|
|
|
background: #d1ecf1;
|
|
|
|
|
|
color: #0c5460;
|
|
|
|
|
|
border: 1px solid #bee5eb;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-pending {
|
|
|
|
|
|
background: #fff3cd;
|
|
|
|
|
|
color: #856404;
|
|
|
|
|
|
border: 1px solid #ffeaa7;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Modal styles */
|
|
|
|
|
|
#job-modal {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
z-index: 1000;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
background-color: rgba(0,0,0,0.5);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#job-modal-content {
|
|
|
|
|
|
background-color: #fefefe;
|
|
|
|
|
|
margin: 15% auto;
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
border: 1px solid #888;
|
|
|
|
|
|
width: 80%;
|
|
|
|
|
|
max-width: 600px;
|
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#job-modal-close {
|
|
|
|
|
|
color: #aaa;
|
|
|
|
|
|
float: right;
|
|
|
|
|
|
font-size: 28px;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#job-modal-close:hover {
|
|
|
|
|
|
color: black;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#job-modal-body {
|
|
|
|
|
|
max-height: 400px;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
background: #f9f9f9;
|
|
|
|
|
|
padding: 15px;
|
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
|
font-family: monospace;
|
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
jQuery(document).ready(function($) {
|
|
|
|
|
|
// Update job status
|
|
|
|
|
|
$('.update-status-btn').click(function() {
|
|
|
|
|
|
var button = $(this);
|
|
|
|
|
|
var jobId = button.data('job-id');
|
|
|
|
|
|
var statusSpan = $('#status-' + jobId);
|
|
|
|
|
|
|
|
|
|
|
|
button.prop('disabled', true).text('Updating...');
|
|
|
|
|
|
|
|
|
|
|
|
$.ajax({
|
|
|
|
|
|
url: ajaxurl,
|
|
|
|
|
|
type: 'POST',
|
|
|
|
|
|
data: {
|
|
|
|
|
|
action: 'miravia_update_job_status',
|
|
|
|
|
|
job_id: jobId
|
|
|
|
|
|
},
|
|
|
|
|
|
success: function(response) {
|
|
|
|
|
|
if(response.success) {
|
|
|
|
|
|
// Reload page to show updated status
|
|
|
|
|
|
location.reload();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
alert('Failed to update status: ' + response.data);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
error: function() {
|
|
|
|
|
|
alert('Failed to update job status');
|
|
|
|
|
|
},
|
|
|
|
|
|
complete: function() {
|
|
|
|
|
|
button.prop('disabled', false).text('Update Status');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Resubmit job
|
|
|
|
|
|
$('.resubmit-job-btn').click(function() {
|
|
|
|
|
|
if(!confirm('Are you sure you want to resubmit this job?')) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var button = $(this);
|
|
|
|
|
|
var jobId = button.data('job-id');
|
|
|
|
|
|
|
|
|
|
|
|
button.prop('disabled', true).text('Resubmitting...');
|
|
|
|
|
|
|
|
|
|
|
|
$.ajax({
|
|
|
|
|
|
url: ajaxurl,
|
|
|
|
|
|
type: 'POST',
|
|
|
|
|
|
data: {
|
|
|
|
|
|
action: 'miravia_resubmit_job',
|
|
|
|
|
|
job_id: jobId
|
|
|
|
|
|
},
|
|
|
|
|
|
success: function(response) {
|
|
|
|
|
|
if(response.success) {
|
|
|
|
|
|
alert('Job resubmitted successfully');
|
|
|
|
|
|
location.reload();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
alert('Failed to resubmit job: ' + response.data);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
error: function() {
|
|
|
|
|
|
alert('Failed to resubmit job');
|
|
|
|
|
|
},
|
|
|
|
|
|
complete: function() {
|
|
|
|
|
|
button.prop('disabled', false).text('Resubmit');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// View error details
|
|
|
|
|
|
$('.view-error-btn').click(function() {
|
|
|
|
|
|
var error = $(this).data('error');
|
|
|
|
|
|
$('#job-modal-title').text('Error Details');
|
|
|
|
|
|
$('#job-modal-body').text(error);
|
|
|
|
|
|
$('#job-modal').show();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// View results
|
|
|
|
|
|
$('.view-results-btn').click(function() {
|
|
|
|
|
|
var results = $(this).data('results');
|
|
|
|
|
|
try {
|
|
|
|
|
|
var parsedResults = JSON.parse(results);
|
|
|
|
|
|
$('#job-modal-title').text('Job Results');
|
|
|
|
|
|
$('#job-modal-body').text(JSON.stringify(parsedResults, null, 2));
|
|
|
|
|
|
} catch(e) {
|
|
|
|
|
|
$('#job-modal-body').text(results);
|
|
|
|
|
|
}
|
|
|
|
|
|
$('#job-modal').show();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Close modal
|
|
|
|
|
|
$('#job-modal-close').click(function() {
|
|
|
|
|
|
$('#job-modal').hide();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Close modal when clicking outside
|
|
|
|
|
|
$(window).click(function(event) {
|
|
|
|
|
|
if (event.target.id === 'job-modal') {
|
|
|
|
|
|
$('#job-modal').hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Auto-refresh for active jobs every 30 seconds
|
|
|
|
|
|
setInterval(function() {
|
|
|
|
|
|
var hasActiveJobs = $('.status-processing, .status-pending').length > 0;
|
|
|
|
|
|
if(hasActiveJobs) {
|
|
|
|
|
|
location.reload();
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 30000);
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|