From a247622d23a4ff52e314ce0440ec221e2eb3c12a Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Mon, 30 Mar 2026 21:36:42 +0200 Subject: [PATCH] feat(tenancy): add delete button on table + add-to-store in edit modal Table actions now show view + edit + delete (trash icon) for non-owner members. Delete opens the existing remove-from-all-stores modal. Edit modal enhanced with "Add to another store" section: - Shows a dashed-border card with store dropdown + role dropdown + add button - Only appears when the member is not yet in all merchant stores - Uses the existing invite API to add the member to the selected store i18n: 2 new keys (add_to_store, select_store) in 4 locales. Co-Authored-By: Claude Opus 4.6 (1M context) --- app/modules/tenancy/locales/de.json | 2 + app/modules/tenancy/locales/en.json | 2 + app/modules/tenancy/locales/fr.json | 2 + app/modules/tenancy/locales/lb.json | 2 + .../static/merchant/js/merchant-team.js | 46 +++++++++++++++++++ .../templates/tenancy/merchant/team.html | 45 ++++++++++++++++-- 6 files changed, 94 insertions(+), 5 deletions(-) diff --git a/app/modules/tenancy/locales/de.json b/app/modules/tenancy/locales/de.json index a1ad5bd6..47c3be28 100644 --- a/app/modules/tenancy/locales/de.json +++ b/app/modules/tenancy/locales/de.json @@ -53,6 +53,8 @@ "personal_info": "Persönliche Informationen", "resend_invitation": "Einladung erneut senden", "save_profile": "Profil speichern", + "add_to_store": "Zu einem anderen Shop hinzufügen", + "select_store": "Shop auswählen...", "multiple_roles": "Mehrere Rollen", "view_member": "Mitglied anzeigen", "account_information": "Kontoinformationen", diff --git a/app/modules/tenancy/locales/en.json b/app/modules/tenancy/locales/en.json index 26c3a9cc..f977f772 100644 --- a/app/modules/tenancy/locales/en.json +++ b/app/modules/tenancy/locales/en.json @@ -53,6 +53,8 @@ "personal_info": "Personal Information", "resend_invitation": "Resend Invitation", "save_profile": "Save Profile", + "add_to_store": "Add to another store", + "select_store": "Select store...", "multiple_roles": "Multiple roles", "view_member": "View Member", "account_information": "Account Information", diff --git a/app/modules/tenancy/locales/fr.json b/app/modules/tenancy/locales/fr.json index 9ffd5351..b5f0cfce 100644 --- a/app/modules/tenancy/locales/fr.json +++ b/app/modules/tenancy/locales/fr.json @@ -53,6 +53,8 @@ "personal_info": "Informations personnelles", "resend_invitation": "Renvoyer l'invitation", "save_profile": "Enregistrer le profil", + "add_to_store": "Ajouter à un autre magasin", + "select_store": "Sélectionner un magasin...", "multiple_roles": "Rôles multiples", "view_member": "Voir le membre", "account_information": "Informations du compte", diff --git a/app/modules/tenancy/locales/lb.json b/app/modules/tenancy/locales/lb.json index 0c15b6c7..834e7f6a 100644 --- a/app/modules/tenancy/locales/lb.json +++ b/app/modules/tenancy/locales/lb.json @@ -53,6 +53,8 @@ "personal_info": "Perséinlech Informatiounen", "resend_invitation": "Aluedung nei schécken", "save_profile": "Profil späicheren", + "add_to_store": "Zu engem anere Shop derbäisetzen", + "select_store": "Shop auswielen...", "multiple_roles": "Méi Rollen", "view_member": "Member kucken", "account_information": "Konto Informatiounen", diff --git a/app/modules/tenancy/static/merchant/js/merchant-team.js b/app/modules/tenancy/static/merchant/js/merchant-team.js index 043d6bb6..c2b9449e 100644 --- a/app/modules/tenancy/static/merchant/js/merchant-team.js +++ b/app/modules/tenancy/static/merchant/js/merchant-team.js @@ -38,6 +38,12 @@ function merchantTeam() { showViewModal: false, selectedMember: null, + // Add to store form (inside edit modal) + addStoreForm: { + store_id: '', + role_name: 'staff', + }, + // Invite form inviteForm: { email: '', @@ -186,6 +192,45 @@ function merchantTeam() { this.showInviteModal = true; }, + /** + * Get stores the member is NOT yet in (for "Add to store" in edit modal) + */ + getAvailableStores(member) { + if (!member || !member.stores) return this.stores; + const memberStoreIds = member.stores.map(s => s.store_id); + return this.stores.filter(s => !memberStoreIds.includes(s.id)); + }, + + /** + * Add member to another store (invites them via existing invite API) + */ + async addMemberToStore(member) { + if (!this.addStoreForm.store_id) return; + + this.saving = true; + try { + await apiClient.post('/merchants/account/team/invite', { + email: member.email, + store_ids: [parseInt(this.addStoreForm.store_id)], + role_name: this.addStoreForm.role_name, + }); + + Utils.showToast(I18n.t('tenancy.messages.invitation_sent_successfully'), 'success'); + merchantTeamLog.info('Added member to store:', this.addStoreForm.store_id); + + this.addStoreForm.store_id = ''; + this.addStoreForm.role_name = 'staff'; + this.showEditModal = false; + this.selectedMember = null; + await this.loadTeamData(); + } catch (error) { + merchantTeamLog.error('Failed to add member to store:', error); + Utils.showToast(error.message || 'Failed to add member to store', 'error'); + } finally { + this.saving = false; + } + }, + /** * Toggle store in invite form store_ids */ @@ -242,6 +287,7 @@ function merchantTeam() { */ openEditModal(member) { this.selectedMember = JSON.parse(JSON.stringify(member)); + this.addStoreForm = { store_id: '', role_name: 'staff' }; this.showEditModal = true; }, diff --git a/app/modules/tenancy/templates/tenancy/merchant/team.html b/app/modules/tenancy/templates/tenancy/merchant/team.html index 15775d86..39ada7b5 100644 --- a/app/modules/tenancy/templates/tenancy/merchant/team.html +++ b/app/modules/tenancy/templates/tenancy/merchant/team.html @@ -137,11 +137,18 @@ + + +