Files
Miravia Connector Bot 552bce9f84 Fix image upload structure for Miravia API compliance
🔧 Bug Fixes:
- Fixed product image structure to match Miravia API requirements
- Updated MiraviaProduct.php getData() method to wrap images in {"Image": [...]} format
- Updated MiraviaCombination.php getData() method to wrap SKU images properly
- Resolved error "[4224] The Main image of the product is required"

📋 Changes:
- Modified getData() methods to transform flat image arrays to nested structure
- Product images: images[] → Images: {"Image": [...]}
- SKU images: images[] → Images: {"Image": [...]}
- Maintains backward compatibility for empty image arrays

🎯 Impact:
- Product uploads will now pass Miravia's image validation
- Both product-level and SKU-level images properly formatted
- Complies with official Miravia API documentation structure

🤖 Generated with Claude Code (https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-21 13:57:16 +02:00

392 lines
14 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
if ( ! defined( 'ABSPATH' ) ) { exit; }
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();
// 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')
];
?>
<div class="wrap">
<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">&times;</span>
<h3 id="job-modal-title">Details</h3>
<div id="job-modal-body"></div>
</div>
</div>
<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>