From 4e6e6a27f9093d32c1a7f943b6e020f8f4f55b70 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Sun, 28 Dec 2025 08:37:47 +0100 Subject: [PATCH] feat: add multilingual support to vendor onboarding workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add language selector with English, French, and German translations - Fix API key help text to reference Letzshop Support team - Update shop slug input with URL prefix and clearer example - Fix step validation bug by adding db.commit() to all POST endpoints - Translate all form labels, buttons, and messages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- app/api/v1/vendor/onboarding.py | 20 +- app/templates/vendor/onboarding.html | 163 +++++++++------- static/vendor/js/onboarding.js | 278 ++++++++++++++++++++++++++- 3 files changed, 376 insertions(+), 85 deletions(-) diff --git a/app/api/v1/vendor/onboarding.py b/app/api/v1/vendor/onboarding.py index 81c18a71..583566e2 100644 --- a/app/api/v1/vendor/onboarding.py +++ b/app/api/v1/vendor/onboarding.py @@ -92,7 +92,7 @@ def save_company_profile( Updates vendor and company records with provided data. """ service = OnboardingService(db) - return service.complete_company_profile( + result = service.complete_company_profile( vendor_id=current_user.token_vendor_id, company_name=request.company_name, brand_name=request.brand_name, @@ -105,6 +105,8 @@ def save_company_profile( default_language=request.default_language, dashboard_language=request.dashboard_language, ) + db.commit() # Commit at API level for transaction control + return result # ============================================================================= @@ -142,12 +144,14 @@ def save_letzshop_api( Tests connection first, only saves if successful. """ service = OnboardingService(db) - return service.complete_letzshop_api( + result = service.complete_letzshop_api( vendor_id=current_user.token_vendor_id, api_key=request.api_key, shop_slug=request.shop_slug, letzshop_vendor_id=request.vendor_id, ) + db.commit() # Commit at API level for transaction control + return result # ============================================================================= @@ -181,7 +185,7 @@ def save_product_import_config( At least one CSV URL must be provided. """ service = OnboardingService(db) - return service.complete_product_import( + result = service.complete_product_import( vendor_id=current_user.token_vendor_id, csv_url_fr=request.csv_url_fr, csv_url_en=request.csv_url_en, @@ -190,6 +194,8 @@ def save_product_import_config( delivery_method=request.delivery_method, preorder_days=request.preorder_days, ) + db.commit() # Commit at API level for transaction control + return result # ============================================================================= @@ -209,12 +215,14 @@ def trigger_order_sync( Creates a background job that imports orders from Letzshop. """ service = OnboardingService(db) - return service.trigger_order_sync( + result = service.trigger_order_sync( vendor_id=current_user.token_vendor_id, user_id=current_user.id, days_back=request.days_back, include_products=request.include_products, ) + db.commit() # Commit at API level for transaction control + return result @router.get( @@ -251,7 +259,9 @@ def complete_order_sync( This also marks the entire onboarding as complete. """ service = OnboardingService(db) - return service.complete_order_sync( + result = service.complete_order_sync( vendor_id=current_user.token_vendor_id, job_id=request.job_id, ) + db.commit() # Commit at API level for transaction control + return result diff --git a/app/templates/vendor/onboarding.html b/app/templates/vendor/onboarding.html index 7cb94b21..8153a15d 100644 --- a/app/templates/vendor/onboarding.html +++ b/app/templates/vendor/onboarding.html @@ -23,8 +23,33 @@ Wizamart -
- of 4 steps completed +
+ +
+ +
+ +
+
+ +
+ / 4 +
@@ -74,15 +99,14 @@ -

Loading your setup...

+

-
@@ -92,77 +116,75 @@
-

Company Profile

-

- Let's set up your company information. This will be used for invoices and customer communication. -

+

+

- +
- +
- -
- +
- +
- +
- - +
- +
- +
- +
@@ -172,53 +194,52 @@
-

Letzshop API Configuration

-

- Connect your Letzshop marketplace account to sync orders automatically. -

+

+

- - +

- Get your API key from Letzshop Vendor Portal > Settings > API Access + (support@letzshop.lu)

- +
- letzshop.lu/vendors/ + letzshop.lu/.../vendors/ -
+

- Connection successful + - +
@@ -227,24 +248,23 @@
-

Product Import Configuration

-

- Set up your product CSV feed URLs for each language. At least one URL is required. -

+

+

- + +

- +
- +
@@ -252,7 +272,7 @@

Letzshop Feed Settings

- +
- +
- + +

@@ -284,28 +305,26 @@
-

Historical Order Import

-

- Import your existing orders from Letzshop to get started with a complete order history. -

+

+

- +
@@ -322,7 +341,7 @@

- orders imported +

@@ -333,9 +352,9 @@
-

Import Complete!

+

- orders have been imported. + .

@@ -345,8 +364,8 @@
@@ -354,14 +373,14 @@ @click="saveAndContinue()" :disabled="saving" class="px-6 py-2 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 focus:outline-none disabled:opacity-50"> - + - Saving... +
diff --git a/static/vendor/js/onboarding.js b/static/vendor/js/onboarding.js index e9976b0d..1b9567d0 100644 --- a/static/vendor/js/onboarding.js +++ b/static/vendor/js/onboarding.js @@ -13,21 +13,283 @@ const onboardingLog = window.LogConfig?.createLogger('ONBOARDING') || console; -function vendorOnboarding() { +// Onboarding translations +const onboardingTranslations = { + en: { + title: 'Welcome to Wizamart', + subtitle: 'Complete these steps to set up your store', + steps: { + company_profile: 'Company Profile', + letzshop_api: 'Letzshop API', + product_import: 'Product Import', + order_sync: 'Order Sync', + }, + step1: { + title: 'Company Profile Setup', + description: 'Tell us about your business. This information will be used for invoices and your store profile.', + company_name: 'Company Name', + brand_name: 'Brand Name', + brand_name_help: 'The name customers will see', + description_label: 'Description', + description_placeholder: 'Brief description of your business', + contact_email: 'Contact Email', + contact_phone: 'Contact Phone', + website: 'Website', + business_address: 'Business Address', + tax_number: 'Tax Number (VAT)', + tax_number_placeholder: 'e.g., LU12345678', + default_language: 'Default Shop Language', + dashboard_language: 'Dashboard Language', + }, + step2: { + title: 'Letzshop API Configuration', + description: 'Connect your Letzshop marketplace account to sync orders automatically.', + api_key: 'Letzshop API Key', + api_key_placeholder: 'Enter your API key', + api_key_help: 'Get your API key from Letzshop Support team', + shop_slug: 'Shop Slug', + shop_slug_help: 'Enter the last part of your Letzshop vendor URL', + test_connection: 'Test Connection', + testing: 'Testing...', + connection_success: 'Connection successful', + connection_failed: 'Connection failed', + }, + step3: { + title: 'Product Import Configuration', + description: 'Configure how products are imported from your CSV feeds.', + csv_urls: 'CSV Feed URLs', + csv_url_fr: 'French CSV URL', + csv_url_en: 'English CSV URL', + csv_url_de: 'German CSV URL', + csv_url_help: 'At least one CSV URL is required', + default_tax_rate: 'Default Tax Rate (%)', + delivery_method: 'Delivery Method', + delivery_package: 'Package Delivery', + delivery_pickup: 'Store Pickup', + preorder_days: 'Preorder Days', + preorder_days_help: 'Days before product is available after order', + }, + step4: { + title: 'Historical Order Import', + description: 'Import your existing orders from Letzshop to start managing them in Wizamart.', + days_back: 'Import orders from last', + days: 'days', + start_import: 'Start Import', + importing: 'Importing...', + import_complete: 'Import Complete!', + orders_imported: 'orders imported', + skip_step: 'Skip this step', + }, + buttons: { + save_continue: 'Save & Continue', + saving: 'Saving...', + back: 'Back', + complete: 'Complete Setup', + retry: 'Retry', + }, + loading: 'Loading your setup...', + errors: { + load_failed: 'Failed to load onboarding status', + save_failed: 'Failed to save. Please try again.', + }, + }, + fr: { + title: 'Bienvenue sur Wizamart', + subtitle: 'Complétez ces étapes pour configurer votre boutique', + steps: { + company_profile: 'Profil Entreprise', + letzshop_api: 'API Letzshop', + product_import: 'Import Produits', + order_sync: 'Sync Commandes', + }, + step1: { + title: 'Configuration du Profil Entreprise', + description: 'Parlez-nous de votre entreprise. Ces informations seront utilisées pour les factures et le profil de votre boutique.', + company_name: 'Nom de l\'Entreprise', + brand_name: 'Nom de la Marque', + brand_name_help: 'Le nom que les clients verront', + description_label: 'Description', + description_placeholder: 'Brève description de votre activité', + contact_email: 'Email de Contact', + contact_phone: 'Téléphone de Contact', + website: 'Site Web', + business_address: 'Adresse Professionnelle', + tax_number: 'Numéro de TVA', + tax_number_placeholder: 'ex: LU12345678', + default_language: 'Langue par Défaut de la Boutique', + dashboard_language: 'Langue du Tableau de Bord', + }, + step2: { + title: 'Configuration de l\'API Letzshop', + description: 'Connectez votre compte Letzshop pour synchroniser automatiquement les commandes.', + api_key: 'Clé API Letzshop', + api_key_placeholder: 'Entrez votre clé API', + api_key_help: 'Obtenez votre clé API auprès de l\'équipe Support Letzshop', + shop_slug: 'Identifiant Boutique', + shop_slug_help: 'Entrez la dernière partie de votre URL vendeur Letzshop', + test_connection: 'Tester la Connexion', + testing: 'Test en cours...', + connection_success: 'Connexion réussie', + connection_failed: 'Échec de la connexion', + }, + step3: { + title: 'Configuration Import Produits', + description: 'Configurez comment les produits sont importés depuis vos flux CSV.', + csv_urls: 'URLs des Flux CSV', + csv_url_fr: 'URL CSV Français', + csv_url_en: 'URL CSV Anglais', + csv_url_de: 'URL CSV Allemand', + csv_url_help: 'Au moins une URL CSV est requise', + default_tax_rate: 'Taux de TVA par Défaut (%)', + delivery_method: 'Méthode de Livraison', + delivery_package: 'Livraison Colis', + delivery_pickup: 'Retrait en Magasin', + preorder_days: 'Jours de Précommande', + preorder_days_help: 'Jours avant disponibilité du produit après commande', + }, + step4: { + title: 'Import Historique des Commandes', + description: 'Importez vos commandes existantes de Letzshop pour commencer à les gérer dans Wizamart.', + days_back: 'Importer les commandes des derniers', + days: 'jours', + start_import: 'Démarrer l\'Import', + importing: 'Import en cours...', + import_complete: 'Import Terminé !', + orders_imported: 'commandes importées', + skip_step: 'Passer cette étape', + }, + buttons: { + save_continue: 'Enregistrer & Continuer', + saving: 'Enregistrement...', + back: 'Retour', + complete: 'Terminer la Configuration', + retry: 'Réessayer', + }, + loading: 'Chargement de votre configuration...', + errors: { + load_failed: 'Échec du chargement du statut d\'onboarding', + save_failed: 'Échec de l\'enregistrement. Veuillez réessayer.', + }, + }, + de: { + title: 'Willkommen bei Wizamart', + subtitle: 'Führen Sie diese Schritte aus, um Ihren Shop einzurichten', + steps: { + company_profile: 'Firmenprofil', + letzshop_api: 'Letzshop API', + product_import: 'Produktimport', + order_sync: 'Bestellsync', + }, + step1: { + title: 'Firmenprofil Einrichten', + description: 'Erzählen Sie uns von Ihrem Unternehmen. Diese Informationen werden für Rechnungen und Ihr Shop-Profil verwendet.', + company_name: 'Firmenname', + brand_name: 'Markenname', + brand_name_help: 'Der Name, den Kunden sehen werden', + description_label: 'Beschreibung', + description_placeholder: 'Kurze Beschreibung Ihres Unternehmens', + contact_email: 'Kontakt-E-Mail', + contact_phone: 'Kontakttelefon', + website: 'Website', + business_address: 'Geschäftsadresse', + tax_number: 'Steuernummer (USt-IdNr.)', + tax_number_placeholder: 'z.B. LU12345678', + default_language: 'Standard-Shop-Sprache', + dashboard_language: 'Dashboard-Sprache', + }, + step2: { + title: 'Letzshop API Konfiguration', + description: 'Verbinden Sie Ihr Letzshop-Konto, um Bestellungen automatisch zu synchronisieren.', + api_key: 'Letzshop API-Schlüssel', + api_key_placeholder: 'Geben Sie Ihren API-Schlüssel ein', + api_key_help: 'Erhalten Sie Ihren API-Schlüssel vom Letzshop Support-Team', + shop_slug: 'Shop-Slug', + shop_slug_help: 'Geben Sie den letzten Teil Ihrer Letzshop-Verkäufer-URL ein', + test_connection: 'Verbindung Testen', + testing: 'Teste...', + connection_success: 'Verbindung erfolgreich', + connection_failed: 'Verbindung fehlgeschlagen', + }, + step3: { + title: 'Produktimport Konfiguration', + description: 'Konfigurieren Sie, wie Produkte aus Ihren CSV-Feeds importiert werden.', + csv_urls: 'CSV-Feed-URLs', + csv_url_fr: 'Französische CSV-URL', + csv_url_en: 'Englische CSV-URL', + csv_url_de: 'Deutsche CSV-URL', + csv_url_help: 'Mindestens eine CSV-URL ist erforderlich', + default_tax_rate: 'Standard-Steuersatz (%)', + delivery_method: 'Liefermethode', + delivery_package: 'Paketlieferung', + delivery_pickup: 'Abholung im Geschäft', + preorder_days: 'Vorbestelltage', + preorder_days_help: 'Tage bis zur Verfügbarkeit nach Bestellung', + }, + step4: { + title: 'Historischer Bestellimport', + description: 'Importieren Sie Ihre bestehenden Bestellungen von Letzshop, um sie in Wizamart zu verwalten.', + days_back: 'Bestellungen der letzten importieren', + days: 'Tage', + start_import: 'Import Starten', + importing: 'Importiere...', + import_complete: 'Import Abgeschlossen!', + orders_imported: 'Bestellungen importiert', + skip_step: 'Diesen Schritt überspringen', + }, + buttons: { + save_continue: 'Speichern & Fortfahren', + saving: 'Speichern...', + back: 'Zurück', + complete: 'Einrichtung Abschließen', + retry: 'Erneut versuchen', + }, + loading: 'Ihre Einrichtung wird geladen...', + errors: { + load_failed: 'Onboarding-Status konnte nicht geladen werden', + save_failed: 'Speichern fehlgeschlagen. Bitte versuchen Sie es erneut.', + }, + }, +}; + +function vendorOnboarding(initialLang = 'en') { return { + // Language + lang: initialLang || localStorage.getItem('onboarding_lang') || 'en', + availableLanguages: ['en', 'fr', 'de'], + languageNames: { en: 'English', fr: 'Français', de: 'Deutsch' }, + languageFlags: { en: '🇬🇧', fr: '🇫🇷', de: '🇩🇪' }, + + // Translation helper + t(key) { + const keys = key.split('.'); + let value = onboardingTranslations[this.lang]; + for (const k of keys) { + value = value?.[k]; + } + return value || key; + }, + + // Change language + setLang(newLang) { + this.lang = newLang; + localStorage.setItem('onboarding_lang', newLang); + }, + // State loading: true, saving: false, testing: false, error: null, - // Steps configuration - steps: [ - { id: 'company_profile', title: 'Company Profile' }, - { id: 'letzshop_api', title: 'Letzshop API' }, - { id: 'product_import', title: 'Product Import' }, - { id: 'order_sync', title: 'Order Sync' }, - ], + // Steps configuration (will be populated with translated titles) + get steps() { + return [ + { id: 'company_profile', title: this.t('steps.company_profile') }, + { id: 'letzshop_api', title: this.t('steps.letzshop_api') }, + { id: 'product_import', title: this.t('steps.product_import') }, + { id: 'order_sync', title: this.t('steps.order_sync') }, + ]; + }, // Current state currentStep: 'company_profile',