From 211c46ebbc90b59786bb3b654f89fa62fad3b8db Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Sun, 29 Mar 2026 13:23:20 +0200 Subject: [PATCH] feat(tenancy): add member detail modal + fix invite name saving Merchant team page: - Consistent member display (full_name + email on every row) - New view button (eye icon) on all members including owner - View modal shows account info (username, role, email verified, last login, account created) and store memberships with roles - API enriched with user metadata (username, role, is_email_verified, last_login, created_at) Invite fix (both merchant and store routes): - first_name and last_name from invite form were never passed to the service that creates the User account. Now passed through correctly. i18n: 6 new keys across 4 locales (view_member, account_information, username, email_verified, last_login, account_created). Co-Authored-By: Claude Opus 4.6 (1M context) --- app/modules/tenancy/locales/de.json | 6 + app/modules/tenancy/locales/en.json | 6 + app/modules/tenancy/locales/fr.json | 6 + app/modules/tenancy/locales/lb.json | 6 + app/modules/tenancy/routes/api/merchant.py | 2 + app/modules/tenancy/routes/api/store_team.py | 4 + .../services/merchant_store_service.py | 6 + .../tenancy/services/store_team_service.py | 4 + .../static/merchant/js/merchant-team.js | 9 ++ .../templates/tenancy/merchant/team.html | 128 ++++++++++++++---- 10 files changed, 151 insertions(+), 26 deletions(-) diff --git a/app/modules/tenancy/locales/de.json b/app/modules/tenancy/locales/de.json index 2027871b..081cf1f1 100644 --- a/app/modules/tenancy/locales/de.json +++ b/app/modules/tenancy/locales/de.json @@ -50,6 +50,12 @@ "stores_and_roles": "Filialen & Rollen", "title": "Team", "total_members": "Mitglieder gesamt", + "view_member": "Mitglied anzeigen", + "account_information": "Kontoinformationen", + "username": "Benutzername", + "email_verified": "E-Mail verifiziert", + "last_login": "Letzte Anmeldung", + "account_created": "Konto erstellt", "viewer": "Betrachter" }, "messages": { diff --git a/app/modules/tenancy/locales/en.json b/app/modules/tenancy/locales/en.json index 9044de75..1977fcb3 100644 --- a/app/modules/tenancy/locales/en.json +++ b/app/modules/tenancy/locales/en.json @@ -50,6 +50,12 @@ "stores_and_roles": "Stores & Roles", "title": "Team", "total_members": "Total Members", + "view_member": "View Member", + "account_information": "Account Information", + "username": "Username", + "email_verified": "Email Verified", + "last_login": "Last Login", + "account_created": "Account Created", "viewer": "Viewer" }, "messages": { diff --git a/app/modules/tenancy/locales/fr.json b/app/modules/tenancy/locales/fr.json index 929bee41..5c01af51 100644 --- a/app/modules/tenancy/locales/fr.json +++ b/app/modules/tenancy/locales/fr.json @@ -50,6 +50,12 @@ "stores_and_roles": "Magasins et rôles", "title": "Équipe", "total_members": "Membres totaux", + "view_member": "Voir le membre", + "account_information": "Informations du compte", + "username": "Nom d'utilisateur", + "email_verified": "Email vérifié", + "last_login": "Dernière connexion", + "account_created": "Compte créé", "viewer": "Lecteur" }, "messages": { diff --git a/app/modules/tenancy/locales/lb.json b/app/modules/tenancy/locales/lb.json index 91874997..da0b4c53 100644 --- a/app/modules/tenancy/locales/lb.json +++ b/app/modules/tenancy/locales/lb.json @@ -50,6 +50,12 @@ "stores_and_roles": "Geschäfter & Rollen", "title": "Team", "total_members": "Memberen total", + "view_member": "Member kucken", + "account_information": "Konto Informatiounen", + "username": "Benotzernumm", + "email_verified": "Email verifizéiert", + "last_login": "Lescht Umeldung", + "account_created": "Konto erstallt", "viewer": "Betruechter" }, "messages": { diff --git a/app/modules/tenancy/routes/api/merchant.py b/app/modules/tenancy/routes/api/merchant.py index 295308bd..4247477e 100644 --- a/app/modules/tenancy/routes/api/merchant.py +++ b/app/modules/tenancy/routes/api/merchant.py @@ -220,6 +220,8 @@ async def merchant_team_invite( inviter=inviter, email=data.email, role_name=data.role_name, + first_name=data.first_name, + last_name=data.last_name, ) results.append(MerchantTeamInviteResult( store_id=store.id, diff --git a/app/modules/tenancy/routes/api/store_team.py b/app/modules/tenancy/routes/api/store_team.py index 9f6aed79..218f0257 100644 --- a/app/modules/tenancy/routes/api/store_team.py +++ b/app/modules/tenancy/routes/api/store_team.py @@ -140,6 +140,8 @@ def invite_team_member( inviter=current_user, email=invitation.email, role_id=invitation.role_id, + first_name=invitation.first_name, + last_name=invitation.last_name, ) elif invitation.role_name: # Use role name with optional custom permissions @@ -149,6 +151,8 @@ def invite_team_member( inviter=current_user, email=invitation.email, role_name=invitation.role_name, + first_name=invitation.first_name, + last_name=invitation.last_name, custom_permissions=invitation.custom_permissions, ) else: diff --git a/app/modules/tenancy/services/merchant_store_service.py b/app/modules/tenancy/services/merchant_store_service.py index 42f08d26..5f90f460 100644 --- a/app/modules/tenancy/services/merchant_store_service.py +++ b/app/modules/tenancy/services/merchant_store_service.py @@ -504,9 +504,15 @@ class MerchantStoreService: members_map[uid] = { "user_id": uid, "email": user.email, + "username": user.username, "first_name": user.first_name, "last_name": user.last_name, "full_name": f"{user.first_name or ''} {user.last_name or ''}".strip() or user.email, + "role": user.role, + "is_active": user.is_active, + "is_email_verified": user.is_email_verified, + "last_login": user.last_login.isoformat() if user.last_login else None, + "created_at": user.created_at.isoformat() if user.created_at else None, "stores": [], "is_owner": uid == merchant.owner_user_id, } diff --git a/app/modules/tenancy/services/store_team_service.py b/app/modules/tenancy/services/store_team_service.py index 5a73533a..59079628 100644 --- a/app/modules/tenancy/services/store_team_service.py +++ b/app/modules/tenancy/services/store_team_service.py @@ -54,6 +54,8 @@ class StoreTeamService: inviter: User, email: str, role_name: str, + first_name: str | None = None, + last_name: str | None = None, custom_permissions: list[str] | None = None, ) -> dict[str, Any]: """ @@ -148,6 +150,8 @@ class StoreTeamService: email=email, username=username, hashed_password=self.auth_manager.hash_password(temp_password), + first_name=first_name, + last_name=last_name, role="store_member", is_active=False, # Will be activated when invitation accepted is_email_verified=False, diff --git a/app/modules/tenancy/static/merchant/js/merchant-team.js b/app/modules/tenancy/static/merchant/js/merchant-team.js index 7c9a8a38..132747bc 100644 --- a/app/modules/tenancy/static/merchant/js/merchant-team.js +++ b/app/modules/tenancy/static/merchant/js/merchant-team.js @@ -35,6 +35,7 @@ function merchantTeam() { showInviteModal: false, showEditModal: false, showRemoveModal: false, + showViewModal: false, selectedMember: null, // Invite form @@ -184,6 +185,14 @@ function merchantTeam() { } }, + /** + * Open view modal for a member (read-only detail) + */ + openViewModal(member) { + this.selectedMember = JSON.parse(JSON.stringify(member)); + this.showViewModal = true; + }, + /** * Open edit modal for a member */ diff --git a/app/modules/tenancy/templates/tenancy/merchant/team.html b/app/modules/tenancy/templates/tenancy/merchant/team.html index a2369b41..e03d84a9 100644 --- a/app/modules/tenancy/templates/tenancy/merchant/team.html +++ b/app/modules/tenancy/templates/tenancy/merchant/team.html @@ -92,13 +92,9 @@
-

- - - -

-

+

+

@@ -142,25 +138,32 @@ - - +
+ + + +
@@ -372,6 +375,79 @@ {% endcall %} + +{% call modal('viewModal', _('tenancy.team.view_member'), 'showViewModal', size='md', show_footer=false) %} +
+ +
+
+ +
+
+

+

+
+
+ + +
+

{{ _('tenancy.team.account_information') }}

+
+
+ +

+
+
+ +

+
+
+ + +
+
+ +

+
+
+ +

+
+
+
+ + +
+

{{ _('tenancy.team.store_roles') }}

+
+ +
+
+ + +
+ +
+
+{% endcall %} + {% endblock %} {% block extra_scripts %}