fix: correct import job modal field names and improve UI

- Fix field name mismatch: use 'imported' and 'updated' instead of
  'imported_count' and 'updated_count' to match API response schema
- Add progress stats cards with color-coded metrics (imported, updated,
  errors, total) for better visual hierarchy
- Replace grid layout with structured table for job details
- Add Language field to display import language
- Add error_message display with icon
- Improve header with icon and subtitle
- Better dark mode support with consistent styling
- Add border and rounded corners to table container

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-11 17:35:36 +01:00
parent 2ecc2a9785
commit 5670b83eab

View File

@@ -492,7 +492,8 @@
Required Alpine.js state:
- showJobModal: boolean
- selectedJob: object with job data
- selectedJob: object with job data (fields: id, vendor_id, marketplace, status, source_url,
imported, updated, error_count, total_processed, started_at, completed_at, language)
- closeJobModal(): function to close and clear
- getVendorName(id): function to resolve vendor name
- formatDate(date): function to format dates
@@ -517,93 +518,127 @@
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0 transform translate-y-1/2">
{# Modal Header #}
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-200">
Import Job Details
</h3>
<button @click="{{ close_action }}" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
<div class="flex items-center justify-between pb-4 border-b border-gray-200 dark:border-gray-700">
<div class="flex items-center space-x-3">
<div class="p-2 bg-purple-100 dark:bg-purple-900/30 rounded-lg">
<span x-html="$icon('cube', 'w-5 h-5 text-purple-600 dark:text-purple-400')"></span>
</div>
<div>
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">
Import Job #<span x-text="{{ job_var }}?.id"></span>
</h3>
<p class="text-sm text-gray-500 dark:text-gray-400" x-text="{{ job_var }}?.marketplace + ' Import'"></p>
</div>
</div>
<button @click="{{ close_action }}" class="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors">
<span x-html="$icon('close', 'w-5 h-5')"></span>
</button>
</div>
{# Modal Content #}
<div x-show="{{ job_var }}" class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Job ID</p>
<p class="text-sm text-gray-900 dark:text-gray-100" x-text="{{ job_var }}?.id"></p>
<div x-show="{{ job_var }}" class="py-4 space-y-6">
{# Status Badge #}
<div class="flex items-center justify-between">
<span class="text-sm font-medium text-gray-600 dark:text-gray-400">Status</span>
<span class="px-3 py-1 font-semibold leading-tight rounded-full text-sm"
:class="{
'text-green-700 bg-green-100 dark:bg-green-900/30 dark:text-green-400': {{ job_var }}?.status === 'completed',
'text-blue-700 bg-blue-100 dark:bg-blue-900/30 dark:text-blue-400': {{ job_var }}?.status === 'processing',
'text-yellow-700 bg-yellow-100 dark:bg-yellow-900/30 dark:text-yellow-400': {{ job_var }}?.status === 'pending',
'text-red-700 bg-red-100 dark:bg-red-900/30 dark:text-red-400': {{ job_var }}?.status === 'failed',
'text-orange-700 bg-orange-100 dark:bg-orange-900/30 dark:text-orange-400': {{ job_var }}?.status === 'completed_with_errors'
}"
x-text="{{ job_var }}?.status.replace('_', ' ').toUpperCase()">
</span>
</div>
{# Progress Stats Cards #}
<div class="grid grid-cols-4 gap-3">
<div class="p-3 bg-green-50 dark:bg-green-900/20 rounded-lg text-center">
<p class="text-2xl font-bold text-green-600 dark:text-green-400" x-text="{{ job_var }}?.imported ?? 0"></p>
<p class="text-xs text-green-700 dark:text-green-300 mt-1">Imported</p>
</div>
<div>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Vendor</p>
<p class="text-sm text-gray-900 dark:text-gray-100" x-text="{{ get_vendor_name }}({{ job_var }}?.vendor_id)"></p>
<div class="p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg text-center">
<p class="text-2xl font-bold text-blue-600 dark:text-blue-400" x-text="{{ job_var }}?.updated ?? 0"></p>
<p class="text-xs text-blue-700 dark:text-blue-300 mt-1">Updated</p>
</div>
<div>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Marketplace</p>
<p class="text-sm text-gray-900 dark:text-gray-100" x-text="{{ job_var }}?.marketplace"></p>
<div class="p-3 bg-red-50 dark:bg-red-900/20 rounded-lg text-center">
<p class="text-2xl font-bold text-red-600 dark:text-red-400" x-text="{{ job_var }}?.error_count ?? 0"></p>
<p class="text-xs text-red-700 dark:text-red-300 mt-1">Errors</p>
</div>
<div>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Status</p>
<span class="px-2 py-1 font-semibold leading-tight rounded-full text-xs"
:class="{
'text-green-700 bg-green-100': {{ job_var }}?.status === 'completed',
'text-blue-700 bg-blue-100': {{ job_var }}?.status === 'processing',
'text-yellow-700 bg-yellow-100': {{ job_var }}?.status === 'pending',
'text-red-700 bg-red-100': {{ job_var }}?.status === 'failed',
'text-orange-700 bg-orange-100': {{ job_var }}?.status === 'completed_with_errors'
}"
x-text="{{ job_var }}?.status.replace('_', ' ').toUpperCase()">
</span>
<div class="p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg text-center">
<p class="text-2xl font-bold text-gray-600 dark:text-gray-300" x-text="{{ job_var }}?.total_processed ?? 0"></p>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Total</p>
</div>
<div class="col-span-2">
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Source URL</p>
<p class="text-sm text-gray-900 dark:text-gray-100 break-all" x-text="{{ job_var }}?.source_url"></p>
</div>
{# Details Table #}
<div class="overflow-hidden border border-gray-200 dark:border-gray-700 rounded-lg">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
<tr>
<td class="px-4 py-3 text-sm font-medium text-gray-600 dark:text-gray-400 bg-gray-50 dark:bg-gray-700/50 w-1/3">Vendor</td>
<td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100" x-text="{{ get_vendor_name }}({{ job_var }}?.vendor_id)"></td>
</tr>
<tr>
<td class="px-4 py-3 text-sm font-medium text-gray-600 dark:text-gray-400 bg-gray-50 dark:bg-gray-700/50">Marketplace</td>
<td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100" x-text="{{ job_var }}?.marketplace"></td>
</tr>
<tr>
<td class="px-4 py-3 text-sm font-medium text-gray-600 dark:text-gray-400 bg-gray-50 dark:bg-gray-700/50">Language</td>
<td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100">
<span class="px-2 py-0.5 bg-gray-100 dark:bg-gray-600 rounded text-xs font-mono" x-text="{{ job_var }}?.language || 'en'"></span>
</td>
</tr>
<tr>
<td class="px-4 py-3 text-sm font-medium text-gray-600 dark:text-gray-400 bg-gray-50 dark:bg-gray-700/50">Source URL</td>
<td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100">
<span class="break-all text-xs font-mono" x-text="{{ job_var }}?.source_url"></span>
</td>
</tr>
<tr>
<td class="px-4 py-3 text-sm font-medium text-gray-600 dark:text-gray-400 bg-gray-50 dark:bg-gray-700/50">Started At</td>
<td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100" x-text="{{ job_var }}?.started_at ? formatDate({{ job_var }}.started_at) : 'Not started'"></td>
</tr>
<tr>
<td class="px-4 py-3 text-sm font-medium text-gray-600 dark:text-gray-400 bg-gray-50 dark:bg-gray-700/50">Completed At</td>
<td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100" x-text="{{ job_var }}?.completed_at ? formatDate({{ job_var }}.completed_at) : 'In progress'"></td>
</tr>
{% if show_created_by %}
<tr>
<td class="px-4 py-3 text-sm font-medium text-gray-600 dark:text-gray-400 bg-gray-50 dark:bg-gray-700/50">Created By</td>
<td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100" x-text="{{ job_var }}?.created_by_name || 'System'"></td>
</tr>
{% endif %}
</tbody>
</table>
</div>
{# Error Message #}
<div x-show="{{ job_var }}?.error_message" class="p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
<div class="flex items-start">
<span x-html="$icon('exclamation-circle', 'w-5 h-5 text-red-500 dark:text-red-400 mt-0.5 mr-2 flex-shrink-0')"></span>
<div>
<p class="text-sm font-medium text-red-800 dark:text-red-300">Error Message</p>
<p class="text-sm text-red-700 dark:text-red-400 mt-1" x-text="{{ job_var }}?.error_message"></p>
</div>
</div>
<div>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Imported</p>
<p class="text-sm text-green-600 dark:text-green-400" x-text="{{ job_var }}?.imported_count"></p>
</div>
<div>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Updated</p>
<p class="text-sm text-blue-600 dark:text-blue-400" x-text="{{ job_var }}?.updated_count"></p>
</div>
<div>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Errors</p>
<p class="text-sm text-red-600 dark:text-red-400" x-text="{{ job_var }}?.error_count"></p>
</div>
<div>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Total Processed</p>
<p class="text-sm text-gray-900 dark:text-gray-100" x-text="{{ job_var }}?.total_processed"></p>
</div>
<div>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Started At</p>
<p class="text-sm text-gray-900 dark:text-gray-100" x-text="{{ job_var }}?.started_at ? formatDate({{ job_var }}.started_at) : 'Not started'"></p>
</div>
<div>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Completed At</p>
<p class="text-sm text-gray-900 dark:text-gray-100" x-text="{{ job_var }}?.completed_at ? formatDate({{ job_var }}.completed_at) : 'Not completed'"></p>
</div>
{% if show_created_by %}
<div>
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Created By</p>
<p class="text-sm text-gray-900 dark:text-gray-100" x-text="{{ job_var }}?.created_by_name || 'System'"></p>
</div>
{% endif %}
</div>
{# Error Details #}
<div x-show="{{ job_var }}?.error_details?.length > 0" class="mt-4">
<div x-show="{{ job_var }}?.error_details?.length > 0">
<p class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-2">Error Details</p>
<div class="p-3 bg-red-50 dark:bg-red-900/20 rounded-lg max-h-48 overflow-y-auto">
<pre class="text-xs text-red-700 dark:text-red-300 whitespace-pre-wrap" x-text="JSON.stringify({{ job_var }}?.error_details, null, 2)"></pre>
<div class="p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg max-h-48 overflow-y-auto">
<pre class="text-xs text-red-700 dark:text-red-300 whitespace-pre-wrap font-mono" x-text="JSON.stringify({{ job_var }}?.error_details, null, 2)"></pre>
</div>
</div>
</div>
{# Modal Footer #}
<div class="flex justify-end mt-6">
<div class="flex justify-end pt-4 border-t border-gray-200 dark:border-gray-700">
<button
@click="{{ close_action }}"
class="px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition-colors duration-150 border border-gray-300 rounded-lg dark:text-gray-400 dark:border-gray-700 hover:border-gray-500 focus:outline-none"
class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"
>
Close
</button>