feat(loyalty): category view mode + mandatory translations
Some checks failed
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / ruff (push) Successful in 14s
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has been cancelled

- Add eye icon to expand read-only view of all translations
- View panel shows EN/FR/DE/LB values with "Edit" button to switch
  to edit mode
- All 4 language fields (EN/FR/DE/LB) now mandatory — Save button
  disabled until all are filled (both add and edit forms)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-25 12:34:37 +02:00
parent f8b2429533
commit 62b83b46a4
2 changed files with 41 additions and 11 deletions

View File

@@ -39,6 +39,7 @@ function adminLoyaltyMerchantDetail() {
showAddCategory: false, showAddCategory: false,
newCategoryName: '', newCategoryName: '',
newCategoryTranslations: { fr: '', de: '', lb: '' }, newCategoryTranslations: { fr: '', de: '', lb: '' },
viewingCategoryId: null,
editingCategoryId: null, editingCategoryId: null,
editCategoryData: { name: '', translations: { fr: '', de: '', lb: '' } }, editCategoryData: { name: '', translations: { fr: '', de: '', lb: '' } },

View File

@@ -259,7 +259,7 @@
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600"> class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600">
{{ _('loyalty.common.cancel') }} {{ _('loyalty.common.cancel') }}
</button> </button>
<button @click="createCategory()" :disabled="!newCategoryName" <button @click="createCategory()" :disabled="!newCategoryName || !newCategoryTranslations.fr || !newCategoryTranslations.de || !newCategoryTranslations.lb"
class="px-4 py-2 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 disabled:opacity-50"> class="px-4 py-2 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 disabled:opacity-50">
{{ _('loyalty.common.save') }} {{ _('loyalty.common.save') }}
</button> </button>
@@ -270,20 +270,17 @@
<div class="space-y-2"> <div class="space-y-2">
<template x-for="cat in storeCategories" :key="cat.id"> <template x-for="cat in storeCategories" :key="cat.id">
<div class="border border-gray-200 dark:border-gray-700 rounded-lg"> <div class="border border-gray-200 dark:border-gray-700 rounded-lg">
<!-- View mode --> <!-- List mode -->
<div x-show="editingCategoryId !== cat.id" class="flex items-center justify-between p-3"> <div x-show="viewingCategoryId !== cat.id && editingCategoryId !== cat.id" class="flex items-center justify-between p-3">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<span class="text-sm font-medium text-gray-700 dark:text-gray-300" x-text="cat.name"></span> <span class="text-sm font-medium text-gray-700 dark:text-gray-300" x-text="cat.name"></span>
<template x-if="cat.name_translations">
<span class="text-xs text-gray-400" x-text="Object.entries(cat.name_translations || {}).filter(([k,v]) => v && k !== 'en').map(([k,v]) => k.toUpperCase() + ': ' + v).join(' · ')"></span>
</template>
<span x-show="!cat.is_active" class="px-2 py-0.5 text-xs rounded-full bg-gray-100 text-gray-500 dark:bg-gray-700">{{ _('loyalty.common.inactive') }}</span> <span x-show="!cat.is_active" class="px-2 py-0.5 text-xs rounded-full bg-gray-100 text-gray-500 dark:bg-gray-700">{{ _('loyalty.common.inactive') }}</span>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button @click="startEditCategory(cat)" type="button" <button @click="viewingCategoryId = (viewingCategoryId === cat.id ? null : cat.id)" type="button"
aria-label="{{ _('loyalty.common.edit') }}" aria-label="{{ _('loyalty.common.view') }}"
class="text-purple-500 hover:text-purple-700"> class="text-blue-500 hover:text-blue-700">
<span x-html="$icon('pencil', 'w-4 h-4')"></span> <span x-html="$icon('eye', 'w-4 h-4')"></span>
</button> </button>
<button @click="toggleCategoryActive(cat)" type="button" <button @click="toggleCategoryActive(cat)" type="button"
:aria-label="cat.is_active ? '{{ _('loyalty.common.deactivate') }}' : '{{ _('loyalty.common.activate') }}'" :aria-label="cat.is_active ? '{{ _('loyalty.common.deactivate') }}' : '{{ _('loyalty.common.activate') }}'"
@@ -297,6 +294,38 @@
</button> </button>
</div> </div>
</div> </div>
<!-- View mode (read-only) -->
<div x-show="viewingCategoryId === cat.id && editingCategoryId !== cat.id" class="p-3 bg-gray-50 dark:bg-gray-900/20">
<div class="grid gap-2 md:grid-cols-2 mb-3">
<div>
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">English (EN)</p>
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="cat.name || '-'"></p>
</div>
<div>
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">French (FR)</p>
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="cat.name_translations?.fr || '-'"></p>
</div>
<div>
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">German (DE)</p>
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="cat.name_translations?.de || '-'"></p>
</div>
<div>
<p class="text-xs font-medium text-gray-500 dark:text-gray-400">Luxembourgish (LB)</p>
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="cat.name_translations?.lb || '-'"></p>
</div>
</div>
<div class="flex justify-end gap-2">
<button @click="viewingCategoryId = null" type="button"
class="px-3 py-1.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600">
{{ _('loyalty.common.close') }}
</button>
<button @click="viewingCategoryId = null; startEditCategory(cat)" type="button"
class="px-3 py-1.5 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700">
<span x-html="$icon('pencil', 'inline w-3.5 h-3.5 mr-1')"></span>
{{ _('loyalty.common.edit') }}
</button>
</div>
</div>
<!-- Edit mode --> <!-- Edit mode -->
<div x-show="editingCategoryId === cat.id" class="p-3 bg-purple-50 dark:bg-purple-900/20"> <div x-show="editingCategoryId === cat.id" class="p-3 bg-purple-50 dark:bg-purple-900/20">
<div class="grid gap-2 md:grid-cols-2 mb-3"> <div class="grid gap-2 md:grid-cols-2 mb-3">
@@ -326,7 +355,7 @@
class="px-3 py-1.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600"> class="px-3 py-1.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600">
{{ _('loyalty.common.cancel') }} {{ _('loyalty.common.cancel') }}
</button> </button>
<button @click="saveEditCategory(cat.id)" :disabled="!editCategoryData.name" <button @click="saveEditCategory(cat.id)" :disabled="!editCategoryData.name || !editCategoryData.translations.fr || !editCategoryData.translations.de || !editCategoryData.translations.lb"
class="px-3 py-1.5 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 disabled:opacity-50"> class="px-3 py-1.5 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 disabled:opacity-50">
{{ _('loyalty.common.save') }} {{ _('loyalty.common.save') }}
</button> </button>