fix(loyalty): card detail — enrolled store name + copy buttons
Some checks failed
Some checks failed
- Fix "Enrolled at: Unknown" by resolving enrolled_at_store_name from the store service and adding it to CardDetailResponse schema. - Add clipboard-copy buttons next to card number, customer name, email, and phone fields using the shared Utils.copyToClipboard() utility with toast feedback. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -514,6 +514,17 @@ def get_card_detail(
|
|||||||
program = card.program
|
program = card.program
|
||||||
customer = card.customer
|
customer = card.customer
|
||||||
|
|
||||||
|
# Resolve enrolled store name
|
||||||
|
enrolled_store_name = None
|
||||||
|
if card.enrolled_at_store_id:
|
||||||
|
from app.modules.tenancy.services.store_service import store_service
|
||||||
|
|
||||||
|
enrolled_store = store_service.get_store_by_id_optional(
|
||||||
|
db, card.enrolled_at_store_id
|
||||||
|
)
|
||||||
|
if enrolled_store:
|
||||||
|
enrolled_store_name = enrolled_store.name
|
||||||
|
|
||||||
return CardDetailResponse(
|
return CardDetailResponse(
|
||||||
id=card.id,
|
id=card.id,
|
||||||
card_number=card.card_number,
|
card_number=card.card_number,
|
||||||
@@ -521,6 +532,7 @@ def get_card_detail(
|
|||||||
merchant_id=card.merchant_id,
|
merchant_id=card.merchant_id,
|
||||||
program_id=card.program_id,
|
program_id=card.program_id,
|
||||||
enrolled_at_store_id=card.enrolled_at_store_id,
|
enrolled_at_store_id=card.enrolled_at_store_id,
|
||||||
|
enrolled_at_store_name=enrolled_store_name,
|
||||||
customer_name=customer.full_name if customer else None,
|
customer_name=customer.full_name if customer else None,
|
||||||
customer_email=customer.email if customer else None,
|
customer_email=customer.email if customer else None,
|
||||||
merchant_name=card.merchant.name if card.merchant else None,
|
merchant_name=card.merchant.name if card.merchant else None,
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ class CardDetailResponse(CardResponse):
|
|||||||
|
|
||||||
# Merchant info
|
# Merchant info
|
||||||
merchant_name: str | None = None
|
merchant_name: str | None = None
|
||||||
|
enrolled_at_store_name: str | None = None
|
||||||
|
|
||||||
# Program info
|
# Program info
|
||||||
program_name: str
|
program_name: str
|
||||||
|
|||||||
@@ -70,15 +70,30 @@
|
|||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">{{ _('loyalty.store.card_detail.name') }}</p>
|
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">{{ _('loyalty.store.card_detail.name') }}</p>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="card?.customer_name || '-'">-</p>
|
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="card?.customer_name || '-'">-</p>
|
||||||
|
<button x-show="card?.customer_name" @click="Utils.copyToClipboard(card.customer_name)" type="button" aria-label="Copy" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
||||||
|
<span x-html="$icon('clipboard-copy', 'w-3.5 h-3.5')"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">{{ _('loyalty.store.card_detail.email') }}</p>
|
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">{{ _('loyalty.store.card_detail.email') }}</p>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="card?.customer_email || '-'">-</p>
|
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="card?.customer_email || '-'">-</p>
|
||||||
|
<button x-show="card?.customer_email" @click="Utils.copyToClipboard(card.customer_email)" type="button" aria-label="Copy" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
||||||
|
<span x-html="$icon('clipboard-copy', 'w-3.5 h-3.5')"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">{{ _('loyalty.store.card_detail.phone') }}</p>
|
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">{{ _('loyalty.store.card_detail.phone') }}</p>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="card?.customer_phone || '-'">-</p>
|
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="card?.customer_phone || '-'">-</p>
|
||||||
|
<button x-show="card?.customer_phone" @click="Utils.copyToClipboard(card.customer_phone)" type="button" aria-label="Copy" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
||||||
|
<span x-html="$icon('clipboard-copy', 'w-3.5 h-3.5')"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">{{ _('loyalty.store.card_detail.birthday') }}</p>
|
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">{{ _('loyalty.store.card_detail.birthday') }}</p>
|
||||||
@@ -96,7 +111,12 @@
|
|||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">{{ _('loyalty.store.card_detail.card_number') }}</p>
|
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">{{ _('loyalty.store.card_detail.card_number') }}</p>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
<p class="text-sm font-mono text-gray-700 dark:text-gray-300" x-text="card?.card_number">-</p>
|
<p class="text-sm font-mono text-gray-700 dark:text-gray-300" x-text="card?.card_number">-</p>
|
||||||
|
<button x-show="card?.card_number" @click="Utils.copyToClipboard(card.card_number)" type="button" aria-label="Copy" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
||||||
|
<span x-html="$icon('clipboard-copy', 'w-3.5 h-3.5')"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">{{ _('loyalty.store.card_detail.status') }}</p>
|
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">{{ _('loyalty.store.card_detail.status') }}</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user