feat(loyalty): add customer autocomplete to terminal search
Terminal search now shows live autocomplete suggestions as the user types (debounced 300ms, min 2 chars). Dropdown shows matching customers with avatar, name, email, card number, and points balance. Uses the existing GET /store/loyalty/cards?search= endpoint (limit=5). Selecting a result loads the full card details via the lookup endpoint. Enter key still works for exact lookup. No new dependencies — uses native Alpine.js dropdown, no Tom Select needed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,9 @@ function storeLoyaltyTerminal() {
|
||||
searchQuery: '',
|
||||
lookingUp: false,
|
||||
selectedCard: null,
|
||||
searchResults: [],
|
||||
showSearchDropdown: false,
|
||||
_searchTimeout: null,
|
||||
|
||||
// Transaction inputs
|
||||
earnAmount: null,
|
||||
@@ -146,6 +149,58 @@ function storeLoyaltyTerminal() {
|
||||
}
|
||||
},
|
||||
|
||||
// Debounced search for autocomplete suggestions
|
||||
debouncedSearchCustomers() {
|
||||
if (this._searchTimeout) clearTimeout(this._searchTimeout);
|
||||
if (!this.searchQuery || this.searchQuery.length < 2) {
|
||||
this.searchResults = [];
|
||||
this.showSearchDropdown = false;
|
||||
return;
|
||||
}
|
||||
this._searchTimeout = setTimeout(() => this.searchCustomers(), 300);
|
||||
},
|
||||
|
||||
async searchCustomers() {
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
search: this.searchQuery,
|
||||
limit: '5',
|
||||
is_active: 'true'
|
||||
});
|
||||
const response = await apiClient.get(`/store/loyalty/cards?${params}`);
|
||||
if (response && response.cards) {
|
||||
this.searchResults = response.cards;
|
||||
this.showSearchDropdown = this.searchResults.length > 0;
|
||||
}
|
||||
} catch (error) {
|
||||
loyaltyTerminalLog.warn('Search failed:', error.message);
|
||||
this.searchResults = [];
|
||||
this.showSearchDropdown = false;
|
||||
}
|
||||
},
|
||||
|
||||
// Select a customer from autocomplete dropdown
|
||||
async selectCustomer(card) {
|
||||
this.showSearchDropdown = false;
|
||||
this.searchResults = [];
|
||||
this.lookingUp = true;
|
||||
|
||||
try {
|
||||
// Use the lookup endpoint to get full card details
|
||||
const response = await apiClient.get(`/store/loyalty/cards/lookup?q=${encodeURIComponent(card.card_number)}`);
|
||||
if (response) {
|
||||
this.selectedCard = response;
|
||||
this.searchQuery = '';
|
||||
loyaltyTerminalLog.info('Customer selected:', this.selectedCard.customer_name);
|
||||
}
|
||||
} catch (error) {
|
||||
Utils.showToast(I18n.t('loyalty.store.terminal.error_lookup', {message: error.message}), 'error');
|
||||
loyaltyTerminalLog.error('Lookup failed:', error);
|
||||
} finally {
|
||||
this.lookingUp = false;
|
||||
}
|
||||
},
|
||||
|
||||
// Clear selected customer
|
||||
clearCustomer() {
|
||||
this.selectedCard = null;
|
||||
|
||||
Reference in New Issue
Block a user