fix(loyalty): terminal icons, server-side i18n, category in transactions
Some checks failed
Some checks failed
- Fix icons: plus-circle → plus, backspace → arrow-left - Convert terminal $t() calls to server-side _() for card_label, stamps_until_reward, reward_label, not_enough_stamps - Inject transaction type labels as server-rendered window._txLabels object (eliminates all async i18n warnings on terminal page) - Resolve category_names in store transactions list endpoint Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -652,11 +652,22 @@ def list_store_transactions(
|
||||
db, merchant_id, skip=skip, limit=limit
|
||||
)
|
||||
|
||||
from app.modules.loyalty.services.category_service import category_service
|
||||
|
||||
tx_responses = []
|
||||
for t in transactions:
|
||||
tx = TransactionResponse.model_validate(t)
|
||||
if t.card and t.card.customer:
|
||||
tx.customer_name = t.card.customer.full_name
|
||||
if t.category_ids and isinstance(t.category_ids, list):
|
||||
names = []
|
||||
for cid in t.category_ids:
|
||||
name = category_service.validate_category_for_store(
|
||||
db, cid, t.store_id or 0
|
||||
)
|
||||
if name:
|
||||
names.append(name)
|
||||
tx.category_names = names if names else None
|
||||
tx_responses.append(tx)
|
||||
|
||||
return TransactionListResponse(transactions=tx_responses, total=total)
|
||||
|
||||
@@ -369,9 +369,11 @@ function storeLoyaltyTerminal() {
|
||||
getTransactionLabel(tx) {
|
||||
const type = tx.transaction_type;
|
||||
if (type) {
|
||||
return I18n.t('loyalty.transactions.' + type, {defaultValue: type.replace(/_/g, ' ')});
|
||||
// Use server-rendered labels (no async flicker)
|
||||
if (window._txLabels && window._txLabels[type]) return window._txLabels[type];
|
||||
return type.replace(/_/g, ' ');
|
||||
}
|
||||
return I18n.t('loyalty.common.unknown');
|
||||
return 'Unknown';
|
||||
},
|
||||
|
||||
getTransactionColor(tx) {
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
<div class="ml-4 flex-1">
|
||||
<p class="font-semibold text-gray-700 dark:text-gray-200" x-text="selectedCard?.customer_name || 'Unknown'"></p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400" x-text="selectedCard?.customer_email"></p>
|
||||
<p class="text-xs text-gray-400 dark:text-gray-500" x-text="$t('loyalty.store.terminal.card_label') + ': ' + selectedCard?.card_number"></p>
|
||||
<p class="text-xs text-gray-400 dark:text-gray-500" x-text="'{{ _('loyalty.store.terminal.card_label') }}' + ': ' + selectedCard?.card_number"></p>
|
||||
</div>
|
||||
<button @click="clearCustomer()" type="button" aria-label="{{ _('loyalty.common.close') }}" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
||||
<span x-html="$icon('x', 'w-5 h-5')"></span>
|
||||
@@ -156,7 +156,7 @@
|
||||
:style="'color: ' + (program?.card_color || '#4F46E5')"
|
||||
x-text="(selectedCard?.stamp_count || 0) + ' / ' + (program?.stamps_target || 10)"></p>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1"
|
||||
x-text="selectedCard?.stamps_until_reward > 0 ? $t('loyalty.store.terminal.more_for_reward', {count: selectedCard.stamps_until_reward}) : $t('loyalty.store.terminal.ready_to_redeem')"></p>
|
||||
x-text="selectedCard?.stamps_until_reward > 0 ? '{{ _('loyalty.store.terminal.more_for_reward') }}'.replace('{count}', selectedCard.stamps_until_reward) : '{{ _('loyalty.store.terminal.ready_to_redeem') }}'"></p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -168,7 +168,7 @@
|
||||
<template x-if="program?.is_stamps_enabled">
|
||||
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
|
||||
<h4 class="font-medium text-gray-700 dark:text-gray-300 mb-3">
|
||||
<span x-html="$icon('plus-circle', 'inline w-4 h-4 mr-1 text-green-500')"></span>
|
||||
<span x-html="$icon('plus', 'inline w-4 h-4 mr-1 text-green-500')"></span>
|
||||
{{ _('loyalty.store.terminal.add_stamp') }}
|
||||
</h4>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mb-3">
|
||||
@@ -192,7 +192,7 @@
|
||||
{{ _('loyalty.store.terminal.redeem_stamps') }}
|
||||
</h4>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mb-3"
|
||||
x-text="selectedCard?.can_redeem_stamps ? $t('loyalty.store.terminal.reward_label') + ': ' + (selectedCard?.stamp_reward_description || program?.stamps_reward_description || $t('loyalty.store.terminal.free_item')) : $t('loyalty.store.terminal.not_enough_stamps')"></p>
|
||||
x-text="selectedCard?.can_redeem_stamps ? '{{ _('loyalty.store.terminal.reward_label') }}' + ': ' + (selectedCard?.stamp_reward_description || program?.stamps_reward_description || '{{ _('loyalty.store.terminal.free_item') }}') : '{{ _('loyalty.store.terminal.not_enough_stamps') }}'"></p>
|
||||
<button @click="showPinModal('redeemStamps')"
|
||||
:disabled="!selectedCard?.can_redeem_stamps"
|
||||
class="w-full px-4 py-2 text-sm font-medium text-white bg-orange-600 rounded-lg hover:bg-orange-700 disabled:opacity-50">
|
||||
@@ -205,7 +205,7 @@
|
||||
<template x-if="program?.is_points_enabled">
|
||||
<div class="p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
|
||||
<h4 class="font-medium text-gray-700 dark:text-gray-300 mb-3">
|
||||
<span x-html="$icon('plus-circle', 'inline w-4 h-4 mr-1 text-green-500')"></span>
|
||||
<span x-html="$icon('plus', 'inline w-4 h-4 mr-1 text-green-500')"></span>
|
||||
{{ _('loyalty.store.terminal.earn_points') }}
|
||||
</h4>
|
||||
<div class="mb-3">
|
||||
@@ -370,7 +370,7 @@
|
||||
</button>
|
||||
<button @click="removePinDigit()" type="button" aria-label="{{ _('loyalty.common.back') }}"
|
||||
class="h-14 text-sm font-medium rounded-lg bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600">
|
||||
<span x-html="$icon('backspace', 'w-6 h-6 mx-auto')"></span>
|
||||
<span x-html="$icon('arrow-left', 'w-6 h-6 mx-auto')"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-end gap-3">
|
||||
@@ -390,5 +390,21 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_scripts %}
|
||||
<script>
|
||||
// Server-rendered transaction type labels (avoids async i18n flicker)
|
||||
window._txLabels = {
|
||||
card_created: {{ _('loyalty.transactions.card_created')|tojson }},
|
||||
welcome_bonus: {{ _('loyalty.transactions.welcome_bonus')|tojson }},
|
||||
stamp_earned: {{ _('loyalty.transactions.stamp_earned')|tojson }},
|
||||
stamp_redeemed: {{ _('loyalty.transactions.stamp_redeemed')|tojson }},
|
||||
stamp_voided: {{ _('loyalty.transactions.stamp_voided')|tojson }},
|
||||
points_earned: {{ _('loyalty.transactions.points_earned')|tojson }},
|
||||
points_redeemed: {{ _('loyalty.transactions.points_redeemed')|tojson }},
|
||||
points_voided: {{ _('loyalty.transactions.points_voided')|tojson }},
|
||||
points_adjustment: {{ _('loyalty.transactions.points_adjustment')|tojson }},
|
||||
points_expired: {{ _('loyalty.transactions.points_expired')|tojson }},
|
||||
reward_redeemed: {{ _('loyalty.transactions.reward_redeemed')|tojson }},
|
||||
};
|
||||
</script>
|
||||
<script defer src="{{ url_for('loyalty_static', path='store/js/loyalty-terminal.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user