fix: improve onboarding UX and fix order sync error

- Remove redundant 1/4 progress counter from header
- Make step indicators mobile-friendly (smaller circles, hidden labels)
- Add CSV URL help text pointing to Letzshop Admin > API > Export Products
- Fix AttributeError in order sync progress (use correct model attributes)

🤖 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-28 09:13:16 +01:00
parent 4e6e6a27f9
commit ff2f475ae4
3 changed files with 43 additions and 46 deletions

View File

@@ -541,28 +541,31 @@ class OnboardingService:
elif job.status == "failed": elif job.status == "failed":
progress = 0 progress = 0
elif job.status == "processing": elif job.status == "processing":
if job.total_shipments and job.total_shipments > 0: # Use orders_processed and shipments_fetched for progress
progress = int((job.processed_shipments or 0) / job.total_shipments * 100) total = job.shipments_fetched or 0
processed = job.orders_processed or 0
if total > 0:
progress = int(processed / total * 100)
else: else:
progress = 50 # Indeterminate progress = 50 # Indeterminate
elif job.status == "fetching":
# Show partial progress during fetch phase
if job.total_pages and job.total_pages > 0:
progress = int((job.current_page or 0) / job.total_pages * 50)
else:
progress = 25 # Indeterminate
# Determine current phase # Determine current phase
current_phase = None current_phase = job.current_phase or job.status
if job.status == "fetching":
current_phase = "fetching"
elif job.status == "processing":
current_phase = "orders"
elif job.status == "completed":
current_phase = "complete"
return { return {
"job_id": job.id, "job_id": job.id,
"status": job.status, "status": job.status,
"progress_percentage": progress, "progress_percentage": progress,
"current_phase": current_phase, "current_phase": current_phase,
"orders_imported": job.processed_shipments or 0, "orders_imported": job.orders_imported or 0,
"orders_total": job.total_shipments, "orders_total": job.shipments_fetched,
"products_imported": 0, # TODO: Track this "products_imported": job.products_matched or 0,
"started_at": job.started_at, "started_at": job.started_at,
"completed_at": job.completed_at, "completed_at": job.completed_at,
"estimated_remaining_seconds": None, # TODO: Calculate "estimated_remaining_seconds": None, # TODO: Calculate

View File

@@ -23,51 +23,45 @@
</div> </div>
<span class="text-xl font-semibold text-gray-800 dark:text-white">Wizamart</span> <span class="text-xl font-semibold text-gray-800 dark:text-white">Wizamart</span>
</div> </div>
<div class="flex items-center space-x-4"> <!-- Language Selector -->
<!-- Language Selector --> <div class="relative" x-data="{ open: false }">
<div class="relative" x-data="{ open: false }"> <button @click="open = !open"
<button @click="open = !open" class="flex items-center space-x-2 px-3 py-2 rounded-lg bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors">
class="flex items-center space-x-2 px-3 py-2 rounded-lg bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors"> <span x-text="languageFlags[lang]"></span>
<span x-text="languageFlags[lang]"></span> <span class="text-sm text-gray-700 dark:text-gray-300 hidden sm:inline" x-text="languageNames[lang]"></span>
<span class="text-sm text-gray-700 dark:text-gray-300" x-text="languageNames[lang]"></span> <svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path> </svg>
</svg> </button>
</button> <div x-show="open" @click.away="open = false" x-cloak
<div x-show="open" @click.away="open = false" x-cloak class="absolute right-0 mt-2 w-40 bg-white dark:bg-gray-700 rounded-lg shadow-lg border border-gray-200 dark:border-gray-600 z-50">
class="absolute right-0 mt-2 w-40 bg-white dark:bg-gray-700 rounded-lg shadow-lg border border-gray-200 dark:border-gray-600 z-50"> <template x-for="langCode in availableLanguages" :key="langCode">
<template x-for="langCode in availableLanguages" :key="langCode"> <button @click="setLang(langCode); open = false"
<button @click="setLang(langCode); open = false" class="w-full flex items-center space-x-2 px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-600 first:rounded-t-lg last:rounded-b-lg"
class="w-full flex items-center space-x-2 px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-600 first:rounded-t-lg last:rounded-b-lg" :class="{ 'bg-purple-50 dark:bg-purple-900/20': lang === langCode }">
:class="{ 'bg-purple-50 dark:bg-purple-900/20': lang === langCode }"> <span x-text="languageFlags[langCode]"></span>
<span x-text="languageFlags[langCode]"></span> <span class="text-sm text-gray-700 dark:text-gray-300" x-text="languageNames[langCode]"></span>
<span class="text-sm text-gray-700 dark:text-gray-300" x-text="languageNames[langCode]"></span> </button>
</button> </template>
</template>
</div>
</div>
<!-- Progress -->
<div class="text-sm text-gray-600 dark:text-gray-400">
<span x-text="completedSteps"></span> / 4
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- Progress Indicator --> <!-- Progress Indicator -->
<div class="max-w-4xl mx-auto mb-8"> <div class="max-w-4xl mx-auto mb-8 px-2 sm:px-0">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<template x-for="(step, index) in steps" :key="step.id"> <template x-for="(step, index) in steps" :key="step.id">
<div class="flex items-center" :class="{ 'flex-1': index < steps.length - 1 }"> <div class="flex items-center" :class="{ 'flex-1': index < steps.length - 1 }">
<!-- Step Circle --> <!-- Step Circle -->
<div class="flex flex-col items-center"> <div class="flex flex-col items-center">
<div class="w-10 h-10 rounded-full flex items-center justify-center text-sm font-semibold transition-all duration-200" <div class="w-8 h-8 sm:w-10 sm:h-10 rounded-full flex items-center justify-center text-xs sm:text-sm font-semibold transition-all duration-200"
:class="{ :class="{
'bg-purple-600 text-white': isStepCompleted(step.id) || currentStep === step.id, 'bg-purple-600 text-white': isStepCompleted(step.id) || currentStep === step.id,
'bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400': !isStepCompleted(step.id) && currentStep !== step.id 'bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400': !isStepCompleted(step.id) && currentStep !== step.id
}"> }">
<template x-if="isStepCompleted(step.id)"> <template x-if="isStepCompleted(step.id)">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4 sm:w-5 sm:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg> </svg>
</template> </template>
@@ -75,12 +69,12 @@
<span x-text="index + 1"></span> <span x-text="index + 1"></span>
</template> </template>
</div> </div>
<span class="mt-2 text-xs font-medium text-gray-600 dark:text-gray-400 text-center w-24" <span class="mt-2 text-xs font-medium text-gray-600 dark:text-gray-400 text-center w-16 sm:w-24 hidden sm:block"
x-text="step.title"></span> x-text="step.title"></span>
</div> </div>
<!-- Connector Line --> <!-- Connector Line -->
<template x-if="index < steps.length - 1"> <template x-if="index < steps.length - 1">
<div class="flex-1 h-1 mx-4 rounded" <div class="flex-1 h-1 mx-1 sm:mx-4 rounded"
:class="{ :class="{
'bg-purple-600': isStepCompleted(step.id), 'bg-purple-600': isStepCompleted(step.id),
'bg-gray-200 dark:bg-gray-700': !isStepCompleted(step.id) 'bg-gray-200 dark:bg-gray-700': !isStepCompleted(step.id)

View File

@@ -61,7 +61,7 @@ const onboardingTranslations = {
csv_url_fr: 'French CSV URL', csv_url_fr: 'French CSV URL',
csv_url_en: 'English CSV URL', csv_url_en: 'English CSV URL',
csv_url_de: 'German CSV URL', csv_url_de: 'German CSV URL',
csv_url_help: 'At least one CSV URL is required', csv_url_help: 'Find your CSV URL in Letzshop Admin Panel > API > Export Products',
default_tax_rate: 'Default Tax Rate (%)', default_tax_rate: 'Default Tax Rate (%)',
delivery_method: 'Delivery Method', delivery_method: 'Delivery Method',
delivery_package: 'Package Delivery', delivery_package: 'Package Delivery',
@@ -139,7 +139,7 @@ const onboardingTranslations = {
csv_url_fr: 'URL CSV Français', csv_url_fr: 'URL CSV Français',
csv_url_en: 'URL CSV Anglais', csv_url_en: 'URL CSV Anglais',
csv_url_de: 'URL CSV Allemand', csv_url_de: 'URL CSV Allemand',
csv_url_help: 'Au moins une URL CSV est requise', csv_url_help: 'Trouvez votre URL CSV dans Letzshop Admin > API > Exporter Produits',
default_tax_rate: 'Taux de TVA par Défaut (%)', default_tax_rate: 'Taux de TVA par Défaut (%)',
delivery_method: 'Méthode de Livraison', delivery_method: 'Méthode de Livraison',
delivery_package: 'Livraison Colis', delivery_package: 'Livraison Colis',
@@ -217,7 +217,7 @@ const onboardingTranslations = {
csv_url_fr: 'Französische CSV-URL', csv_url_fr: 'Französische CSV-URL',
csv_url_en: 'Englische CSV-URL', csv_url_en: 'Englische CSV-URL',
csv_url_de: 'Deutsche CSV-URL', csv_url_de: 'Deutsche CSV-URL',
csv_url_help: 'Mindestens eine CSV-URL ist erforderlich', csv_url_help: 'Finden Sie Ihre CSV-URL im Letzshop Admin-Panel > API > Produkte exportieren',
default_tax_rate: 'Standard-Steuersatz (%)', default_tax_rate: 'Standard-Steuersatz (%)',
delivery_method: 'Liefermethode', delivery_method: 'Liefermethode',
delivery_package: 'Paketlieferung', delivery_package: 'Paketlieferung',