From 442967410005290cf82d04ffa54c302ff76230e2 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Mon, 23 Mar 2026 20:58:10 +0100 Subject: [PATCH] feat(loyalty): add staff autocomplete to PIN management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When creating or editing a staff PIN in the store context, the name field now shows an autocomplete dropdown with the store's team members (loaded from GET /store/team/members). Selecting a member auto-fills name and staff_id (email). The dropdown filters as you type. Only active in store context (where staffApiPrefix is configured). Merchant and admin PIN views are unaffected — merchant has no staffApiPrefix, admin is read-only. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../static/shared/js/loyalty-pins-list.js | 47 +++++++++++++++ .../loyalty/static/store/js/loyalty-pins.js | 1 + .../templates/loyalty/shared/pins-list.html | 60 +++++++++++++++---- 3 files changed, 98 insertions(+), 10 deletions(-) diff --git a/app/modules/loyalty/static/shared/js/loyalty-pins-list.js b/app/modules/loyalty/static/shared/js/loyalty-pins-list.js index 69aec4c5..73a9a1f2 100644 --- a/app/modules/loyalty/static/shared/js/loyalty-pins-list.js +++ b/app/modules/loyalty/static/shared/js/loyalty-pins-list.js @@ -24,6 +24,7 @@ function loyaltyPinsList(config) { pins: [], program: null, locations: [], + staffMembers: [], // Filters filters: { @@ -57,6 +58,33 @@ function loyaltyPinsList(config) { store_id: '' }, + // Staff autocomplete state + staffSearch: '', + showStaffDropdown: false, + + get filteredStaff() { + if (!this.staffSearch) return this.staffMembers; + const q = this.staffSearch.toLowerCase(); + return this.staffMembers.filter(m => + (m.full_name && m.full_name.toLowerCase().includes(q)) || + (m.email && m.email.toLowerCase().includes(q)) + ); + }, + + selectStaffMember(member) { + this.pinForm.name = member.full_name; + this.pinForm.staff_id = member.email; + this.staffSearch = member.full_name; + this.showStaffDropdown = false; + }, + + clearStaffSelection() { + this.staffSearch = ''; + this.pinForm.name = ''; + this.pinForm.staff_id = ''; + this.showStaffDropdown = false; + }, + // Action state saving: false, deleting: false, @@ -88,6 +116,9 @@ function loyaltyPinsList(config) { if (config.showStoreFilter) { parallel.push(this.loadLocations()); } + if (config.showCrud && config.staffApiPrefix) { + parallel.push(this.loadStaffMembers()); + } await Promise.all(parallel); } } catch (error) { @@ -137,6 +168,18 @@ function loyaltyPinsList(config) { } }, + async loadStaffMembers() { + try { + const response = await apiClient.get(config.staffApiPrefix + '/team/members'); + if (response && response.members) { + this.staffMembers = response.members.filter(m => m.is_active); + loyaltyPinsListLog.info('Loaded', this.staffMembers.length, 'staff members'); + } + } catch (error) { + loyaltyPinsListLog.warn('Failed to load staff members:', error.message); + } + }, + computeStats() { this.stats.total = this.pins.length; this.stats.active = this.pins.filter(p => p.is_active && !p.is_locked).length; @@ -156,6 +199,8 @@ function loyaltyPinsList(config) { pin: '', store_id: '' }; + this.staffSearch = ''; + this.showStaffDropdown = false; this.showCreateModal = true; }, @@ -167,6 +212,8 @@ function loyaltyPinsList(config) { pin: '', store_id: pin.store_id || '' }; + this.staffSearch = pin.name || ''; + this.showStaffDropdown = false; this.showEditModal = true; }, diff --git a/app/modules/loyalty/static/store/js/loyalty-pins.js b/app/modules/loyalty/static/store/js/loyalty-pins.js index 4a35df14..7df28861 100644 --- a/app/modules/loyalty/static/store/js/loyalty-pins.js +++ b/app/modules/loyalty/static/store/js/loyalty-pins.js @@ -6,6 +6,7 @@ const storePinsLog = window.LogConfig.loggers.storePins || window.LogConfig.crea function storeLoyaltyPins() { return loyaltyPinsList({ apiPrefix: '/store/loyalty', + staffApiPrefix: '/store', showStoreFilter: false, showCrud: true, currentPage: 'pins', diff --git a/app/modules/loyalty/templates/loyalty/shared/pins-list.html b/app/modules/loyalty/templates/loyalty/shared/pins-list.html index 920bda74..8bde479a 100644 --- a/app/modules/loyalty/templates/loyalty/shared/pins-list.html +++ b/app/modules/loyalty/templates/loyalty/shared/pins-list.html @@ -133,17 +133,37 @@ {% call modal('createPinModal', _('loyalty.shared.pins.create_pin'), 'showCreateModal', size='md', show_footer=false) %}
-
+ +
- + +
+ +
- +
@@ -182,17 +202,37 @@ {% call modal('editPinModal', _('loyalty.shared.pins.edit_pin'), 'showEditModal', size='md', show_footer=false) %}
-
+ +
- + +
+ +
- +